gtk3 - adapted draw routine to gtk_cairo_should_draw_window. gtk-criticals with undef...
[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 void
3860 pspp_sheet_view_draw_bin (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;
3918     }
3919
3920 #if GTK3_TRANSITION
3921   /* clip event->area to the visible area */
3922   if (Zarea.height < 0.5)
3923     return;
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;
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_VOID (has_next);
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;
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       GList *tmp_list;
4493
4494       cairo_save (cr);
4495       gtk_cairo_transform_to_window(cr,widget,tree_view->priv->bin_window);
4496       pspp_sheet_view_draw_bin (widget, cr);
4497       cairo_restore (cr);
4498
4499       /* We can't just chain up to Container::expose as it will try to send the
4500        * event to the headers, so we handle propagating it to our children
4501        * (eg. widgets being edited) ourselves.
4502        */
4503       tmp_list = tree_view->priv->children;
4504       while (tmp_list)
4505         {
4506           PsppSheetViewChild *child = tmp_list->data;
4507           tmp_list = tmp_list->next;
4508
4509           gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
4510         }
4511     }
4512   if (gtk_cairo_should_draw_window (cr, tree_view->priv->header_window))
4513     {
4514       gint n_visible_columns;
4515       GList *list;
4516
4517       for (list = tree_view->priv->columns; list != NULL; list = list->next)
4518         {
4519           PsppSheetViewColumn *column = list->data;
4520
4521           if (column == tree_view->priv->drag_column || !column->visible)
4522             continue;
4523
4524           if (span_intersects (column->allocation.x, column->allocation.width,
4525                                (int) gtk_adjustment_get_value (tree_view->priv->hadjustment),
4526                                (int) gtk_widget_get_allocated_width (widget))
4527               && column->button != NULL)
4528             gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4529                                           column->button, cr);
4530         }
4531
4532       n_visible_columns = 0;
4533       for (list = tree_view->priv->columns; list; list = list->next)
4534         {
4535           if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4536             continue;
4537           n_visible_columns ++;
4538         }
4539       cairo_save (cr);
4540       gtk_cairo_transform_to_window(cr,widget,tree_view->priv->header_window);
4541       pspp_sheet_view_draw_vertical_grid_lines (tree_view,
4542                                                 cr,
4543                                                 n_visible_columns,
4544                                                 0,
4545                                                 TREE_VIEW_HEADER_HEIGHT (tree_view));
4546       cairo_restore (cr);
4547     }
4548   if (tree_view->priv->drag_window &&
4549       gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window))
4550     {
4551       gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4552                                     tree_view->priv->drag_column->button,
4553                                     cr);
4554     }
4555
4556   return FALSE;
4557 }
4558
4559 enum
4560 {
4561   DROP_HOME,
4562   DROP_RIGHT,
4563   DROP_LEFT,
4564   DROP_END
4565 };
4566
4567 /* returns 0x1 when no column has been found -- yes it's hackish */
4568 static PsppSheetViewColumn *
4569 pspp_sheet_view_get_drop_column (PsppSheetView       *tree_view,
4570                                PsppSheetViewColumn *column,
4571                                gint               drop_position)
4572 {
4573   PsppSheetViewColumn *left_column = NULL;
4574   PsppSheetViewColumn *cur_column = NULL;
4575   GList *tmp_list;
4576
4577   if (!column->reorderable)
4578     return (PsppSheetViewColumn *)0x1;
4579
4580   switch (drop_position)
4581     {
4582       case DROP_HOME:
4583         /* find first column where we can drop */
4584         tmp_list = tree_view->priv->columns;
4585         if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4586           return (PsppSheetViewColumn *)0x1;
4587
4588         while (tmp_list)
4589           {
4590             g_assert (tmp_list);
4591
4592             cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4593             tmp_list = tmp_list->next;
4594
4595             if (left_column && left_column->visible == FALSE)
4596               continue;
4597
4598             if (!tree_view->priv->column_drop_func)
4599               return left_column;
4600
4601             if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4602               {
4603                 left_column = cur_column;
4604                 continue;
4605               }
4606
4607             return left_column;
4608           }
4609
4610         if (!tree_view->priv->column_drop_func)
4611           return left_column;
4612
4613         if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4614           return left_column;
4615         else
4616           return (PsppSheetViewColumn *)0x1;
4617         break;
4618
4619       case DROP_RIGHT:
4620         /* find first column after column where we can drop */
4621         tmp_list = tree_view->priv->columns;
4622
4623         for (; tmp_list; tmp_list = tmp_list->next)
4624           if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4625             break;
4626
4627         if (!tmp_list || !tmp_list->next)
4628           return (PsppSheetViewColumn *)0x1;
4629
4630         tmp_list = tmp_list->next;
4631         left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4632         tmp_list = tmp_list->next;
4633
4634         while (tmp_list)
4635           {
4636             g_assert (tmp_list);
4637
4638             cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4639             tmp_list = tmp_list->next;
4640
4641             if (left_column && left_column->visible == FALSE)
4642               {
4643                 left_column = cur_column;
4644                 if (tmp_list)
4645                   tmp_list = tmp_list->next;
4646                 continue;
4647               }
4648
4649             if (!tree_view->priv->column_drop_func)
4650               return left_column;
4651
4652             if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4653               {
4654                 left_column = cur_column;
4655                 continue;
4656               }
4657
4658             return left_column;
4659           }
4660
4661         if (!tree_view->priv->column_drop_func)
4662           return left_column;
4663
4664         if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4665           return left_column;
4666         else
4667           return (PsppSheetViewColumn *)0x1;
4668         break;
4669
4670       case DROP_LEFT:
4671         /* find first column before column where we can drop */
4672         tmp_list = tree_view->priv->columns;
4673
4674         for (; tmp_list; tmp_list = tmp_list->next)
4675           if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4676             break;
4677
4678         if (!tmp_list || !tmp_list->prev)
4679           return (PsppSheetViewColumn *)0x1;
4680
4681         tmp_list = tmp_list->prev;
4682         cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4683         tmp_list = tmp_list->prev;
4684
4685         while (tmp_list)
4686           {
4687             g_assert (tmp_list);
4688
4689             left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4690
4691             if (left_column && !left_column->visible)
4692               {
4693                 /*if (!tmp_list->prev)
4694                   return (PsppSheetViewColumn *)0x1;
4695                   */
4696 /*
4697                 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4698                 tmp_list = tmp_list->prev->prev;
4699                 continue;*/
4700
4701                 cur_column = left_column;
4702                 if (tmp_list)
4703                   tmp_list = tmp_list->prev;
4704                 continue;
4705               }
4706
4707             if (!tree_view->priv->column_drop_func)
4708               return left_column;
4709
4710             if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4711               return left_column;
4712
4713             cur_column = left_column;
4714             tmp_list = tmp_list->prev;
4715           }
4716
4717         if (!tree_view->priv->column_drop_func)
4718           return NULL;
4719
4720         if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4721           return NULL;
4722         else
4723           return (PsppSheetViewColumn *)0x1;
4724         break;
4725
4726       case DROP_END:
4727         /* same as DROP_HOME case, but doing it backwards */
4728         tmp_list = g_list_last (tree_view->priv->columns);
4729         cur_column = NULL;
4730
4731         if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4732           return (PsppSheetViewColumn *)0x1;
4733
4734         while (tmp_list)
4735           {
4736             g_assert (tmp_list);
4737
4738             left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4739
4740             if (left_column && !left_column->visible)
4741               {
4742                 cur_column = left_column;
4743                 tmp_list = tmp_list->prev;
4744               }
4745
4746             if (!tree_view->priv->column_drop_func)
4747               return left_column;
4748
4749             if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4750               return left_column;
4751
4752             cur_column = left_column;
4753             tmp_list = tmp_list->prev;
4754           }
4755
4756         if (!tree_view->priv->column_drop_func)
4757           return NULL;
4758
4759         if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4760           return NULL;
4761         else
4762           return (PsppSheetViewColumn *)0x1;
4763         break;
4764     }
4765
4766   return (PsppSheetViewColumn *)0x1;
4767 }
4768
4769 static gboolean
4770 pspp_sheet_view_key_press (GtkWidget   *widget,
4771                          GdkEventKey *event)
4772 {
4773   PsppSheetView *tree_view = (PsppSheetView *) widget;
4774
4775   if (tree_view->priv->rubber_band_status)
4776     {
4777       if (event->keyval == GDK_Escape)
4778         pspp_sheet_view_stop_rubber_band (tree_view);
4779
4780       return TRUE;
4781     }
4782
4783   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4784     {
4785       if (event->keyval == GDK_Escape)
4786         {
4787           tree_view->priv->cur_reorder = NULL;
4788           pspp_sheet_view_button_release_drag_column (widget, NULL);
4789         }
4790       return TRUE;
4791     }
4792
4793   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4794     {
4795       GList *focus_column;
4796       gboolean rtl;
4797
4798       rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4799
4800       for (focus_column = tree_view->priv->columns;
4801            focus_column;
4802            focus_column = focus_column->next)
4803         {
4804           PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4805
4806           if (column->button && gtk_widget_has_focus (column->button))
4807             break;
4808         }
4809
4810       if (focus_column &&
4811           (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4812           (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4813            || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4814         {
4815           PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4816
4817           if (!column->resizable)
4818             {
4819               gtk_widget_error_bell (widget);
4820               return TRUE;
4821             }
4822
4823           if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4824               || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4825             {
4826               gint old_width = column->resized_width;
4827
4828               column->resized_width = MAX (column->resized_width,
4829                                            column->width);
4830               column->resized_width -= 2;
4831               if (column->resized_width < 0)
4832                 column->resized_width = 0;
4833
4834               if (column->min_width == -1)
4835                 column->resized_width = MAX (column->button_request,
4836                                              column->resized_width);
4837               else
4838                 column->resized_width = MAX (column->min_width,
4839                                              column->resized_width);
4840
4841               if (column->max_width != -1)
4842                 column->resized_width = MIN (column->resized_width,
4843                                              column->max_width);
4844
4845               column->use_resized_width = TRUE;
4846
4847               if (column->resized_width != old_width)
4848                 gtk_widget_queue_resize (widget);
4849               else
4850                 gtk_widget_error_bell (widget);
4851             }
4852           else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4853                    || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4854             {
4855               gint old_width = column->resized_width;
4856
4857               column->resized_width = MAX (column->resized_width,
4858                                            column->width);
4859               column->resized_width += 2;
4860
4861               if (column->max_width != -1)
4862                 column->resized_width = MIN (column->resized_width,
4863                                              column->max_width);
4864
4865               column->use_resized_width = TRUE;
4866
4867               if (column->resized_width != old_width)
4868                 gtk_widget_queue_resize (widget);
4869               else
4870                 gtk_widget_error_bell (widget);
4871             }
4872
4873           return TRUE;
4874         }
4875
4876       if (focus_column &&
4877           (event->state & GDK_MOD1_MASK) &&
4878           (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4879            || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4880            || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4881            || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4882         {
4883           PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4884
4885           if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4886               || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4887             {
4888               PsppSheetViewColumn *col;
4889               col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
4890               if (col != (PsppSheetViewColumn *)0x1)
4891                 pspp_sheet_view_move_column_after (tree_view, column, col);
4892               else
4893                 gtk_widget_error_bell (widget);
4894             }
4895           else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4896                    || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4897             {
4898               PsppSheetViewColumn *col;
4899               col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4900               if (col != (PsppSheetViewColumn *)0x1)
4901                 pspp_sheet_view_move_column_after (tree_view, column, col);
4902               else
4903                 gtk_widget_error_bell (widget);
4904             }
4905           else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4906             {
4907               PsppSheetViewColumn *col;
4908               col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4909               if (col != (PsppSheetViewColumn *)0x1)
4910                 pspp_sheet_view_move_column_after (tree_view, column, col);
4911               else
4912                 gtk_widget_error_bell (widget);
4913             }
4914           else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4915             {
4916               PsppSheetViewColumn *col;
4917               col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4918               if (col != (PsppSheetViewColumn *)0x1)
4919                 pspp_sheet_view_move_column_after (tree_view, column, col);
4920               else
4921                 gtk_widget_error_bell (widget);
4922             }
4923
4924           return TRUE;
4925         }
4926     }
4927
4928   /* Chain up to the parent class.  It handles the keybindings. */
4929   if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4930     return TRUE;
4931
4932   if (tree_view->priv->search_entry_avoid_unhandled_binding)
4933     {
4934       tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4935       return FALSE;
4936     }
4937
4938   /* We pass the event to the search_entry.  If its text changes, then we start
4939    * the typeahead find capabilities. */
4940   if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4941       && tree_view->priv->enable_search
4942       && !tree_view->priv->search_custom_entry_set)
4943     {
4944       GdkEvent *new_event;
4945       char *old_text;
4946       const char *new_text;
4947       gboolean retval;
4948       GdkScreen *screen;
4949       gboolean text_modified;
4950       gulong popup_menu_id;
4951
4952       pspp_sheet_view_ensure_interactive_directory (tree_view);
4953
4954       /* Make a copy of the current text */
4955       old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4956       new_event = gdk_event_copy ((GdkEvent *) event);
4957       g_object_unref (((GdkEventKey *) new_event)->window);
4958       ((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window));
4959       gtk_widget_realize (tree_view->priv->search_window);
4960
4961       popup_menu_id = g_signal_connect (tree_view->priv->search_entry, 
4962                                         "popup-menu", G_CALLBACK (gtk_true),
4963                                         NULL);
4964
4965       /* Move the entry off screen */
4966       screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4967       gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4968                        gdk_screen_get_width (screen) + 1,
4969                        gdk_screen_get_height (screen) + 1);
4970       gtk_widget_show (tree_view->priv->search_window);
4971
4972       /* Send the event to the window.  If the preedit_changed signal is emitted
4973        * during this event, we will set priv->imcontext_changed  */
4974       tree_view->priv->imcontext_changed = FALSE;
4975       retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4976       gdk_event_free (new_event);
4977       gtk_widget_hide (tree_view->priv->search_window);
4978
4979       g_signal_handler_disconnect (tree_view->priv->search_entry, 
4980                                    popup_menu_id);
4981
4982       /* We check to make sure that the entry tried to handle the text, and that
4983        * the text has changed.
4984        */
4985       new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
4986       text_modified = strcmp (old_text, new_text) != 0;
4987       g_free (old_text);
4988       if (tree_view->priv->imcontext_changed ||    /* we're in a preedit */
4989           (retval && text_modified))               /* ...or the text was modified */
4990         {
4991           if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
4992             {
4993               gtk_widget_grab_focus (GTK_WIDGET (tree_view));
4994               return TRUE;
4995             }
4996           else
4997             {
4998               gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
4999               return FALSE;
5000             }
5001         }
5002     }
5003
5004   return FALSE;
5005 }
5006
5007 static gboolean
5008 pspp_sheet_view_key_release (GtkWidget   *widget,
5009                            GdkEventKey *event)
5010 {
5011   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
5012
5013   if (tree_view->priv->rubber_band_status)
5014     return TRUE;
5015
5016   return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
5017 }
5018
5019 /* FIXME Is this function necessary? Can I get an enter_notify event
5020  * w/o either an expose event or a mouse motion event?
5021  */
5022 static gboolean
5023 pspp_sheet_view_enter_notify (GtkWidget        *widget,
5024                             GdkEventCrossing *event)
5025 {
5026   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
5027   int node;
5028   gint new_y;
5029
5030   /* Sanity check it */
5031   if (event->window != tree_view->priv->bin_window)
5032     return FALSE;
5033
5034   if (tree_view->priv->row_count == 0)
5035     return FALSE;
5036
5037   if (event->mode == GDK_CROSSING_GRAB ||
5038       event->mode == GDK_CROSSING_GTK_GRAB ||
5039       event->mode == GDK_CROSSING_GTK_UNGRAB ||
5040       event->mode == GDK_CROSSING_STATE_CHANGED)
5041     return TRUE;
5042
5043   /* find the node internally */
5044   new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
5045   if (new_y < 0)
5046     new_y = 0;
5047   pspp_sheet_view_find_offset (tree_view, new_y, &node);
5048
5049   tree_view->priv->event_last_x = event->x;
5050   tree_view->priv->event_last_y = event->y;
5051
5052   prelight_or_select (tree_view, node, event->x, event->y);
5053
5054   return TRUE;
5055 }
5056
5057 static gboolean
5058 pspp_sheet_view_leave_notify (GtkWidget        *widget,
5059                             GdkEventCrossing *event)
5060 {
5061   PsppSheetView *tree_view;
5062
5063   if (event->mode == GDK_CROSSING_GRAB)
5064     return TRUE;
5065
5066   tree_view = PSPP_SHEET_VIEW (widget);
5067
5068   if (tree_view->priv->prelight_node >= 0)
5069     _pspp_sheet_view_queue_draw_node (tree_view,
5070                                    tree_view->priv->prelight_node,
5071                                    NULL);
5072
5073   tree_view->priv->event_last_x = -10000;
5074   tree_view->priv->event_last_y = -10000;
5075
5076   prelight_or_select (tree_view,
5077                       -1,
5078                       -1000, -1000); /* coords not possibly over an arrow */
5079
5080   return TRUE;
5081 }
5082
5083
5084 static gint
5085 pspp_sheet_view_focus_out (GtkWidget     *widget,
5086                          GdkEventFocus *event)
5087 {
5088   PsppSheetView *tree_view;
5089
5090   tree_view = PSPP_SHEET_VIEW (widget);
5091
5092   gtk_widget_queue_draw (widget);
5093
5094   /* destroy interactive search dialog */
5095   if (tree_view->priv->search_window)
5096     pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
5097
5098   return FALSE;
5099 }
5100
5101
5102 /* Incremental Reflow
5103  */
5104
5105 static void
5106 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
5107                                  int node)
5108 {
5109   GtkAllocation allocation;
5110   gint y = pspp_sheet_view_node_find_offset (tree_view, node)
5111     - gtk_adjustment_get_value (tree_view->priv->vadjustment)
5112     + TREE_VIEW_HEADER_HEIGHT (tree_view);
5113
5114   gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5115
5116   gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
5117                               0, y,
5118                               allocation.width,
5119                               tree_view->priv->fixed_height);
5120 }
5121
5122 static gboolean
5123 node_is_visible (PsppSheetView *tree_view,
5124                  int node)
5125 {
5126   int y;
5127   int height;
5128
5129   y = pspp_sheet_view_node_find_offset (tree_view, node);
5130   height = ROW_HEIGHT (tree_view);
5131
5132   if (y >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5133       y + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5134                      + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5135     return TRUE;
5136
5137   return FALSE;
5138 }
5139
5140 /* Returns the row height. */
5141 static gint
5142 validate_row (PsppSheetView *tree_view,
5143               int node,
5144               GtkTreeIter *iter,
5145               GtkTreePath *path)
5146 {
5147   PsppSheetViewColumn *column;
5148   GList *list, *first_column, *last_column;
5149   gint height = 0;
5150   gint horizontal_separator;
5151   gint vertical_separator;
5152   gint focus_line_width;
5153   gboolean draw_vgrid_lines, draw_hgrid_lines;
5154   gint focus_pad;
5155   gint grid_line_width;
5156   gboolean wide_separators;
5157   gint separator_height;
5158
5159   gtk_widget_style_get (GTK_WIDGET (tree_view),
5160                         "focus-padding", &focus_pad,
5161                         "focus-line-width", &focus_line_width,
5162                         "horizontal-separator", &horizontal_separator,
5163                         "vertical-separator", &vertical_separator,
5164                         "grid-line-width", &grid_line_width,
5165                         "wide-separators",  &wide_separators,
5166                         "separator-height", &separator_height,
5167                         NULL);
5168   
5169   draw_vgrid_lines =
5170     tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5171     || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5172   draw_hgrid_lines =
5173     tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5174     || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5175
5176   for (last_column = g_list_last (tree_view->priv->columns);
5177        last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5178        last_column = last_column->prev)
5179     ;
5180
5181   for (first_column = g_list_first (tree_view->priv->columns);
5182        first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5183        first_column = first_column->next)
5184     ;
5185
5186   for (list = tree_view->priv->columns; list; list = list->next)
5187     {
5188       gint tmp_width;
5189       gint tmp_height;
5190
5191       column = list->data;
5192
5193       if (! column->visible)
5194         continue;
5195
5196       pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5197       pspp_sheet_view_column_cell_get_size (column,
5198                                           NULL, NULL, NULL,
5199                                           &tmp_width, &tmp_height);
5200
5201       tmp_height += vertical_separator;
5202       height = MAX (height, tmp_height);
5203
5204       tmp_width = tmp_width + horizontal_separator;
5205
5206       if (draw_vgrid_lines)
5207         {
5208           if (list->data == first_column || list->data == last_column)
5209             tmp_width += grid_line_width / 2.0;
5210           else
5211             tmp_width += grid_line_width;
5212         }
5213
5214       if (tmp_width > column->requested_width)
5215         column->requested_width = tmp_width;
5216     }
5217
5218   if (draw_hgrid_lines)
5219     height += grid_line_width;
5220
5221   tree_view->priv->post_validation_flag = TRUE;
5222   return height;
5223 }
5224
5225
5226 static void
5227 validate_visible_area (PsppSheetView *tree_view)
5228 {
5229   GtkTreePath *path = NULL;
5230   GtkTreePath *above_path = NULL;
5231   GtkTreeIter iter;
5232   int node = -1;
5233   gboolean size_changed = FALSE;
5234   gint total_height;
5235   gint area_above = 0;
5236   gint area_below = 0;
5237   GtkAllocation allocation;
5238
5239   if (tree_view->priv->row_count == 0)
5240     return;
5241
5242   if (tree_view->priv->scroll_to_path == NULL)
5243     return;
5244
5245   gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5246
5247   total_height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5248
5249   if (total_height == 0)
5250     return;
5251
5252   path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5253   if (path)
5254     {
5255       /* we are going to scroll, and will update dy */
5256       _pspp_sheet_view_find_node (tree_view, path, &node);
5257       gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5258
5259       if (tree_view->priv->scroll_to_use_align)
5260         {
5261           gint height = ROW_HEIGHT (tree_view);
5262           area_above = (total_height - height) *
5263             tree_view->priv->scroll_to_row_align;
5264           area_below = total_height - area_above - height;
5265           area_above = MAX (area_above, 0);
5266           area_below = MAX (area_below, 0);
5267         }
5268       else
5269         {
5270           /* two cases:
5271            * 1) row not visible
5272            * 2) row visible
5273            */
5274           gint dy;
5275           gint height = ROW_HEIGHT (tree_view);
5276
5277           dy = pspp_sheet_view_node_find_offset (tree_view, node);
5278
5279           if (dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5280               dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5281                               + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5282             {
5283               /* row visible: keep the row at the same position */
5284               area_above = dy - gtk_adjustment_get_value (tree_view->priv->vadjustment);
5285               area_below = (gtk_adjustment_get_value (tree_view->priv->vadjustment) +
5286                             gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5287                 - dy - height;
5288             }
5289           else
5290             {
5291               /* row not visible */
5292               if (dy >= 0
5293                   && dy + height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5294                 {
5295                   /* row at the beginning -- fixed */
5296                   area_above = dy;
5297                   area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment)
5298                     - area_above - height;
5299                 }
5300               else if (dy >= (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5301                               gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5302                 {
5303                   /* row at the end -- fixed */
5304                   area_above = dy - (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5305                                      gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
5306                   area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) -
5307                     area_above - height;
5308
5309                   if (area_below < 0)
5310                     {
5311                       area_above = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - height;
5312                       area_below = 0;
5313                     }
5314                 }
5315               else
5316                 {
5317                   /* row somewhere in the middle, bring it to the top
5318                    * of the view
5319                    */
5320                   area_above = 0;
5321                   area_below = total_height - height;
5322                 }
5323             }
5324         }
5325     }
5326   else
5327     /* the scroll to isn't valid; ignore it.
5328      */
5329     {
5330       gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5331       tree_view->priv->scroll_to_path = NULL;
5332       return;
5333     }
5334
5335   above_path = gtk_tree_path_copy (path);
5336
5337   /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5338    * backwards is much slower then forward, as there is no iter_prev function.
5339    * We go forwards first in case we run out of tree.  Then we go backwards to
5340    * fill out the top.
5341    */
5342   while (node >= 0 && area_below > 0)
5343     {
5344       gboolean done = FALSE;
5345       do
5346         {
5347           node = pspp_sheet_view_node_next (tree_view, node);
5348           if (node >= 0)
5349             {
5350               gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5351               done = TRUE;
5352               gtk_tree_path_next (path);
5353
5354               /* Sanity Check! */
5355               TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5356             }
5357           else
5358             break;
5359         }
5360       while (!done);
5361
5362       if (node < 0)
5363         break;
5364
5365       area_below -= ROW_HEIGHT (tree_view);
5366     }
5367   gtk_tree_path_free (path);
5368
5369   /* If we ran out of tree, and have extra area_below left, we need to add it
5370    * to area_above */
5371   if (area_below > 0)
5372     area_above += area_below;
5373
5374   _pspp_sheet_view_find_node (tree_view, above_path, &node);
5375
5376   /* We walk backwards */
5377   while (area_above > 0)
5378     {
5379       node = pspp_sheet_view_node_prev (tree_view, node);
5380
5381       /* Always find the new path in the tree.  We cannot just assume
5382        * a gtk_tree_path_prev() is enough here, as there might be children
5383        * in between this node and the previous sibling node.  If this
5384        * appears to be a performance hotspot in profiles, we can look into
5385        * intrigate logic for keeping path, node and iter in sync like
5386        * we do for forward walks.  (Which will be hard because of the lacking
5387        * iter_prev).
5388        */
5389
5390       if (node < 0)
5391         break;
5392
5393       gtk_tree_path_free (above_path);
5394       above_path = _pspp_sheet_view_find_path (tree_view, node);
5395
5396       gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5397
5398       area_above -= ROW_HEIGHT (tree_view);
5399     }
5400
5401   /* set the dy here to scroll to the path,
5402    * and sync the top row accordingly
5403    */
5404   pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5405   pspp_sheet_view_top_row_to_dy (tree_view);
5406
5407   /* update width/height and queue a resize */
5408   if (size_changed)
5409     {
5410       GtkRequisition requisition;
5411
5412       /* We temporarily guess a size, under the assumption that it will be the
5413        * same when we get our next size_allocate.  If we don't do this, we'll be
5414        * in an inconsistent state if we call top_row_to_dy. */
5415
5416       gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5417       gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5418       gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5419       gtk_adjustment_changed (tree_view->priv->hadjustment);
5420       gtk_adjustment_changed (tree_view->priv->vadjustment);
5421       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5422     }
5423
5424   gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5425   tree_view->priv->scroll_to_path = NULL;
5426
5427   if (above_path)
5428     gtk_tree_path_free (above_path);
5429
5430   if (tree_view->priv->scroll_to_column)
5431     {
5432       tree_view->priv->scroll_to_column = NULL;
5433     }
5434   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5435 }
5436
5437 static void
5438 initialize_fixed_height_mode (PsppSheetView *tree_view)
5439 {
5440   if (!tree_view->priv->row_count)
5441     return;
5442
5443   if (tree_view->priv->fixed_height_set)
5444     return;
5445
5446   if (tree_view->priv->fixed_height < 0)
5447     {
5448       GtkTreeIter iter;
5449       GtkTreePath *path;
5450
5451       int node = 0;
5452
5453       path = _pspp_sheet_view_find_path (tree_view, node);
5454       gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5455
5456       tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5457
5458       gtk_tree_path_free (path);
5459
5460       g_object_notify (G_OBJECT (tree_view), "fixed-height");
5461     }
5462 }
5463
5464 /* Our strategy for finding nodes to validate is a little convoluted.  We find
5465  * the left-most uninvalidated node.  We then try walking right, validating
5466  * nodes.  Once we find a valid node, we repeat the previous process of finding
5467  * the first invalid node.
5468  */
5469
5470 static gboolean
5471 validate_rows_handler (PsppSheetView *tree_view)
5472 {
5473   initialize_fixed_height_mode (tree_view);
5474   if (tree_view->priv->validate_rows_timer)
5475     {
5476       g_source_remove (tree_view->priv->validate_rows_timer);
5477       tree_view->priv->validate_rows_timer = 0;
5478     }
5479
5480   return FALSE;
5481 }
5482
5483 static gboolean
5484 do_presize_handler (PsppSheetView *tree_view)
5485 {
5486   GtkRequisition requisition;
5487
5488   validate_visible_area (tree_view);
5489   tree_view->priv->presize_handler_timer = 0;
5490
5491   if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5492     return FALSE;
5493
5494   gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5495
5496   gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5497   gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5498   gtk_adjustment_changed (tree_view->priv->hadjustment);
5499   gtk_adjustment_changed (tree_view->priv->vadjustment);
5500   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5501                    
5502   return FALSE;
5503 }
5504
5505 static gboolean
5506 presize_handler_callback (gpointer data)
5507 {
5508   do_presize_handler (PSPP_SHEET_VIEW (data));
5509                    
5510   return FALSE;
5511 }
5512
5513 static void
5514 install_presize_handler (PsppSheetView *tree_view)
5515 {
5516   if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5517     return;
5518
5519   if (! tree_view->priv->presize_handler_timer)
5520     {
5521       tree_view->priv->presize_handler_timer =
5522         gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5523     }
5524   if (! tree_view->priv->validate_rows_timer)
5525     {
5526       tree_view->priv->validate_rows_timer =
5527         gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5528     }
5529 }
5530
5531 static gboolean
5532 scroll_sync_handler (PsppSheetView *tree_view)
5533 {
5534   if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5535     gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5536   else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5537     pspp_sheet_view_top_row_to_dy (tree_view);
5538   else
5539     pspp_sheet_view_dy_to_top_row (tree_view);
5540
5541   tree_view->priv->scroll_sync_timer = 0;
5542
5543   return FALSE;
5544 }
5545
5546 static void
5547 install_scroll_sync_handler (PsppSheetView *tree_view)
5548 {
5549   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5550     return;
5551
5552   if (!tree_view->priv->scroll_sync_timer)
5553     {
5554       tree_view->priv->scroll_sync_timer =
5555         gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5556     }
5557 }
5558
5559 static void
5560 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5561                            GtkTreePath *path,
5562                            gint         offset)
5563 {
5564   gtk_tree_row_reference_free (tree_view->priv->top_row);
5565
5566   if (!path)
5567     {
5568       tree_view->priv->top_row = NULL;
5569       tree_view->priv->top_row_dy = 0;
5570     }
5571   else
5572     {
5573       tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5574       tree_view->priv->top_row_dy = offset;
5575     }
5576 }
5577
5578 /* Always call this iff dy is in the visible range.  If the tree is empty, then
5579  * it's set to be NULL, and top_row_dy is 0;
5580  */
5581 static void
5582 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5583 {
5584   gint offset;
5585   GtkTreePath *path;
5586   int node;
5587
5588   if (tree_view->priv->row_count == 0)
5589     {
5590       pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5591     }
5592   else
5593     {
5594       offset = pspp_sheet_view_find_offset (tree_view,
5595                                             tree_view->priv->dy,
5596                                             &node);
5597
5598       if (node < 0)
5599         {
5600           pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5601         }
5602       else
5603         {
5604           path = _pspp_sheet_view_find_path (tree_view, node);
5605           pspp_sheet_view_set_top_row (tree_view, path, offset);
5606           gtk_tree_path_free (path);
5607         }
5608     }
5609 }
5610
5611 static void
5612 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5613 {
5614   GtkTreePath *path;
5615   int node;
5616   int new_dy;
5617
5618   /* Avoid recursive calls */
5619   if (tree_view->priv->in_top_row_to_dy)
5620     return;
5621
5622   if (tree_view->priv->top_row)
5623     path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5624   else
5625     path = NULL;
5626
5627   if (!path)
5628     node = -1;
5629   else
5630     _pspp_sheet_view_find_node (tree_view, path, &node);
5631
5632   if (path)
5633     gtk_tree_path_free (path);
5634
5635   if (node < 0)
5636     {
5637       /* keep dy and set new toprow */
5638       gtk_tree_row_reference_free (tree_view->priv->top_row);
5639       tree_view->priv->top_row = NULL;
5640       tree_view->priv->top_row_dy = 0;
5641       /* DO NOT install the idle handler */
5642       pspp_sheet_view_dy_to_top_row (tree_view);
5643       return;
5644     }
5645
5646   if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5647     {
5648       /* new top row -- do NOT install the idle handler */
5649       pspp_sheet_view_dy_to_top_row (tree_view);
5650       return;
5651     }
5652
5653   new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5654   new_dy += tree_view->priv->top_row_dy;
5655
5656   if (new_dy + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
5657     new_dy = tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
5658
5659   new_dy = MAX (0, new_dy);
5660
5661   tree_view->priv->in_top_row_to_dy = TRUE;
5662   gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5663   tree_view->priv->in_top_row_to_dy = FALSE;
5664 }
5665
5666
5667 void
5668 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5669 {
5670   install_presize_handler (tree_view);
5671 }
5672
5673 /* Drag-and-drop */
5674
5675 static void
5676 set_source_row (GdkDragContext *context,
5677                 GtkTreeModel   *model,
5678                 GtkTreePath    *source_row)
5679 {
5680   g_object_set_data_full (G_OBJECT (context),
5681                           "gtk-tree-view-source-row",
5682                           source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5683                           (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5684 }
5685
5686 static GtkTreePath*
5687 get_source_row (GdkDragContext *context)
5688 {
5689   GtkTreeRowReference *ref =
5690     g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5691
5692   if (ref)
5693     return gtk_tree_row_reference_get_path (ref);
5694   else
5695     return NULL;
5696 }
5697
5698 typedef struct
5699 {
5700   GtkTreeRowReference *dest_row;
5701   guint                path_down_mode   : 1;
5702   guint                empty_view_drop  : 1;
5703   guint                drop_append_mode : 1;
5704 }
5705 DestRow;
5706
5707 static void
5708 dest_row_free (gpointer data)
5709 {
5710   DestRow *dr = (DestRow *)data;
5711
5712   gtk_tree_row_reference_free (dr->dest_row);
5713   g_slice_free (DestRow, dr);
5714 }
5715
5716 static void
5717 set_dest_row (GdkDragContext *context,
5718               GtkTreeModel   *model,
5719               GtkTreePath    *dest_row,
5720               gboolean        path_down_mode,
5721               gboolean        empty_view_drop,
5722               gboolean        drop_append_mode)
5723 {
5724   DestRow *dr;
5725
5726   if (!dest_row)
5727     {
5728       g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5729                               NULL, NULL);
5730       return;
5731     }
5732
5733   dr = g_slice_new (DestRow);
5734
5735   dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5736   dr->path_down_mode = path_down_mode != FALSE;
5737   dr->empty_view_drop = empty_view_drop != FALSE;
5738   dr->drop_append_mode = drop_append_mode != FALSE;
5739
5740   g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5741                           dr, (GDestroyNotify) dest_row_free);
5742 }
5743
5744 static GtkTreePath*
5745 get_dest_row (GdkDragContext *context,
5746               gboolean       *path_down_mode)
5747 {
5748   DestRow *dr =
5749     g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5750
5751   if (dr)
5752     {
5753       GtkTreePath *path = NULL;
5754
5755       if (path_down_mode)
5756         *path_down_mode = dr->path_down_mode;
5757
5758       if (dr->dest_row)
5759         path = gtk_tree_row_reference_get_path (dr->dest_row);
5760       else if (dr->empty_view_drop)
5761         path = gtk_tree_path_new_from_indices (0, -1);
5762       else
5763         path = NULL;
5764
5765       if (path && dr->drop_append_mode)
5766         gtk_tree_path_next (path);
5767
5768       return path;
5769     }
5770   else
5771     return NULL;
5772 }
5773
5774 /* Get/set whether drag_motion requested the drag data and
5775  * drag_data_received should thus not actually insert the data,
5776  * since the data doesn't result from a drop.
5777  */
5778 static void
5779 set_status_pending (GdkDragContext *context,
5780                     GdkDragAction   suggested_action)
5781 {
5782   g_object_set_data (G_OBJECT (context),
5783                      "gtk-tree-view-status-pending",
5784                      GINT_TO_POINTER (suggested_action));
5785 }
5786
5787 static GdkDragAction
5788 get_status_pending (GdkDragContext *context)
5789 {
5790   return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5791                                              "gtk-tree-view-status-pending"));
5792 }
5793
5794 static TreeViewDragInfo*
5795 get_info (PsppSheetView *tree_view)
5796 {
5797   return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5798 }
5799
5800 static void
5801 destroy_info (TreeViewDragInfo *di)
5802 {
5803   g_slice_free (TreeViewDragInfo, di);
5804 }
5805
5806 static TreeViewDragInfo*
5807 ensure_info (PsppSheetView *tree_view)
5808 {
5809   TreeViewDragInfo *di;
5810
5811   di = get_info (tree_view);
5812
5813   if (di == NULL)
5814     {
5815       di = g_slice_new0 (TreeViewDragInfo);
5816
5817       g_object_set_data_full (G_OBJECT (tree_view),
5818                               "gtk-tree-view-drag-info",
5819                               di,
5820                               (GDestroyNotify) destroy_info);
5821     }
5822
5823   return di;
5824 }
5825
5826 static void
5827 remove_info (PsppSheetView *tree_view)
5828 {
5829   g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5830 }
5831
5832 #if 0
5833 static gint
5834 drag_scan_timeout (gpointer data)
5835 {
5836   PsppSheetView *tree_view;
5837   gint x, y;
5838   GdkModifierType state;
5839   GtkTreePath *path = NULL;
5840   PsppSheetViewColumn *column = NULL;
5841   GdkRectangle visible_rect;
5842
5843   GDK_THREADS_ENTER ();
5844
5845   tree_view = PSPP_SHEET_VIEW (data);
5846
5847   gdk_window_get_pointer (tree_view->priv->bin_window,
5848                           &x, &y, &state);
5849
5850   pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5851
5852   /* See if we are near the edge. */
5853   if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5854       (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5855       (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5856       (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5857     {
5858       pspp_sheet_view_get_path_at_pos (tree_view,
5859                                      tree_view->priv->bin_window,
5860                                      x, y,
5861                                      &path,
5862                                      &column,
5863                                      NULL,
5864                                      NULL);
5865
5866       if (path != NULL)
5867         {
5868           pspp_sheet_view_scroll_to_cell (tree_view,
5869                                         path,
5870                                         column,
5871                                         TRUE,
5872                                         0.5, 0.5);
5873
5874           gtk_tree_path_free (path);
5875         }
5876     }
5877
5878   GDK_THREADS_LEAVE ();
5879
5880   return TRUE;
5881 }
5882 #endif /* 0 */
5883
5884 static void
5885 add_scroll_timeout (PsppSheetView *tree_view)
5886 {
5887   if (tree_view->priv->scroll_timeout == 0)
5888     {
5889       tree_view->priv->scroll_timeout =
5890         gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5891     }
5892 }
5893
5894 static void
5895 remove_scroll_timeout (PsppSheetView *tree_view)
5896 {
5897   if (tree_view->priv->scroll_timeout != 0)
5898     {
5899       g_source_remove (tree_view->priv->scroll_timeout);
5900       tree_view->priv->scroll_timeout = 0;
5901     }
5902 }
5903
5904 static gboolean
5905 check_model_dnd (GtkTreeModel *model,
5906                  GType         required_iface,
5907                  const gchar  *signal)
5908 {
5909   if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5910     {
5911       g_warning ("You must override the default '%s' handler "
5912                  "on PsppSheetView when using models that don't support "
5913                  "the %s interface and enabling drag-and-drop. The simplest way to do this "
5914                  "is to connect to '%s' and call "
5915                  "g_signal_stop_emission_by_name() in your signal handler to prevent "
5916                  "the default handler from running. Look at the source code "
5917                  "for the default handler in gtktreeview.c to get an idea what "
5918                  "your handler should do. (gtktreeview.c is in the GTK source "
5919                  "code.) If you're using GTK from a language other than C, "
5920                  "there may be a more natural way to override default handlers, e.g. via derivation.",
5921                  signal, g_type_name (required_iface), signal);
5922       return FALSE;
5923     }
5924   else
5925     return TRUE;
5926 }
5927
5928 static gboolean
5929 scroll_row_timeout (gpointer data)
5930 {
5931   PsppSheetView *tree_view = data;
5932
5933   pspp_sheet_view_horizontal_autoscroll (tree_view);
5934   pspp_sheet_view_vertical_autoscroll (tree_view);
5935
5936   if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5937     pspp_sheet_view_update_rubber_band (tree_view);
5938
5939   return TRUE;
5940 }
5941
5942 /* Returns TRUE if event should not be propagated to parent widgets */
5943 static gboolean
5944 set_destination_row (PsppSheetView    *tree_view,
5945                      GdkDragContext *context,
5946                      /* coordinates relative to the widget */
5947                      gint            x,
5948                      gint            y,
5949                      GdkDragAction  *suggested_action,
5950                      GdkAtom        *target)
5951 {
5952   GtkTreePath *path = NULL;
5953   PsppSheetViewDropPosition pos;
5954   PsppSheetViewDropPosition old_pos;
5955   TreeViewDragInfo *di;
5956   GtkWidget *widget;
5957   GtkTreePath *old_dest_path = NULL;
5958   gboolean can_drop = FALSE;
5959
5960   *suggested_action = 0;
5961   *target = GDK_NONE;
5962
5963   widget = GTK_WIDGET (tree_view);
5964
5965   di = get_info (tree_view);
5966
5967   if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5968     {
5969       /* someone unset us as a drag dest, note that if
5970        * we return FALSE drag_leave isn't called
5971        */
5972
5973       pspp_sheet_view_set_drag_dest_row (tree_view,
5974                                        NULL,
5975                                        PSPP_SHEET_VIEW_DROP_BEFORE);
5976
5977       remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5978
5979       return FALSE; /* no longer a drop site */
5980     }
5981
5982   *target = gtk_drag_dest_find_target (widget, context,
5983                                        gtk_drag_dest_get_target_list (widget));
5984   if (*target == GDK_NONE)
5985     {
5986       return FALSE;
5987     }
5988
5989   if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
5990                                           x, y,
5991                                           &path,
5992                                           &pos))
5993     {
5994       gint n_children;
5995       GtkTreeModel *model;
5996
5997       /* the row got dropped on empty space, let's setup a special case
5998        */
5999
6000       if (path)
6001         gtk_tree_path_free (path);
6002
6003       model = pspp_sheet_view_get_model (tree_view);
6004
6005       n_children = gtk_tree_model_iter_n_children (model, NULL);
6006       if (n_children)
6007         {
6008           pos = PSPP_SHEET_VIEW_DROP_AFTER;
6009           path = gtk_tree_path_new_from_indices (n_children - 1, -1);
6010         }
6011       else
6012         {
6013           pos = PSPP_SHEET_VIEW_DROP_BEFORE;
6014           path = gtk_tree_path_new_from_indices (0, -1);
6015         }
6016
6017       can_drop = TRUE;
6018
6019       goto out;
6020     }
6021
6022   g_assert (path);
6023
6024   /* If we left the current row's "open" zone, unset the timeout for
6025    * opening the row
6026    */
6027   pspp_sheet_view_get_drag_dest_row (tree_view,
6028                                    &old_dest_path,
6029                                    &old_pos);
6030
6031   if (old_dest_path)
6032     gtk_tree_path_free (old_dest_path);
6033
6034   if (TRUE /* FIXME if the location droppable predicate */)
6035     {
6036       can_drop = TRUE;
6037     }
6038
6039 out:
6040   if (can_drop)
6041     {
6042       GtkWidget *source_widget;
6043
6044       *suggested_action = gdk_drag_context_get_suggested_action (context);
6045       source_widget = gtk_drag_get_source_widget (context);
6046
6047       if (source_widget == widget)
6048         {
6049           /* Default to MOVE, unless the user has
6050            * pressed ctrl or shift to affect available actions
6051            */
6052           if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
6053             *suggested_action = GDK_ACTION_MOVE;
6054         }
6055
6056       pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6057                                        path, pos);
6058     }
6059   else
6060     {
6061       /* can't drop here */
6062       pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6063                                        NULL,
6064                                        PSPP_SHEET_VIEW_DROP_BEFORE);
6065     }
6066
6067   if (path)
6068     gtk_tree_path_free (path);
6069
6070   return TRUE;
6071 }
6072
6073 static GtkTreePath*
6074 get_logical_dest_row (PsppSheetView *tree_view,
6075                       gboolean    *path_down_mode,
6076                       gboolean    *drop_append_mode)
6077 {
6078   /* adjust path to point to the row the drop goes in front of */
6079   GtkTreePath *path = NULL;
6080   PsppSheetViewDropPosition pos;
6081
6082   g_return_val_if_fail (path_down_mode != NULL, NULL);
6083   g_return_val_if_fail (drop_append_mode != NULL, NULL);
6084
6085   *path_down_mode = FALSE;
6086   *drop_append_mode = 0;
6087
6088   pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6089
6090   if (path == NULL)
6091     return NULL;
6092
6093   if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
6094     ; /* do nothing */
6095   else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
6096            pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
6097     *path_down_mode = TRUE;
6098   else
6099     {
6100       GtkTreeIter iter;
6101       GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
6102
6103       g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
6104
6105       if (!gtk_tree_model_get_iter (model, &iter, path) ||
6106           !gtk_tree_model_iter_next (model, &iter))
6107         *drop_append_mode = 1;
6108       else
6109         {
6110           *drop_append_mode = 0;
6111           gtk_tree_path_next (path);
6112         }
6113     }
6114
6115   return path;
6116 }
6117
6118 static gboolean
6119 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView      *tree_view,
6120                                         GdkEventMotion   *event)
6121 {
6122   GtkWidget *widget = GTK_WIDGET (tree_view);
6123   GdkDragContext *context;
6124   TreeViewDragInfo *di;
6125   GtkTreePath *path = NULL;
6126   gint button;
6127   gint cell_x, cell_y;
6128   GtkTreeModel *model;
6129   gboolean retval = FALSE;
6130
6131   di = get_info (tree_view);
6132
6133   if (di == NULL || !di->source_set)
6134     goto out;
6135
6136   if (tree_view->priv->pressed_button < 0)
6137     goto out;
6138
6139   if (!gtk_drag_check_threshold (widget,
6140                                  tree_view->priv->press_start_x,
6141                                  tree_view->priv->press_start_y,
6142                                  event->x, event->y))
6143     goto out;
6144
6145   model = pspp_sheet_view_get_model (tree_view);
6146
6147   if (model == NULL)
6148     goto out;
6149
6150   button = tree_view->priv->pressed_button;
6151   tree_view->priv->pressed_button = -1;
6152
6153   pspp_sheet_view_get_path_at_pos (tree_view,
6154                                  tree_view->priv->press_start_x,
6155                                  tree_view->priv->press_start_y,
6156                                  &path,
6157                                  NULL,
6158                                  &cell_x,
6159                                  &cell_y);
6160
6161   if (path == NULL)
6162     goto out;
6163
6164   if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6165       !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6166                                            path))
6167     goto out;
6168
6169   if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6170     goto out;
6171
6172   /* Now we can begin the drag */
6173
6174   retval = TRUE;
6175
6176   context = gtk_drag_begin (widget,
6177                             gtk_drag_source_get_target_list (widget),
6178                             di->source_actions,
6179                             button,
6180                             (GdkEvent*)event);
6181
6182   set_source_row (context, model, path);
6183
6184  out:
6185   if (path)
6186     gtk_tree_path_free (path);
6187
6188   return retval;
6189 }
6190
6191
6192
6193 static void
6194 pspp_sheet_view_drag_begin (GtkWidget      *widget,
6195                           GdkDragContext *context)
6196 {
6197 #if GTK3_TRANSITION
6198   PsppSheetView *tree_view;
6199   GtkTreePath *path = NULL;
6200   gint cell_x, cell_y;
6201   GdkPixmap *row_pix;
6202   TreeViewDragInfo *di;
6203
6204   tree_view = PSPP_SHEET_VIEW (widget);
6205
6206   /* if the user uses a custom DND source impl, we don't set the icon here */
6207   di = get_info (tree_view);
6208
6209   if (di == NULL || !di->source_set)
6210     return;
6211
6212   pspp_sheet_view_get_path_at_pos (tree_view,
6213                                  tree_view->priv->press_start_x,
6214                                  tree_view->priv->press_start_y,
6215                                  &path,
6216                                  NULL,
6217                                  &cell_x,
6218                                  &cell_y);
6219
6220   g_return_if_fail (path != NULL);
6221
6222   row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6223                                                 path);
6224
6225   gtk_drag_set_icon_pixmap (context,
6226                             gdk_drawable_get_colormap (row_pix),
6227                             row_pix,
6228                             NULL,
6229                             /* the + 1 is for the black border in the icon */
6230                             tree_view->priv->press_start_x + 1,
6231                             cell_y + 1);
6232
6233   g_object_unref (row_pix);
6234   gtk_tree_path_free (path);
6235 #endif
6236 }
6237
6238
6239 static void
6240 pspp_sheet_view_drag_end (GtkWidget      *widget,
6241                         GdkDragContext *context)
6242 {
6243   /* do nothing */
6244 }
6245
6246 /* Default signal implementations for the drag signals */
6247 static void
6248 pspp_sheet_view_drag_data_get (GtkWidget        *widget,
6249                              GdkDragContext   *context,
6250                              GtkSelectionData *selection_data,
6251                              guint             info,
6252                              guint             time)
6253 {
6254   PsppSheetView *tree_view;
6255   GtkTreeModel *model;
6256   TreeViewDragInfo *di;
6257   GtkTreePath *source_row;
6258
6259   tree_view = PSPP_SHEET_VIEW (widget);
6260
6261   model = pspp_sheet_view_get_model (tree_view);
6262
6263   if (model == NULL)
6264     return;
6265
6266   di = get_info (PSPP_SHEET_VIEW (widget));
6267
6268   if (di == NULL)
6269     return;
6270
6271   source_row = get_source_row (context);
6272
6273   if (source_row == NULL)
6274     return;
6275
6276   /* We can implement the GTK_TREE_MODEL_ROW target generically for
6277    * any model; for DragSource models there are some other targets
6278    * we also support.
6279    */
6280
6281   if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6282       gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6283                                           source_row,
6284                                           selection_data))
6285     goto done;
6286
6287   /* If drag_data_get does nothing, try providing row data. */
6288   if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6289     {
6290       gtk_tree_set_row_drag_data (selection_data,
6291                                   model,
6292                                   source_row);
6293     }
6294
6295  done:
6296   gtk_tree_path_free (source_row);
6297 }
6298
6299
6300 static void
6301 pspp_sheet_view_drag_data_delete (GtkWidget      *widget,
6302                                 GdkDragContext *context)
6303 {
6304   TreeViewDragInfo *di;
6305   GtkTreeModel *model;
6306   PsppSheetView *tree_view;
6307   GtkTreePath *source_row;
6308
6309   tree_view = PSPP_SHEET_VIEW (widget);
6310   model = pspp_sheet_view_get_model (tree_view);
6311
6312   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6313     return;
6314
6315   di = get_info (tree_view);
6316
6317   if (di == NULL)
6318     return;
6319
6320   source_row = get_source_row (context);
6321
6322   if (source_row == NULL)
6323     return;
6324
6325   gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6326                                          source_row);
6327
6328   gtk_tree_path_free (source_row);
6329
6330   set_source_row (context, NULL, NULL);
6331 }
6332
6333 static void
6334 pspp_sheet_view_drag_leave (GtkWidget      *widget,
6335                           GdkDragContext *context,
6336                           guint             time)
6337 {
6338   /* unset any highlight row */
6339   pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6340                                    NULL,
6341                                    PSPP_SHEET_VIEW_DROP_BEFORE);
6342
6343   remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6344 }
6345
6346
6347 static gboolean
6348 pspp_sheet_view_drag_motion (GtkWidget        *widget,
6349                            GdkDragContext   *context,
6350                            /* coordinates relative to the widget */
6351                            gint              x,
6352                            gint              y,
6353                            guint             time)
6354 {
6355   gboolean empty;
6356   GtkTreePath *path = NULL;
6357   PsppSheetViewDropPosition pos;
6358   PsppSheetView *tree_view;
6359   GdkDragAction suggested_action = 0;
6360   GdkAtom target;
6361
6362   tree_view = PSPP_SHEET_VIEW (widget);
6363
6364   if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6365     return FALSE;
6366
6367   pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6368
6369   /* we only know this *after* set_desination_row */
6370   empty = tree_view->priv->empty_view_drop;
6371
6372   if (path == NULL && !empty)
6373     {
6374       /* Can't drop here. */
6375       gdk_drag_status (context, 0, time);
6376     }
6377   else
6378     {
6379       if (tree_view->priv->open_dest_timeout == 0 &&
6380           (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6381            pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6382         {
6383           /* Nothing. */
6384         }
6385       else
6386         {
6387           add_scroll_timeout (tree_view);
6388         }
6389
6390       if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6391         {
6392           /* Request data so we can use the source row when
6393            * determining whether to accept the drop
6394            */
6395           set_status_pending (context, suggested_action);
6396           gtk_drag_get_data (widget, context, target, time);
6397         }
6398       else
6399         {
6400           set_status_pending (context, 0);
6401           gdk_drag_status (context, suggested_action, time);
6402         }
6403     }
6404
6405   if (path)
6406     gtk_tree_path_free (path);
6407
6408   return TRUE;
6409 }
6410
6411
6412 static gboolean
6413 pspp_sheet_view_drag_drop (GtkWidget        *widget,
6414                          GdkDragContext   *context,
6415                          /* coordinates relative to the widget */
6416                          gint              x,
6417                          gint              y,
6418                          guint             time)
6419 {
6420   PsppSheetView *tree_view;
6421   GtkTreePath *path;
6422   GdkDragAction suggested_action = 0;
6423   GdkAtom target = GDK_NONE;
6424   TreeViewDragInfo *di;
6425   GtkTreeModel *model;
6426   gboolean path_down_mode;
6427   gboolean drop_append_mode;
6428
6429   tree_view = PSPP_SHEET_VIEW (widget);
6430
6431   model = pspp_sheet_view_get_model (tree_view);
6432
6433   remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6434
6435   di = get_info (tree_view);
6436
6437   if (di == NULL)
6438     return FALSE;
6439
6440   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6441     return FALSE;
6442
6443   if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6444     return FALSE;
6445
6446   path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6447
6448   if (target != GDK_NONE && path != NULL)
6449     {
6450       /* in case a motion had requested drag data, change things so we
6451        * treat drag data receives as a drop.
6452        */
6453       set_status_pending (context, 0);
6454       set_dest_row (context, model, path,
6455                     path_down_mode, tree_view->priv->empty_view_drop,
6456                     drop_append_mode);
6457     }
6458
6459   if (path)
6460     gtk_tree_path_free (path);
6461
6462   /* Unset this thing */
6463   pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6464                                    NULL,
6465                                    PSPP_SHEET_VIEW_DROP_BEFORE);
6466
6467   if (target != GDK_NONE)
6468     {
6469       gtk_drag_get_data (widget, context, target, time);
6470       return TRUE;
6471     }
6472   else
6473     return FALSE;
6474 }
6475
6476 static void
6477 pspp_sheet_view_drag_data_received (GtkWidget        *widget,
6478                                   GdkDragContext   *context,
6479                                   /* coordinates relative to the widget */
6480                                   gint              x,
6481                                   gint              y,
6482                                   GtkSelectionData *selection_data,
6483                                   guint             info,
6484                                   guint             time)
6485 {
6486   GtkTreePath *path;
6487   TreeViewDragInfo *di;
6488   gboolean accepted = FALSE;
6489   GtkTreeModel *model;
6490   PsppSheetView *tree_view;
6491   GtkTreePath *dest_row;
6492   GdkDragAction suggested_action;
6493   gboolean path_down_mode;
6494   gboolean drop_append_mode;
6495
6496   tree_view = PSPP_SHEET_VIEW (widget);
6497
6498   model = pspp_sheet_view_get_model (tree_view);
6499
6500   if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6501     return;
6502
6503   di = get_info (tree_view);
6504
6505   if (di == NULL)
6506     return;
6507
6508   suggested_action = get_status_pending (context);
6509
6510   if (suggested_action)
6511     {
6512       /* We are getting this data due to a request in drag_motion,
6513        * rather than due to a request in drag_drop, so we are just
6514        * supposed to call drag_status, not actually paste in the
6515        * data.
6516        */
6517       path = get_logical_dest_row (tree_view, &path_down_mode,
6518                                    &drop_append_mode);
6519
6520       if (path == NULL)
6521         suggested_action = 0;
6522       else if (path_down_mode)
6523         gtk_tree_path_down (path);
6524
6525       if (suggested_action)
6526         {
6527           if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6528                                                      path,
6529                                                      selection_data))
6530             {
6531               if (path_down_mode)
6532                 {
6533                   path_down_mode = FALSE;
6534                   gtk_tree_path_up (path);
6535
6536                   if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6537                                                              path,
6538                                                              selection_data))
6539                     suggested_action = 0;
6540                 }
6541               else
6542                 suggested_action = 0;
6543             }
6544         }
6545
6546       gdk_drag_status (context, suggested_action, time);
6547
6548       if (path)
6549         gtk_tree_path_free (path);
6550
6551       /* If you can't drop, remove user drop indicator until the next motion */
6552       if (suggested_action == 0)
6553         pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6554                                          NULL,
6555                                          PSPP_SHEET_VIEW_DROP_BEFORE);
6556
6557       return;
6558     }
6559
6560   dest_row = get_dest_row (context, &path_down_mode);
6561
6562   if (dest_row == NULL)
6563     return;
6564
6565   if (gtk_selection_data_get_length (selection_data) >= 0)
6566     {
6567       if (path_down_mode)
6568         {
6569           gtk_tree_path_down (dest_row);
6570           if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6571                                                      dest_row, selection_data))
6572             gtk_tree_path_up (dest_row);
6573         }
6574     }
6575
6576   if (gtk_selection_data_get_length (selection_data) >= 0)
6577     {
6578       if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6579                                                  dest_row,
6580                                                  selection_data))
6581         accepted = TRUE;
6582     }
6583
6584   gtk_drag_finish (context,
6585                    accepted,
6586                    (gdk_drag_context_get_actions (context) == GDK_ACTION_MOVE),
6587                    time);
6588
6589   if (gtk_tree_path_get_depth (dest_row) == 1
6590       && gtk_tree_path_get_indices (dest_row)[0] == 0)
6591     {
6592       /* special special case drag to "0", scroll to first item */
6593       if (!tree_view->priv->scroll_to_path)
6594         pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6595     }
6596
6597   gtk_tree_path_free (dest_row);
6598
6599   /* drop dest_row */
6600   set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6601 }
6602
6603
6604
6605 /* GtkContainer Methods
6606  */
6607
6608
6609 static void
6610 pspp_sheet_view_remove (GtkContainer *container,
6611                       GtkWidget    *widget)
6612 {
6613   PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6614   PsppSheetViewChild *child = NULL;
6615   GList *tmp_list;
6616
6617   tmp_list = tree_view->priv->children;
6618   while (tmp_list)
6619     {
6620       child = tmp_list->data;
6621       if (child->widget == widget)
6622         {
6623           gtk_widget_unparent (widget);
6624
6625           tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6626           g_list_free_1 (tmp_list);
6627           g_slice_free (PsppSheetViewChild, child);
6628           return;
6629         }
6630
6631       tmp_list = tmp_list->next;
6632     }
6633
6634   tmp_list = tree_view->priv->columns;
6635
6636   while (tmp_list)
6637     {
6638       PsppSheetViewColumn *column;
6639       column = tmp_list->data;
6640       if (column->button == widget)
6641         {
6642           gtk_widget_unparent (widget);
6643           return;
6644         }
6645       tmp_list = tmp_list->next;
6646     }
6647 }
6648
6649 static void
6650 pspp_sheet_view_forall (GtkContainer *container,
6651                       gboolean      include_internals,
6652                       GtkCallback   callback,
6653                       gpointer      callback_data)
6654 {
6655   PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6656   PsppSheetViewChild *child = NULL;
6657   PsppSheetViewColumn *column;
6658   GList *tmp_list;
6659
6660   tmp_list = tree_view->priv->children;
6661   while (tmp_list)
6662     {
6663       child = tmp_list->data;
6664       tmp_list = tmp_list->next;
6665
6666       (* callback) (child->widget, callback_data);
6667     }
6668   if (include_internals == FALSE)
6669     return;
6670
6671   for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6672     {
6673       column = tmp_list->data;
6674
6675       if (column->button)
6676         (* callback) (column->button, callback_data);
6677     }
6678 }
6679
6680 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6681  * cells. If so we draw one big row-spanning focus rectangle.
6682  */
6683 static gboolean
6684 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6685 {
6686   GList *list;
6687
6688   if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6689     return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6690
6691   for (list = tree_view->priv->columns; list; list = list->next)
6692     {
6693       if (!((PsppSheetViewColumn *)list->data)->visible)
6694         continue;
6695       if (_pspp_sheet_view_column_count_special_cells (list->data))
6696         return TRUE;
6697     }
6698
6699   return FALSE;
6700 }
6701
6702 static void
6703 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6704                               PsppSheetViewColumn *focus_column,
6705                               gboolean clamp_column_visible)
6706 {
6707   g_return_if_fail (focus_column != NULL);
6708
6709   tree_view->priv->focus_column = focus_column;
6710
6711   if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view)) != focus_column->button)
6712     gtk_widget_grab_focus (focus_column->button);
6713
6714   if (clamp_column_visible)
6715     pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6716 }
6717
6718 /* Returns TRUE if the focus is within the headers, after the focus operation is
6719  * done
6720  */
6721 static gboolean
6722 pspp_sheet_view_header_focus (PsppSheetView      *tree_view,
6723                             GtkDirectionType  dir,
6724                             gboolean          clamp_column_visible)
6725 {
6726   GtkWidget *focus_child;
6727   PsppSheetViewColumn *focus_column;
6728   GList *last_column, *first_column;
6729   GList *tmp_list;
6730   gboolean rtl;
6731
6732   if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6733     return FALSE;
6734
6735   focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
6736
6737   first_column = tree_view->priv->columns;
6738   while (first_column)
6739     {
6740       PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6741
6742       if (pspp_sheet_view_column_can_focus (c) && c->visible)
6743         break;
6744       first_column = first_column->next;
6745     }
6746
6747   /* No headers are visible, or are focusable.  We can't focus in or out.
6748    */
6749   if (first_column == NULL)
6750     return FALSE;
6751
6752   last_column = g_list_last (tree_view->priv->columns);
6753   while (last_column)
6754     {
6755       PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6756
6757       if (pspp_sheet_view_column_can_focus (c) && c->visible)
6758         break;
6759       last_column = last_column->prev;
6760     }
6761
6762
6763   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6764
6765   switch (dir)
6766     {
6767     case GTK_DIR_TAB_BACKWARD:
6768     case GTK_DIR_TAB_FORWARD:
6769     case GTK_DIR_UP:
6770     case GTK_DIR_DOWN:
6771       if (focus_child == NULL)
6772         {
6773           if (tree_view->priv->focus_column != NULL &&
6774               pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6775             focus_column = tree_view->priv->focus_column;
6776           else
6777             focus_column = first_column->data;
6778           pspp_sheet_view_focus_column (tree_view, focus_column,
6779                                         clamp_column_visible);
6780           return TRUE;
6781         }
6782       return FALSE;
6783
6784     case GTK_DIR_LEFT:
6785     case GTK_DIR_RIGHT:
6786       if (focus_child == NULL)
6787         {
6788           if (tree_view->priv->focus_column != NULL)
6789             focus_column = tree_view->priv->focus_column;
6790           else if (dir == GTK_DIR_LEFT)
6791             focus_column = last_column->data;
6792           else
6793             focus_column = first_column->data;
6794           pspp_sheet_view_focus_column (tree_view, focus_column,
6795                                         clamp_column_visible);
6796           return TRUE;
6797         }
6798
6799       if (gtk_widget_child_focus (focus_child, dir))
6800         {
6801           /* The focus moves inside the button. */
6802           /* This is probably a great example of bad UI */
6803           if (clamp_column_visible)
6804             pspp_sheet_view_clamp_column_visible (tree_view,
6805                                                   tree_view->priv->focus_column,
6806                                                   FALSE);
6807           return TRUE;
6808         }
6809
6810       /* We need to move the focus among the row of buttons. */
6811       for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6812         if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6813           break;
6814
6815       if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6816           || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6817         {
6818           gtk_widget_error_bell (GTK_WIDGET (tree_view));
6819           return TRUE;
6820         }
6821
6822       while (tmp_list)
6823         {
6824           PsppSheetViewColumn *column;
6825
6826           if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6827             tmp_list = tmp_list->next;
6828           else
6829             tmp_list = tmp_list->prev;
6830
6831           if (tmp_list == NULL)
6832             {
6833               g_warning ("Internal button not found");
6834               break;
6835             }
6836           column = tmp_list->data;
6837           if (column->visible &&
6838               pspp_sheet_view_column_can_focus (column))
6839             {
6840               if (column->button)
6841                 {
6842                   pspp_sheet_view_focus_column (tree_view, column,
6843                                                 clamp_column_visible);
6844                   return TRUE;
6845                 }
6846             }
6847         }
6848       return FALSE;
6849
6850     default:
6851       g_assert_not_reached ();
6852       break;
6853     }
6854
6855   return FALSE;
6856 }
6857
6858 /* This function returns in 'path' the first focusable path, if the given path
6859  * is already focusable, it's the returned one.
6860  *
6861  */
6862 static gboolean
6863 search_first_focusable_path (PsppSheetView  *tree_view,
6864                              GtkTreePath **path,
6865                              gboolean      search_forward,
6866                              int *new_node)
6867 {
6868   /* XXX this function is trivial given that the sheetview doesn't support
6869      separator rows */
6870   int node = -1;
6871
6872   if (!path || !*path)
6873     return FALSE;
6874
6875   _pspp_sheet_view_find_node (tree_view, *path, &node);
6876
6877   if (node < 0)
6878     return FALSE;
6879
6880   if (new_node)
6881     *new_node = node;
6882
6883   return (*path != NULL);
6884 }
6885
6886 static gint
6887 pspp_sheet_view_focus (GtkWidget        *widget,
6888                      GtkDirectionType  direction)
6889 {
6890   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6891   GtkContainer *container = GTK_CONTAINER (widget);
6892   GtkWidget *focus_child;
6893
6894   if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6895     return FALSE;
6896
6897   focus_child = gtk_container_get_focus_child (container);
6898
6899   pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6900   /* Case 1.  Headers currently have focus. */
6901   if (focus_child)
6902     {
6903       switch (direction)
6904         {
6905         case GTK_DIR_LEFT:
6906         case GTK_DIR_RIGHT:
6907           pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6908           return TRUE;
6909         case GTK_DIR_TAB_BACKWARD:
6910         case GTK_DIR_UP:
6911           return FALSE;
6912         case GTK_DIR_TAB_FORWARD:
6913         case GTK_DIR_DOWN:
6914           gtk_widget_grab_focus (widget);
6915           return TRUE;
6916         default:
6917           g_assert_not_reached ();
6918           return FALSE;
6919         }
6920     }
6921
6922   /* Case 2. We don't have focus at all. */
6923   if (!gtk_widget_has_focus (widget))
6924     {
6925       if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6926         gtk_widget_grab_focus (widget);
6927       return TRUE;
6928     }
6929
6930   /* Case 3. We have focus already. */
6931   if (direction == GTK_DIR_TAB_BACKWARD)
6932     return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6933   else if (direction == GTK_DIR_TAB_FORWARD)
6934     return FALSE;
6935
6936   /* Other directions caught by the keybindings */
6937   gtk_widget_grab_focus (widget);
6938   return TRUE;
6939 }
6940
6941 static void
6942 pspp_sheet_view_grab_focus (GtkWidget *widget)
6943 {
6944   GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6945
6946   pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6947 }
6948
6949 static void
6950 pspp_sheet_view_style_set (GtkWidget *widget,
6951                          GtkStyle *previous_style)
6952 {
6953   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6954   GList *list;
6955   PsppSheetViewColumn *column;
6956
6957   if (gtk_widget_get_realized (widget))
6958     {
6959       gdk_window_set_background (tree_view->priv->bin_window, &gtk_widget_get_style (widget)->base[gtk_widget_get_state (widget)]);
6960       gtk_style_set_background (gtk_widget_get_style (widget), tree_view->priv->header_window, GTK_STATE_NORMAL);
6961       pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6962     }
6963
6964   gtk_widget_style_get (widget,
6965                         "expander-size", &tree_view->priv->expander_size,
6966                         NULL);
6967   tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6968
6969   for (list = tree_view->priv->columns; list; list = list->next)
6970     {
6971       column = list->data;
6972       _pspp_sheet_view_column_cell_set_dirty (column);
6973     }
6974
6975   tree_view->priv->fixed_height = -1;
6976
6977   /* Invalidate cached button style. */
6978   if (tree_view->priv->button_style)
6979     {
6980       g_object_unref (tree_view->priv->button_style);
6981       tree_view->priv->button_style = NULL;
6982     }
6983
6984   gtk_widget_queue_resize (widget);
6985 }
6986
6987
6988 static void
6989 pspp_sheet_view_set_focus_child (GtkContainer *container,
6990                                GtkWidget    *child)
6991 {
6992   PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6993   GList *list;
6994
6995   for (list = tree_view->priv->columns; list; list = list->next)
6996     {
6997       if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
6998         {
6999           tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7000           break;
7001         }
7002     }
7003
7004   GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
7005 }
7006
7007 static void
7008 pspp_sheet_view_set_adjustments (PsppSheetView   *tree_view,
7009                                GtkAdjustment *hadj,
7010                                GtkAdjustment *vadj)
7011 {
7012   gboolean need_adjust = FALSE;
7013
7014   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7015
7016   if (hadj)
7017     g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
7018   else
7019     hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7020   if (vadj)
7021     g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
7022   else
7023     vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7024
7025   if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
7026     {
7027       g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
7028                                             pspp_sheet_view_adjustment_changed,
7029                                             tree_view);
7030       g_object_unref (tree_view->priv->hadjustment);
7031     }
7032
7033   if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
7034     {
7035       g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
7036                                             pspp_sheet_view_adjustment_changed,
7037                                             tree_view);
7038       g_object_unref (tree_view->priv->vadjustment);
7039     }
7040
7041   if (tree_view->priv->hadjustment != hadj)
7042     {
7043       tree_view->priv->hadjustment = hadj;
7044       g_object_ref_sink (tree_view->priv->hadjustment);
7045
7046       g_signal_connect (tree_view->priv->hadjustment, "value-changed",
7047                         G_CALLBACK (pspp_sheet_view_adjustment_changed),
7048                         tree_view);
7049       need_adjust = TRUE;
7050     }
7051
7052   if (tree_view->priv->vadjustment != vadj)
7053     {
7054       tree_view->priv->vadjustment = vadj;
7055       g_object_ref_sink (tree_view->priv->vadjustment);
7056
7057       g_signal_connect (tree_view->priv->vadjustment, "value-changed",
7058                         G_CALLBACK (pspp_sheet_view_adjustment_changed),
7059                         tree_view);
7060       need_adjust = TRUE;
7061     }
7062
7063   if (need_adjust)
7064     pspp_sheet_view_adjustment_changed (NULL, tree_view);
7065 }
7066
7067
7068 static gboolean
7069 pspp_sheet_view_real_move_cursor (PsppSheetView       *tree_view,
7070                                 GtkMovementStep    step,
7071                                 gint               count)
7072 {
7073   PsppSheetSelectMode mode;
7074   GdkModifierType state;
7075
7076   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
7077   g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
7078                         step == GTK_MOVEMENT_VISUAL_POSITIONS ||
7079                         step == GTK_MOVEMENT_DISPLAY_LINES ||
7080                         step == GTK_MOVEMENT_PAGES ||
7081                         step == GTK_MOVEMENT_BUFFER_ENDS ||
7082                         step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
7083
7084   if (tree_view->priv->row_count == 0)
7085     return FALSE;
7086   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7087     return FALSE;
7088
7089   pspp_sheet_view_stop_editing (tree_view, FALSE);
7090   PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7091   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7092
7093   mode = 0;
7094   if (gtk_get_current_event_state (&state))
7095     {
7096       if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7097         mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
7098       if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
7099         mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
7100     }
7101   /* else we assume not pressed */
7102
7103   switch (step)
7104     {
7105     case GTK_MOVEMENT_LOGICAL_POSITIONS:
7106       pspp_sheet_view_move_cursor_tab (tree_view, count);
7107       break;
7108     case GTK_MOVEMENT_VISUAL_POSITIONS:
7109       pspp_sheet_view_move_cursor_left_right (tree_view, count, mode);
7110       break;
7111     case GTK_MOVEMENT_DISPLAY_LINES:
7112       pspp_sheet_view_move_cursor_up_down (tree_view, count, mode);
7113       break;
7114     case GTK_MOVEMENT_PAGES:
7115       pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode);
7116       break;
7117     case GTK_MOVEMENT_BUFFER_ENDS:
7118       pspp_sheet_view_move_cursor_start_end (tree_view, count, mode);
7119       break;
7120     case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7121       pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode);
7122       break;
7123     default:
7124       g_assert_not_reached ();
7125     }
7126
7127   return TRUE;
7128 }
7129
7130 static void
7131 pspp_sheet_view_put (PsppSheetView *tree_view,
7132                    GtkWidget   *child_widget,
7133                    /* in bin_window coordinates */
7134                    gint         x,
7135                    gint         y,
7136                    gint         width,
7137                    gint         height)
7138 {
7139   PsppSheetViewChild *child;
7140   
7141   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7142   g_return_if_fail (GTK_IS_WIDGET (child_widget));
7143
7144   child = g_slice_new (PsppSheetViewChild);
7145
7146   child->widget = child_widget;
7147   child->x = x;
7148   child->y = y;
7149   child->width = width;
7150   child->height = height;
7151
7152   tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7153
7154   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7155     gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7156   
7157   gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7158 }
7159
7160 void
7161 _pspp_sheet_view_child_move_resize (PsppSheetView *tree_view,
7162                                   GtkWidget   *widget,
7163                                   /* in tree coordinates */
7164                                   gint         x,
7165                                   gint         y,
7166                                   gint         width,
7167                                   gint         height)
7168 {
7169   PsppSheetViewChild *child = NULL;
7170   GList *list;
7171   GdkRectangle allocation;
7172
7173   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7174   g_return_if_fail (GTK_IS_WIDGET (widget));
7175
7176   for (list = tree_view->priv->children; list; list = list->next)
7177     {
7178       if (((PsppSheetViewChild *)list->data)->widget == widget)
7179         {
7180           child = list->data;
7181           break;
7182         }
7183     }
7184   if (child == NULL)
7185     return;
7186
7187   allocation.x = child->x = x;
7188   allocation.y = child->y = y;
7189   allocation.width = child->width = width;
7190   allocation.height = child->height = height;
7191
7192   if (gtk_widget_get_realized (widget))
7193     gtk_widget_size_allocate (widget, &allocation);
7194 }
7195
7196
7197 /* TreeModel Callbacks
7198  */
7199
7200 static void
7201 pspp_sheet_view_row_changed (GtkTreeModel *model,
7202                            GtkTreePath  *path,
7203                            GtkTreeIter  *iter,
7204                            gpointer      data)
7205 {
7206   PsppSheetView *tree_view = (PsppSheetView *)data;
7207   int node;
7208   gboolean free_path = FALSE;
7209   GtkTreePath *cursor_path;
7210
7211   g_return_if_fail (path != NULL || iter != NULL);
7212
7213   if (tree_view->priv->cursor != NULL)
7214     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7215   else
7216     cursor_path = NULL;
7217
7218   if (tree_view->priv->edited_column &&
7219       (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7220     pspp_sheet_view_stop_editing (tree_view, TRUE);
7221
7222   if (cursor_path != NULL)
7223     gtk_tree_path_free (cursor_path);
7224
7225   if (path == NULL)
7226     {
7227       path = gtk_tree_model_get_path (model, iter);
7228       free_path = TRUE;
7229     }
7230   else if (iter == NULL)
7231     gtk_tree_model_get_iter (model, iter, path);
7232
7233   _pspp_sheet_view_find_node (tree_view,
7234                               path,
7235                               &node);
7236
7237   if (node >= 0)
7238     {
7239       if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7240         pspp_sheet_view_node_queue_redraw (tree_view, node);
7241     }
7242   
7243   if (free_path)
7244     gtk_tree_path_free (path);
7245 }
7246
7247 static void
7248 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7249                             GtkTreePath  *path,
7250                             GtkTreeIter  *iter,
7251                             gpointer      data)
7252 {
7253   PsppSheetView *tree_view = (PsppSheetView *) data;
7254   gint *indices;
7255   int tmpnode = -1;
7256   gint height = tree_view->priv->fixed_height;
7257   gboolean free_path = FALSE;
7258   gboolean node_visible = TRUE;
7259
7260   g_return_if_fail (path != NULL || iter != NULL);
7261
7262   if (path == NULL)
7263     {
7264       path = gtk_tree_model_get_path (model, iter);
7265       free_path = TRUE;
7266     }
7267   else if (iter == NULL)
7268     gtk_tree_model_get_iter (model, iter, path);
7269
7270   tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7271
7272   /* Update all row-references */
7273   gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7274   indices = gtk_tree_path_get_indices (path);
7275   tmpnode = indices[0];
7276
7277   range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7278
7279   if (height > 0)
7280     {
7281       if (node_visible && node_is_visible (tree_view, tmpnode))
7282         gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7283       else
7284         gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7285     }
7286   else
7287     install_presize_handler (tree_view);
7288   if (free_path)
7289     gtk_tree_path_free (path);
7290 }
7291
7292 static void
7293 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7294                            GtkTreePath  *path,
7295                            gpointer      data)
7296 {
7297   PsppSheetView *tree_view = (PsppSheetView *)data;
7298   int node;
7299
7300   g_return_if_fail (path != NULL);
7301
7302   gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7303
7304   _pspp_sheet_view_find_node (tree_view, path, &node);
7305
7306   if (node < 0)
7307     return;
7308
7309   range_tower_delete (tree_view->priv->selected, node, 1);
7310
7311   /* Ensure we don't have a dangling pointer to a dead node */
7312   ensure_unprelighted (tree_view);
7313
7314   /* Cancel editting if we've started */
7315   pspp_sheet_view_stop_editing (tree_view, TRUE);
7316
7317   if (tree_view->priv->destroy_count_func)
7318     {
7319       gint child_count = 0;
7320       tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7321     }
7322
7323   tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7324
7325   if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7326     {
7327       gtk_tree_row_reference_free (tree_view->priv->top_row);
7328       tree_view->priv->top_row = NULL;
7329     }
7330
7331   install_scroll_sync_handler (tree_view);
7332
7333   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7334
7335 #if 0
7336   if (helper_data.changed)
7337     g_signal_emit_by_name (tree_view->priv->selection, "changed");
7338 #endif
7339 }
7340
7341 static void
7342 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7343                               GtkTreePath  *parent,
7344                               GtkTreeIter  *iter,
7345                               gint         *new_order,
7346                               gpointer      data)
7347 {
7348   PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7349   gint len;
7350
7351   /* XXX need to adjust selection */
7352   len = gtk_tree_model_iter_n_children (model, iter);
7353
7354   if (len < 2)
7355     return;
7356
7357   gtk_tree_row_reference_reordered (G_OBJECT (data),
7358                                     parent,
7359                                     iter,
7360                                     new_order);
7361
7362   if (gtk_tree_path_get_depth (parent) != 0)
7363     return;
7364
7365   if (tree_view->priv->edited_column)
7366     pspp_sheet_view_stop_editing (tree_view, TRUE);
7367
7368   /* we need to be unprelighted */
7369   ensure_unprelighted (tree_view);
7370
7371   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7372
7373   pspp_sheet_view_dy_to_top_row (tree_view);
7374 }
7375
7376
7377 /* Internal tree functions
7378  */
7379
7380
7381 static void
7382 pspp_sheet_view_get_background_xrange (PsppSheetView       *tree_view,
7383                                      PsppSheetViewColumn *column,
7384                                      gint              *x1,
7385                                      gint              *x2)
7386 {
7387   PsppSheetViewColumn *tmp_column = NULL;
7388   gint total_width;
7389   GList *list;
7390   gboolean rtl;
7391
7392   if (x1)
7393     *x1 = 0;
7394
7395   if (x2)
7396     *x2 = 0;
7397
7398   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7399
7400   total_width = 0;
7401   for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7402        list;
7403        list = (rtl ? list->prev : list->next))
7404     {
7405       tmp_column = list->data;
7406
7407       if (tmp_column == column)
7408         break;
7409
7410       if (tmp_column->visible)
7411         total_width += tmp_column->width;
7412     }
7413
7414   if (tmp_column != column)
7415     {
7416       g_warning (G_STRLOC": passed-in column isn't in the tree");
7417       return;
7418     }
7419
7420   if (x1)
7421     *x1 = total_width;
7422
7423   if (x2)
7424     {
7425       if (column->visible)
7426         *x2 = total_width + column->width;
7427       else
7428         *x2 = total_width; /* width of 0 */
7429     }
7430 }
7431
7432 /* Make sure the node is visible vertically */
7433 static void
7434 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7435                                     int node)
7436 {
7437   gint node_dy, height;
7438   GtkTreePath *path = NULL;
7439
7440   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7441     return;
7442
7443   /* just return if the node is visible, avoiding a costly expose */
7444   node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7445   height = ROW_HEIGHT (tree_view);
7446   if (node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment)
7447       && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
7448                               + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
7449     return;
7450
7451   path = _pspp_sheet_view_find_path (tree_view, node);
7452   if (path)
7453     {
7454       /* We process updates because we want to clear old selected items when we scroll.
7455        * if this is removed, we get a "selection streak" at the bottom. */
7456       gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7457       pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7458       gtk_tree_path_free (path);
7459     }
7460 }
7461
7462 static void
7463 pspp_sheet_view_clamp_column_visible (PsppSheetView       *tree_view,
7464                                     PsppSheetViewColumn *column,
7465                                     gboolean           focus_to_cell)
7466 {
7467   gint x, width;
7468
7469   if (column == NULL)
7470     return;
7471
7472   x = column->allocation.x;
7473   width = column->allocation.width;
7474
7475   if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7476     {
7477       /* The column is larger than the horizontal page size.  If the
7478        * column has cells which can be focussed individually, then we make
7479        * sure the cell which gets focus is fully visible (if even the
7480        * focus cell is bigger than the page size, we make sure the
7481        * left-hand side of the cell is visible).
7482        *
7483        * If the column does not have those so-called special cells, we
7484        * make sure the left-hand side of the column is visible.
7485        */
7486
7487       if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7488         {
7489           GtkTreePath *cursor_path;
7490           GdkRectangle background_area, cell_area, focus_area;
7491
7492           cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7493
7494           pspp_sheet_view_get_cell_area (tree_view,
7495                                        cursor_path, column, &cell_area);
7496           pspp_sheet_view_get_background_area (tree_view,
7497                                              cursor_path, column,
7498                                              &background_area);
7499
7500           gtk_tree_path_free (cursor_path);
7501
7502           _pspp_sheet_view_column_get_focus_area (column,
7503                                                 &background_area,
7504                                                 &cell_area,
7505                                                 &focus_area);
7506
7507           x = focus_area.x;
7508           width = focus_area.width;
7509
7510           if (width < gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7511             {
7512               if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7513                 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7514                                           x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7515               else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7516                 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7517             }
7518         }
7519
7520       gtk_adjustment_set_value (tree_view->priv->hadjustment,
7521                                 CLAMP (x,
7522                                        gtk_adjustment_get_lower (tree_view->priv->hadjustment),
7523                                        gtk_adjustment_get_upper (tree_view->priv->hadjustment)
7524                                        - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)));
7525     }
7526   else
7527     {
7528       if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7529           gtk_adjustment_set_value (tree_view->priv->hadjustment,
7530                                     x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7531       else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7532         gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7533   }
7534 }
7535
7536 GtkTreePath *
7537 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7538                             int node)
7539 {
7540   GtkTreePath *path;
7541
7542   path = gtk_tree_path_new ();
7543   if (node >= 0)
7544     gtk_tree_path_append_index (path, node);
7545   return path;
7546 }
7547
7548 void
7549 _pspp_sheet_view_find_node (PsppSheetView  *tree_view,
7550                           GtkTreePath  *path,
7551                           int *node)
7552 {
7553   gint *indices = gtk_tree_path_get_indices (path);
7554   gint depth = gtk_tree_path_get_depth (path);
7555
7556   *node = -1;
7557   if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7558     return;
7559   *node = indices[0];
7560 }
7561
7562 static void
7563 pspp_sheet_view_add_move_binding (GtkBindingSet  *binding_set,
7564                                 guint           keyval,
7565                                 guint           modmask,
7566                                 gboolean        add_shifted_binding,
7567                                 GtkMovementStep step,
7568                                 gint            count)
7569 {
7570   
7571   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7572                                 "move-cursor", 2,
7573                                 G_TYPE_ENUM, step,
7574                                 G_TYPE_INT, count);
7575
7576   if (add_shifted_binding)
7577     gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7578                                   "move-cursor", 2,
7579                                   G_TYPE_ENUM, step,
7580                                   G_TYPE_INT, count);
7581
7582   if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7583    return;
7584
7585   gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7586                                 "move-cursor", 2,
7587                                 G_TYPE_ENUM, step,
7588                                 G_TYPE_INT, count);
7589
7590   gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7591                                 "move-cursor", 2,
7592                                 G_TYPE_ENUM, step,
7593                                 G_TYPE_INT, count);
7594 }
7595
7596 static void
7597 pspp_sheet_view_set_column_drag_info (PsppSheetView       *tree_view,
7598                                     PsppSheetViewColumn *column)
7599 {
7600   PsppSheetViewColumn *left_column;
7601   PsppSheetViewColumn *cur_column = NULL;
7602   PsppSheetViewColumnReorder *reorder;
7603   gboolean rtl;
7604   GList *tmp_list;
7605   gint left;
7606
7607   /* We want to precalculate the motion list such that we know what column slots
7608    * are available.
7609    */
7610   left_column = NULL;
7611   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7612
7613   /* First, identify all possible drop spots */
7614   if (rtl)
7615     tmp_list = g_list_last (tree_view->priv->columns);
7616   else
7617     tmp_list = g_list_first (tree_view->priv->columns);
7618
7619   while (tmp_list)
7620     {
7621       cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7622       tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7623
7624       if (cur_column->visible == FALSE)
7625         continue;
7626
7627       /* If it's not the column moving and func tells us to skip over the column, we continue. */
7628       if (left_column != column && cur_column != column &&
7629           tree_view->priv->column_drop_func &&
7630           ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7631         {
7632           left_column = cur_column;
7633           continue;
7634         }
7635       reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7636       reorder->left_column = left_column;
7637       left_column = reorder->right_column = cur_column;
7638
7639       tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7640     }
7641
7642   /* Add the last one */
7643   if (tree_view->priv->column_drop_func == NULL ||
7644       ((left_column != column) &&
7645        tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7646     {
7647       reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7648       reorder->left_column = left_column;
7649       reorder->right_column = NULL;
7650       tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7651     }
7652
7653   /* We quickly check to see if it even makes sense to reorder columns. */
7654   /* If there is nothing that can be moved, then we return */
7655
7656   if (tree_view->priv->column_drag_info == NULL)
7657     return;
7658
7659   /* We know there are always 2 slots possbile, as you can always return column. */
7660   /* If that's all there is, return */
7661   if (tree_view->priv->column_drag_info->next == NULL || 
7662       (tree_view->priv->column_drag_info->next->next == NULL &&
7663        ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7664        ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7665     {
7666       for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7667         g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7668       g_list_free (tree_view->priv->column_drag_info);
7669       tree_view->priv->column_drag_info = NULL;
7670       return;
7671     }
7672   /* We fill in the ranges for the columns, now that we've isolated them */
7673   left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7674
7675   for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7676     {
7677       reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7678
7679       reorder->left_align = left;
7680       if (tmp_list->next != NULL)
7681         {
7682           g_assert (tmp_list->next->data);
7683           left = reorder->right_align = (reorder->right_column->allocation.x +
7684                                          reorder->right_column->allocation.width +
7685                                          ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7686         }
7687       else
7688         {
7689           gint width = gdk_window_get_width (tree_view->priv->header_window);
7690           reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7691         }
7692     }
7693 }
7694
7695 void
7696 _pspp_sheet_view_column_start_drag (PsppSheetView       *tree_view,
7697                                   PsppSheetViewColumn *column)
7698 {
7699   GdkEvent *send_event;
7700   GtkAllocation allocation;
7701   gint x, y;
7702   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7703   GdkDisplay *display = gdk_screen_get_display (screen);
7704
7705   g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7706   g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7707   g_return_if_fail (column->button);
7708
7709   pspp_sheet_view_set_column_drag_info (tree_view, column);
7710
7711   if (tree_view->priv->column_drag_info == NULL)
7712     return;
7713
7714   if (tree_view->priv->drag_window == NULL)
7715     {
7716       GdkWindowAttr attributes;
7717       guint attributes_mask;
7718
7719       attributes.window_type = GDK_WINDOW_CHILD;
7720       attributes.wclass = GDK_INPUT_OUTPUT;
7721       attributes.x = column->allocation.x;
7722       attributes.y = 0;
7723       attributes.width = column->allocation.width;
7724       attributes.height = column->allocation.height;
7725       attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7726       attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7727       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL ;
7728
7729       tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7730                                                      &attributes,
7731                                                      attributes_mask);
7732       gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7733     }
7734
7735   gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7736   gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7737
7738   gtk_grab_remove (column->button);
7739
7740   send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7741   send_event->crossing.send_event = TRUE;
7742   send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button)));
7743   send_event->crossing.subwindow = NULL;
7744   send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7745   send_event->crossing.time = GDK_CURRENT_TIME;
7746
7747   gtk_propagate_event (column->button, send_event);
7748   gdk_event_free (send_event);
7749
7750   send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7751   send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7752   send_event->button.send_event = TRUE;
7753   send_event->button.time = GDK_CURRENT_TIME;
7754   send_event->button.x = -1;
7755   send_event->button.y = -1;
7756   send_event->button.axes = NULL;
7757   send_event->button.state = 0;
7758   send_event->button.button = 1;
7759   send_event->button.device = 
7760     gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
7761
7762   send_event->button.x_root = 0;
7763   send_event->button.y_root = 0;
7764
7765   gtk_propagate_event (column->button, send_event);
7766   gdk_event_free (send_event);
7767
7768   /* Kids, don't try this at home */
7769   g_object_ref (column->button);
7770   gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7771   gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7772   gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7773   g_object_unref (column->button);
7774
7775   tree_view->priv->drag_column_x = column->allocation.x;
7776   allocation = column->allocation;
7777   allocation.x = 0;
7778   gtk_widget_size_allocate (column->button, &allocation);
7779   gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7780
7781   tree_view->priv->drag_column = column;
7782   gdk_window_show (tree_view->priv->drag_window);
7783
7784   gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7785
7786   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7787   while (gtk_events_pending ())
7788     gtk_main_iteration ();
7789
7790   PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7791   gdk_pointer_grab (tree_view->priv->drag_window,
7792                     FALSE,
7793                     GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7794                     NULL, NULL, GDK_CURRENT_TIME);
7795   gdk_keyboard_grab (tree_view->priv->drag_window,
7796                      FALSE,
7797                      GDK_CURRENT_TIME);
7798 }
7799
7800 void
7801 _pspp_sheet_view_queue_draw_node (PsppSheetView        *tree_view,
7802                                 int node,
7803                                 const GdkRectangle *clip_rect)
7804 {
7805   GdkRectangle rect;
7806   GtkAllocation allocation;
7807
7808   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7809     return;
7810
7811   gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
7812   rect.x = 0;
7813   rect.width = MAX (tree_view->priv->width, allocation.width);
7814
7815   rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7816   rect.height = ROW_HEIGHT (tree_view);
7817
7818   if (clip_rect)
7819     {
7820       GdkRectangle new_rect;
7821
7822       gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7823
7824       gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7825     }
7826   else
7827     {
7828       gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7829     }
7830 }
7831
7832 static void
7833 pspp_sheet_view_queue_draw_path (PsppSheetView        *tree_view,
7834                                GtkTreePath        *path,
7835                                const GdkRectangle *clip_rect)
7836 {
7837   int node = -1;
7838
7839   _pspp_sheet_view_find_node (tree_view, path, &node);
7840
7841   if (node)
7842     _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7843 }
7844
7845 static void
7846 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7847
7848 {
7849   GtkTreePath *cursor_path;
7850
7851   if ((tree_view->priv->row_count == 0) ||
7852       (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7853     return;
7854
7855   cursor_path = NULL;
7856   if (tree_view->priv->cursor)
7857     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7858
7859   if (cursor_path == NULL)
7860     {
7861       /* There's no cursor.  Move the cursor to the first selected row, if any
7862        * are selected, otherwise to the first row in the sheetview.
7863        */
7864       GList *selected_rows;
7865       GtkTreeModel *model;
7866       PsppSheetSelection *selection;
7867
7868       selection = pspp_sheet_view_get_selection (tree_view);
7869       selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7870
7871       if (selected_rows)
7872         {
7873           /* XXX we could avoid doing O(n) work to get this result */
7874           cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7875           g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7876           g_list_free (selected_rows);
7877         }
7878       else
7879         {
7880           cursor_path = gtk_tree_path_new_first ();
7881           search_first_focusable_path (tree_view, &cursor_path,
7882                                        TRUE, NULL);
7883         }
7884
7885       gtk_tree_row_reference_free (tree_view->priv->cursor);
7886       tree_view->priv->cursor = NULL;
7887
7888       if (cursor_path)
7889         {
7890           if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7891               tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7892             pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0);
7893           else
7894             pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0);
7895         }
7896     }
7897
7898   if (cursor_path)
7899     {
7900       /* Now find a column for the cursor. */
7901       PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7902
7903       pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7904       gtk_tree_path_free (cursor_path);
7905
7906       if (tree_view->priv->focus_column == NULL)
7907         {
7908           GList *list;
7909           for (list = tree_view->priv->columns; list; list = list->next)
7910             {
7911               if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7912                 {
7913                   tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7914                   pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7915                   pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7916                   break;
7917                 }
7918             }
7919
7920         }
7921     }
7922 }
7923
7924 static gboolean
7925 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7926                                    gint         count,
7927                                    PsppSheetSelectMode mode)
7928 {
7929   gint selection_count;
7930   int cursor_node = -1;
7931   int new_cursor_node = -1;
7932   GtkTreePath *cursor_path = NULL;
7933   gboolean grab_focus = TRUE;
7934
7935   if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7936     return FALSE;
7937
7938   cursor_path = NULL;
7939   if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7940     /* FIXME: we lost the cursor; should we get the first? */
7941     return FALSE;
7942
7943   cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7944   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7945
7946   if (cursor_node < 0)
7947     /* FIXME: we lost the cursor; should we get the first? */
7948     return FALSE;
7949
7950   selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7951
7952   if (selection_count == 0
7953       && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7954       && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
7955     {
7956       /* Don't move the cursor, but just select the current node */
7957       new_cursor_node = cursor_node;
7958     }
7959   else
7960     {
7961       if (count == -1)
7962         new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7963       else
7964         new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7965     }
7966
7967   gtk_tree_path_free (cursor_path);
7968
7969   if (new_cursor_node)
7970     {
7971       cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7972
7973       search_first_focusable_path (tree_view, &cursor_path,
7974                                    (count != -1),
7975                                    &new_cursor_node);
7976
7977       if (cursor_path)
7978         gtk_tree_path_free (cursor_path);
7979     }
7980
7981   /*
7982    * If the list has only one item and multi-selection is set then select
7983    * the row (if not yet selected).
7984    */
7985   if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7986        tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7987       new_cursor_node < 0)
7988     {
7989       if (count == -1)
7990         new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7991       else
7992         new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7993
7994       if (new_cursor_node < 0
7995           && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
7996         {
7997           new_cursor_node = cursor_node;
7998         }
7999       else
8000         {
8001           new_cursor_node = -1;
8002         }
8003     }
8004
8005   if (new_cursor_node >= 0)
8006     {
8007       cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
8008       pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode);
8009       gtk_tree_path_free (cursor_path);
8010     }
8011   else
8012     {
8013       pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8014
8015       if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8016         {
8017           if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
8018                                           count < 0 ?
8019                                           GTK_DIR_UP : GTK_DIR_DOWN))
8020             {
8021               GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8022
8023               if (toplevel)
8024                 gtk_widget_child_focus (toplevel,
8025                                         count < 0 ?
8026                                         GTK_DIR_TAB_BACKWARD :
8027                                         GTK_DIR_TAB_FORWARD);
8028
8029               grab_focus = FALSE;
8030             }
8031         }
8032       else
8033         {
8034           gtk_widget_error_bell (GTK_WIDGET (tree_view));
8035         }
8036     }
8037
8038   if (grab_focus)
8039     gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8040
8041   return new_cursor_node >= 0;
8042 }
8043
8044 static void
8045 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
8046                                           gint         count,
8047                                           PsppSheetSelectMode mode)
8048 {
8049   int cursor_node = -1;
8050   GtkTreePath *old_cursor_path = NULL;
8051   GtkTreePath *cursor_path = NULL;
8052   int start_cursor_node = -1;
8053   gint y;
8054   gint window_y;
8055   gint vertical_separator;
8056
8057   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8058     return;
8059
8060   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8061     old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8062   else
8063     /* This is sorta weird.  Focus in should give us a cursor */
8064     return;
8065
8066   gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
8067   _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
8068
8069   if (cursor_node < 0)
8070     {
8071       /* FIXME: we lost the cursor.  Should we try to get one? */
8072       gtk_tree_path_free (old_cursor_path);
8073       return;
8074     }
8075
8076   y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8077   window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
8078   y += tree_view->priv->cursor_offset;
8079   y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment);
8080   y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment),  (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator);
8081
8082   if (y >= tree_view->priv->height)
8083     y = tree_view->priv->height - 1;
8084
8085   tree_view->priv->cursor_offset =
8086     pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
8087
8088   if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
8089     {
8090       cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
8091       tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
8092     }
8093
8094   y -= tree_view->priv->cursor_offset;
8095   cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8096
8097   start_cursor_node = cursor_node;
8098
8099   if (! search_first_focusable_path (tree_view, &cursor_path,
8100                                      (count != -1),
8101                                      &cursor_node))
8102     {
8103       /* It looks like we reached the end of the view without finding
8104        * a focusable row.  We will step backwards to find the last
8105        * focusable row.
8106        */
8107       cursor_node = start_cursor_node;
8108       cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8109
8110       search_first_focusable_path (tree_view, &cursor_path,
8111                                    (count == -1),
8112                                    &cursor_node);
8113     }
8114
8115   if (!cursor_path)
8116     goto cleanup;
8117
8118   /* update y */
8119   y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8120
8121   pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode);
8122
8123   y -= window_y;
8124   pspp_sheet_view_scroll_to_point (tree_view, -1, y);
8125   pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8126   _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8127
8128   if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
8129     gtk_widget_error_bell (GTK_WIDGET (tree_view));
8130
8131   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8132
8133 cleanup:
8134   gtk_tree_path_free (old_cursor_path);
8135   gtk_tree_path_free (cursor_path);
8136 }
8137
8138 static void
8139 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8140                                         gint         count,
8141                                         PsppSheetSelectMode mode)
8142 {
8143   int cursor_node = -1;
8144   GtkTreePath *cursor_path = NULL;
8145   PsppSheetViewColumn *column;
8146   GtkTreeIter iter;
8147   GList *list;
8148   gboolean found_column = FALSE;
8149   gboolean rtl;
8150
8151   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8152
8153   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8154     return;
8155
8156   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8157     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8158   else
8159     return;
8160
8161   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8162   if (cursor_node < 0)
8163     return;
8164   if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8165     {
8166       gtk_tree_path_free (cursor_path);
8167       return;
8168     }
8169   gtk_tree_path_free (cursor_path);
8170
8171   list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8172   if (tree_view->priv->focus_column)
8173     {
8174       for (; list; list = (rtl ? list->prev : list->next))
8175         {
8176           if (list->data == tree_view->priv->focus_column)
8177             break;
8178         }
8179     }
8180
8181   while (list)
8182     {
8183       gboolean left, right;
8184
8185       column = list->data;
8186       if (column->visible == FALSE || column->row_head)
8187         goto loop_end;
8188
8189       pspp_sheet_view_column_cell_set_cell_data (column,
8190                                                tree_view->priv->model,
8191                                                &iter);
8192
8193       if (rtl)
8194         {
8195           right = list->prev ? TRUE : FALSE;
8196           left = list->next ? TRUE : FALSE;
8197         }
8198       else
8199         {
8200           left = list->prev ? TRUE : FALSE;
8201           right = list->next ? TRUE : FALSE;
8202         }
8203
8204       if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8205         {
8206           tree_view->priv->focus_column = column;
8207           found_column = TRUE;
8208           break;
8209         }
8210     loop_end:
8211       if (count == 1)
8212         list = rtl ? list->prev : list->next;
8213       else
8214         list = rtl ? list->next : list->prev;
8215     }
8216
8217   if (found_column)
8218     {
8219       _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8220       g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8221       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8222     }
8223   else
8224     {
8225       gtk_widget_error_bell (GTK_WIDGET (tree_view));
8226     }
8227
8228   pspp_sheet_view_clamp_column_visible (tree_view,
8229                                       tree_view->priv->focus_column, TRUE);
8230 }
8231
8232 static void
8233 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8234                                             gint         count,
8235                                             PsppSheetSelectMode mode)
8236 {
8237   int cursor_node = -1;
8238   GtkTreePath *cursor_path = NULL;
8239   PsppSheetViewColumn *column;
8240   PsppSheetViewColumn *found_column;
8241   GtkTreeIter iter;
8242   GList *list;
8243   gboolean rtl;
8244
8245   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8246
8247   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8248     return;
8249
8250   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8251     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8252   else
8253     return;
8254
8255   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8256   if (cursor_node < 0)
8257     return;
8258   if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8259     {
8260       gtk_tree_path_free (cursor_path);
8261       return;
8262     }
8263   gtk_tree_path_free (cursor_path);
8264
8265   list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8266   if (tree_view->priv->focus_column)
8267     {
8268       for (; list; list = (rtl ? list->prev : list->next))
8269         {
8270           if (list->data == tree_view->priv->focus_column)
8271             break;
8272         }
8273     }
8274
8275   found_column = NULL;
8276   while (list)
8277     {
8278       gboolean left, right;
8279
8280       column = list->data;
8281       if (column->visible == FALSE || column->row_head)
8282         goto loop_end;
8283
8284       pspp_sheet_view_column_cell_set_cell_data (column,
8285                                                tree_view->priv->model,
8286                                                &iter);
8287
8288       if (rtl)
8289         {
8290           right = list->prev ? TRUE : FALSE;
8291           left = list->next ? TRUE : FALSE;
8292         }
8293       else
8294         {
8295           left = list->prev ? TRUE : FALSE;
8296           right = list->next ? TRUE : FALSE;
8297         }
8298
8299       if (column->tabbable
8300           && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8301         found_column = column;
8302
8303     loop_end:
8304       if (count == 1)
8305         list = rtl ? list->prev : list->next;
8306       else
8307         list = rtl ? list->next : list->prev;
8308     }
8309
8310   if (found_column)
8311     {
8312       tree_view->priv->focus_column = found_column;
8313       _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8314       g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8315       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8316     }
8317
8318   pspp_sheet_view_clamp_column_visible (tree_view,
8319                                       tree_view->priv->focus_column, TRUE);
8320 }
8321
8322 static gboolean
8323 try_move_cursor_tab (PsppSheetView *tree_view,
8324                      gboolean start_at_focus_column,
8325                      gint count)
8326 {
8327   PsppSheetViewColumn *column;
8328   GtkTreeIter iter;
8329   int cursor_node = -1;
8330   GtkTreePath *cursor_path = NULL;
8331   gboolean rtl;
8332   GList *list;
8333
8334   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8335     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8336   else
8337     return TRUE;
8338
8339   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8340   if (cursor_node < 0)
8341     return TRUE;
8342   if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8343     {
8344       gtk_tree_path_free (cursor_path);
8345       return TRUE;
8346     }
8347   gtk_tree_path_free (cursor_path);
8348
8349   rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8350   if (start_at_focus_column)
8351     {
8352       list = (rtl
8353               ? g_list_last (tree_view->priv->columns)
8354               : g_list_first (tree_view->priv->columns));
8355       if (tree_view->priv->focus_column)
8356         {
8357           for (; list; list = (rtl ? list->prev : list->next))
8358             {
8359               if (list->data == tree_view->priv->focus_column)
8360                 break;
8361             }
8362         }
8363     }
8364   else
8365     {
8366       list = (rtl ^ (count == 1)
8367               ? g_list_first (tree_view->priv->columns)
8368               : g_list_last (tree_view->priv->columns));
8369     }
8370
8371   while (list)
8372     {
8373       gboolean left, right;
8374
8375       column = list->data;
8376       if (column->visible == FALSE || !column->tabbable)
8377         goto loop_end;
8378
8379       pspp_sheet_view_column_cell_set_cell_data (column,
8380                                                  tree_view->priv->model,
8381                                                  &iter);
8382
8383       if (rtl)
8384         {
8385           right = list->prev ? TRUE : FALSE;
8386           left = list->next ? TRUE : FALSE;
8387         }
8388       else
8389         {
8390           left = list->prev ? TRUE : FALSE;
8391           right = list->next ? TRUE : FALSE;
8392         }
8393
8394       if (column->tabbable
8395           && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8396         {
8397           tree_view->priv->focus_column = column;
8398           _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8399           g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8400           gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8401           return TRUE;
8402         }
8403     loop_end:
8404       if (count == 1)
8405         list = rtl ? list->prev : list->next;
8406       else
8407         list = rtl ? list->next : list->prev;
8408     }
8409
8410   return FALSE;
8411 }
8412
8413 static void
8414 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8415                                  gint         count)
8416 {
8417   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8418     return;
8419
8420   if (!try_move_cursor_tab (tree_view, TRUE, count))
8421     {
8422       if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0)
8423           && !try_move_cursor_tab (tree_view, FALSE, count))
8424         gtk_widget_error_bell (GTK_WIDGET (tree_view));
8425     }
8426
8427   pspp_sheet_view_clamp_column_visible (tree_view,
8428                                         tree_view->priv->focus_column, TRUE);
8429 }
8430
8431 static void
8432 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8433                                        gint         count,
8434                                        PsppSheetSelectMode mode)
8435 {
8436   int cursor_node;
8437   GtkTreePath *path;
8438   GtkTreePath *old_path;
8439
8440   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8441     return;
8442
8443   g_return_if_fail (tree_view->priv->row_count > 0);
8444
8445   pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8446
8447   if (count == -1)
8448     {
8449       /* Now go forward to find the first focusable row. */
8450       path = _pspp_sheet_view_find_path (tree_view, 0);
8451       search_first_focusable_path (tree_view, &path,
8452                                    TRUE, &cursor_node);
8453     }
8454   else
8455     {
8456       /* Now go backwards to find last focusable row. */
8457       path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8458       search_first_focusable_path (tree_view, &path,
8459                                    FALSE, &cursor_node);
8460     }
8461
8462   if (!path)
8463     goto cleanup;
8464
8465   if (gtk_tree_path_compare (old_path, path))
8466     {
8467       pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
8468       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8469     }
8470   else
8471     {
8472       gtk_widget_error_bell (GTK_WIDGET (tree_view));
8473     }
8474
8475 cleanup:
8476   gtk_tree_path_free (old_path);
8477   gtk_tree_path_free (path);
8478 }
8479
8480 static gboolean
8481 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8482 {
8483   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8484     return FALSE;
8485
8486   if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8487       tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8488     return FALSE;
8489
8490   pspp_sheet_selection_select_all (tree_view->priv->selection);
8491
8492   return TRUE;
8493 }
8494
8495 static gboolean
8496 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8497 {
8498   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8499     return FALSE;
8500
8501   if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8502       tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8503     return FALSE;
8504
8505   pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8506
8507   return TRUE;
8508 }
8509
8510 static gboolean
8511 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8512                                         gboolean     start_editing,
8513                                         PsppSheetSelectMode mode)
8514 {
8515   int new_node = -1;
8516   int cursor_node = -1;
8517   GtkTreePath *cursor_path = NULL;
8518
8519   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8520     return FALSE;
8521
8522   if (tree_view->priv->cursor)
8523     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8524
8525   if (cursor_path == NULL)
8526     return FALSE;
8527
8528   _pspp_sheet_view_find_node (tree_view, cursor_path,
8529                               &cursor_node);
8530
8531   if (cursor_node < 0)
8532     {
8533       gtk_tree_path_free (cursor_path);
8534       return FALSE;
8535     }
8536
8537   if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing &&
8538       tree_view->priv->focus_column)
8539     {
8540       if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8541         {
8542           gtk_tree_path_free (cursor_path);
8543           return TRUE;
8544         }
8545     }
8546
8547   _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8548                                             cursor_node,
8549                                             cursor_path,
8550                                             mode,
8551                                             FALSE);
8552
8553   /* We bail out if the original (tree, node) don't exist anymore after
8554    * handling the selection-changed callback.  We do return TRUE because
8555    * the key press has been handled at this point.
8556    */
8557   _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8558
8559   if (cursor_node != new_node)
8560     return FALSE;
8561
8562   pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8563
8564   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8565   _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8566
8567   if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8568     pspp_sheet_view_row_activated (tree_view, cursor_path,
8569                                  tree_view->priv->focus_column);
8570     
8571   gtk_tree_path_free (cursor_path);
8572
8573   return TRUE;
8574 }
8575
8576 static gboolean
8577 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8578 {
8579   int new_node = -1;
8580   int cursor_node = -1;
8581   GtkTreePath *cursor_path = NULL;
8582
8583   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8584     return FALSE;
8585
8586   cursor_path = NULL;
8587   if (tree_view->priv->cursor)
8588     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8589
8590   if (cursor_path == NULL)
8591     return FALSE;
8592
8593   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8594   if (cursor_node < 0)
8595     {
8596       gtk_tree_path_free (cursor_path);
8597       return FALSE;
8598     }
8599
8600   _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8601                                             cursor_node,
8602                                             cursor_path,
8603                                             PSPP_SHEET_SELECT_MODE_TOGGLE,
8604                                             FALSE);
8605
8606   /* We bail out if the original (tree, node) don't exist anymore after
8607    * handling the selection-changed callback.  We do return TRUE because
8608    * the key press has been handled at this point.
8609    */
8610   _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8611
8612   if (cursor_node != new_node)
8613     return FALSE;
8614
8615   pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8616
8617   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8618   pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8619   gtk_tree_path_free (cursor_path);
8620
8621   return TRUE;
8622 }
8623
8624 static gboolean
8625 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8626 {
8627   pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8628   tree_view->priv->typeselect_flush_timeout = 0;
8629
8630   return FALSE;
8631 }
8632
8633 /* Cut and paste from gtkwindow.c */
8634 static void
8635 send_focus_change (GtkWidget *widget,
8636                    gboolean   in)
8637 {
8638   GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8639
8640   fevent->focus_change.type = GDK_FOCUS_CHANGE;
8641   fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
8642   fevent->focus_change.in = in;
8643   
8644   gtk_widget_send_focus_change (widget, fevent);
8645   gdk_event_free (fevent);
8646 }
8647
8648 static void
8649 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8650 {
8651   GtkWidget *frame, *vbox, *toplevel;
8652   GdkScreen *screen;
8653
8654   if (tree_view->priv->search_custom_entry_set)
8655     return;
8656
8657   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8658   screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8659
8660    if (tree_view->priv->search_window != NULL)
8661      {
8662        if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8663          gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8664                                       GTK_WINDOW (tree_view->priv->search_window));
8665        else if (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)))
8666          gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
8667                                          GTK_WINDOW (tree_view->priv->search_window));
8668        gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8669        return;
8670      }
8671    
8672   tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8673   gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8674
8675   if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8676     gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8677                                  GTK_WINDOW (tree_view->priv->search_window));
8678
8679   gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8680                             GDK_WINDOW_TYPE_HINT_UTILITY);
8681   gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8682   g_signal_connect (tree_view->priv->search_window, "delete-event",
8683                     G_CALLBACK (pspp_sheet_view_search_delete_event),
8684                     tree_view);
8685   g_signal_connect (tree_view->priv->search_window, "key-press-event",
8686                     G_CALLBACK (pspp_sheet_view_search_key_press_event),
8687                     tree_view);
8688   g_signal_connect (tree_view->priv->search_window, "button-press-event",
8689                     G_CALLBACK (pspp_sheet_view_search_button_press_event),
8690                     tree_view);
8691   g_signal_connect (tree_view->priv->search_window, "scroll-event",
8692                     G_CALLBACK (pspp_sheet_view_search_scroll_event),
8693                     tree_view);
8694
8695   frame = gtk_frame_new (NULL);
8696   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8697   gtk_widget_show (frame);
8698   gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8699
8700   vbox = gtk_vbox_new (FALSE, 0);
8701   gtk_widget_show (vbox);
8702   gtk_container_add (GTK_CONTAINER (frame), vbox);
8703   gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8704
8705   /* add entry */
8706   tree_view->priv->search_entry = gtk_entry_new ();
8707   gtk_widget_show (tree_view->priv->search_entry);
8708   g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8709                     G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8710                     tree_view);
8711   g_signal_connect (tree_view->priv->search_entry,
8712                     "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8713                     tree_view);
8714
8715 #if GTK3_TRANSITION
8716   g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8717                     "preedit-changed",
8718                     G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8719                     tree_view);
8720 #endif
8721
8722   gtk_container_add (GTK_CONTAINER (vbox),
8723                      tree_view->priv->search_entry);
8724
8725   gtk_widget_realize (tree_view->priv->search_entry);
8726 }
8727
8728 /* Pops up the interactive search entry.  If keybinding is TRUE then the user
8729  * started this by typing the start_interactive_search keybinding.  Otherwise, it came from 
8730  */
8731 static gboolean
8732 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8733                                              gboolean     keybinding)
8734 {
8735   /* We only start interactive search if we have focus or the columns
8736    * have focus.  If one of our children have focus, we don't want to
8737    * start the search.
8738    */
8739   GList *list;
8740   gboolean found_focus = FALSE;
8741   GtkWidgetClass *entry_parent_class;
8742   
8743   if (!tree_view->priv->enable_search && !keybinding)
8744     return FALSE;
8745
8746   if (tree_view->priv->search_custom_entry_set)
8747     return FALSE;
8748
8749   if (tree_view->priv->search_window != NULL &&
8750       gtk_widget_get_visible (tree_view->priv->search_window))
8751     return TRUE;
8752
8753   for (list = tree_view->priv->columns; list; list = list->next)
8754     {
8755       PsppSheetViewColumn *column;
8756
8757       column = list->data;
8758       if (! column->visible)
8759         continue;
8760
8761       if (column->button && gtk_widget_has_focus (column->button))
8762         {
8763           found_focus = TRUE;
8764           break;
8765         }
8766     }
8767   
8768   if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8769     found_focus = TRUE;
8770
8771   if (!found_focus)
8772     return FALSE;
8773
8774   if (tree_view->priv->search_column < 0)
8775     return FALSE;
8776
8777   pspp_sheet_view_ensure_interactive_directory (tree_view);
8778
8779   if (keybinding)
8780     gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8781
8782   /* done, show it */
8783   tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8784   gtk_widget_show (tree_view->priv->search_window);
8785   if (tree_view->priv->search_entry_changed_id == 0)
8786     {
8787       tree_view->priv->search_entry_changed_id =
8788         g_signal_connect (tree_view->priv->search_entry, "changed",
8789                           G_CALLBACK (pspp_sheet_view_search_init),
8790                           tree_view);
8791     }
8792
8793   tree_view->priv->typeselect_flush_timeout =
8794     gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8795                    (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8796                    tree_view);
8797
8798   /* Grab focus will select all the text.  We don't want that to happen, so we
8799    * call the parent instance and bypass the selection change.  This is probably
8800    * really non-kosher. */
8801   entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8802   (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8803
8804   /* send focus-in event */
8805   send_focus_change (tree_view->priv->search_entry, TRUE);
8806
8807   /* search first matching iter */
8808   pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8809
8810   return TRUE;
8811 }
8812
8813 static gboolean
8814 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8815 {
8816   return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8817 }
8818
8819 /* this function returns the new width of the column being resized given
8820  * the column and x position of the cursor; the x cursor position is passed
8821  * in as a pointer and automagicly corrected if it's beyond min/max limits
8822  */
8823 static gint
8824 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8825                                 gint       i,
8826                                 gint      *x)
8827 {
8828   PsppSheetViewColumn *column;
8829   gint width;
8830   gboolean rtl;
8831
8832   /* first translate the x position from gtk_widget_get_window (widget)
8833    * to clist->clist_window
8834    */
8835   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8836   column = g_list_nth (tree_view->priv->columns, i)->data;
8837   width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8838  
8839   /* Clamp down the value */
8840   if (column->min_width == -1)
8841     width = MAX (column->button_request, width);
8842   else
8843     width = MAX (column->min_width, width);
8844   if (column->max_width != -1)
8845     width = MIN (width, column->max_width);
8846
8847   *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8848  
8849   return width;
8850 }
8851
8852
8853 /* FIXME this adjust_allocation is a big cut-and-paste from
8854  * GtkCList, needs to be some "official" way to do this
8855  * factored out.
8856  */
8857 typedef struct
8858 {
8859   GdkWindow *window;
8860   int dx;
8861   int dy;
8862 } ScrollData;
8863
8864 /* The window to which gtk_widget_get_window (widget) is relative */
8865 #define ALLOCATION_WINDOW(widget)               \
8866    (!gtk_widget_get_has_window (widget) ?               \
8867     gtk_widget_get_window (widget) :                          \
8868     gdk_window_get_parent (gtk_widget_get_window (widget)))
8869
8870 static void
8871 adjust_allocation_recurse (GtkWidget *widget,
8872                            gpointer   data)
8873 {
8874   ScrollData *scroll_data = data;
8875   GtkAllocation allocation;
8876   gtk_widget_get_allocation (widget, &allocation);
8877   /* Need to really size allocate instead of just poking
8878    * into widget->allocation if the widget is not realized.
8879    * FIXME someone figure out why this was.
8880    */
8881   if (!gtk_widget_get_realized (widget))
8882     {
8883       if (gtk_widget_get_visible (widget))
8884         {
8885           GdkRectangle tmp_rectangle = allocation;
8886           tmp_rectangle.x += scroll_data->dx;
8887           tmp_rectangle.y += scroll_data->dy;
8888           
8889           gtk_widget_size_allocate (widget, &tmp_rectangle);
8890         }
8891     }
8892   else
8893     {
8894       if (ALLOCATION_WINDOW (widget) == scroll_data->window)
8895         {
8896           allocation.x += scroll_data->dx;
8897           allocation.y += scroll_data->dy;
8898           
8899           if (GTK_IS_CONTAINER (widget))
8900             gtk_container_forall (GTK_CONTAINER (widget),
8901                                   adjust_allocation_recurse,
8902                                   data);
8903         }
8904     }
8905 }
8906
8907 static void
8908 adjust_allocation (GtkWidget *widget,
8909                    int        dx,
8910                    int        dy)
8911 {
8912   ScrollData scroll_data;
8913
8914   if (gtk_widget_get_realized (widget))
8915     scroll_data.window = ALLOCATION_WINDOW (widget);
8916   else
8917     scroll_data.window = NULL;
8918     
8919   scroll_data.dx = dx;
8920   scroll_data.dy = dy;
8921   
8922   adjust_allocation_recurse (widget, &scroll_data);
8923 }
8924
8925 void 
8926 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8927
8928 /* Callbacks */
8929 static void
8930 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8931                                   PsppSheetView   *tree_view)
8932 {
8933   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8934     {
8935       GList *list;
8936       gint dy;
8937         
8938       gdk_window_move (tree_view->priv->bin_window,
8939                        - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8940                        TREE_VIEW_HEADER_HEIGHT (tree_view));
8941       gdk_window_move (tree_view->priv->header_window,
8942                        - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8943                        0);
8944       dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8945       if (dy)
8946         {
8947           update_prelight (tree_view,
8948                            tree_view->priv->event_last_x,
8949                            tree_view->priv->event_last_y - dy);
8950
8951           if (tree_view->priv->edited_column &&
8952               GTK_IS_WIDGET (tree_view->priv->edited_column->editable_widget))
8953             {
8954               GList *list;
8955               GtkWidget *widget;
8956               PsppSheetViewChild *child = NULL;
8957
8958               widget = GTK_WIDGET (tree_view->priv->edited_column->editable_widget);
8959               adjust_allocation (widget, 0, dy); 
8960               
8961               for (list = tree_view->priv->children; list; list = list->next)
8962                 {
8963                   child = (PsppSheetViewChild *)list->data;
8964                   if (child->widget == widget)
8965                     {
8966                       child->y += dy;
8967                       break;
8968                     }
8969                 }
8970             }
8971         }
8972       gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8973
8974       if (tree_view->priv->dy != (int) gtk_adjustment_get_value (tree_view->priv->vadjustment))
8975         {
8976           /* update our dy and top_row */
8977           tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8978
8979           if (!tree_view->priv->in_top_row_to_dy)
8980             pspp_sheet_view_dy_to_top_row (tree_view);
8981         }
8982     }
8983 }
8984
8985 \f
8986
8987 /* Public methods
8988  */
8989
8990 /**
8991  * pspp_sheet_view_new:
8992  *
8993  * Creates a new #PsppSheetView widget.
8994  *
8995  * Return value: A newly created #PsppSheetView widget.
8996  **/
8997 GtkWidget *
8998 pspp_sheet_view_new (void)
8999 {
9000   return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
9001 }
9002
9003 /**
9004  * pspp_sheet_view_new_with_model:
9005  * @model: the model.
9006  *
9007  * Creates a new #PsppSheetView widget with the model initialized to @model.
9008  *
9009  * Return value: A newly created #PsppSheetView widget.
9010  **/
9011 GtkWidget *
9012 pspp_sheet_view_new_with_model (GtkTreeModel *model)
9013 {
9014   return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
9015 }
9016
9017 /* Public Accessors
9018  */
9019
9020 /**
9021  * pspp_sheet_view_get_model:
9022  * @tree_view: a #PsppSheetView
9023  *
9024  * Returns the model the #PsppSheetView is based on.  Returns %NULL if the
9025  * model is unset.
9026  *
9027  * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
9028  **/
9029 GtkTreeModel *
9030 pspp_sheet_view_get_model (PsppSheetView *tree_view)
9031 {
9032   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9033
9034   return tree_view->priv->model;
9035 }
9036
9037 /**
9038  * pspp_sheet_view_set_model:
9039  * @tree_view: A #GtkTreeNode.
9040  * @model: (allow-none): The model.
9041  *
9042  * Sets the model for a #PsppSheetView.  If the @tree_view already has a model
9043  * set, it will remove it before setting the new model.  If @model is %NULL,
9044  * then it will unset the old model.
9045  **/
9046 void
9047 pspp_sheet_view_set_model (PsppSheetView  *tree_view,
9048                          GtkTreeModel *model)
9049 {
9050   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9051   g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
9052
9053   if (model == tree_view->priv->model)
9054     return;
9055
9056   if (tree_view->priv->scroll_to_path)
9057     {
9058       gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9059       tree_view->priv->scroll_to_path = NULL;
9060     }
9061
9062   if (tree_view->priv->model)
9063     {
9064       GList *tmplist = tree_view->priv->columns;
9065
9066       if (tree_view->priv->selected)
9067         range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
9068       pspp_sheet_view_stop_editing (tree_view, TRUE);
9069
9070       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9071                                             pspp_sheet_view_row_changed,
9072                                             tree_view);
9073       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9074                                             pspp_sheet_view_row_inserted,
9075                                             tree_view);
9076       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9077                                             pspp_sheet_view_row_deleted,
9078                                             tree_view);
9079       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9080                                             pspp_sheet_view_rows_reordered,
9081                                             tree_view);
9082
9083       for (; tmplist; tmplist = tmplist->next)
9084         _pspp_sheet_view_column_unset_model (tmplist->data,
9085                                            tree_view->priv->model);
9086
9087       tree_view->priv->prelight_node = -1;
9088
9089       gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
9090       tree_view->priv->drag_dest_row = NULL;
9091       gtk_tree_row_reference_free (tree_view->priv->cursor);
9092       tree_view->priv->cursor = NULL;
9093       gtk_tree_row_reference_free (tree_view->priv->anchor);
9094       tree_view->priv->anchor = NULL;
9095       gtk_tree_row_reference_free (tree_view->priv->top_row);
9096       tree_view->priv->top_row = NULL;
9097       gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9098       tree_view->priv->scroll_to_path = NULL;
9099
9100       tree_view->priv->scroll_to_column = NULL;
9101
9102       g_object_unref (tree_view->priv->model);
9103
9104       tree_view->priv->search_column = -1;
9105       tree_view->priv->fixed_height = -1;
9106       tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
9107       tree_view->priv->last_button_x = -1;
9108       tree_view->priv->last_button_y = -1;
9109     }
9110
9111   tree_view->priv->model = model;
9112
9113   if (tree_view->priv->model)
9114     {
9115       gint i;
9116
9117       if (tree_view->priv->search_column == -1)
9118         {
9119           for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
9120             {
9121               GType type = gtk_tree_model_get_column_type (model, i);
9122
9123               if (g_value_type_transformable (type, G_TYPE_STRING))
9124                 {
9125                   tree_view->priv->search_column = i;
9126                   break;
9127                 }
9128             }
9129         }
9130
9131       g_object_ref (tree_view->priv->model);
9132       g_signal_connect (tree_view->priv->model,
9133                         "row-changed",
9134                         G_CALLBACK (pspp_sheet_view_row_changed),
9135                         tree_view);
9136       g_signal_connect (tree_view->priv->model,
9137                         "row-inserted",
9138                         G_CALLBACK (pspp_sheet_view_row_inserted),
9139                         tree_view);
9140       g_signal_connect (tree_view->priv->model,
9141                         "row-deleted",
9142                         G_CALLBACK (pspp_sheet_view_row_deleted),
9143                         tree_view);
9144       g_signal_connect (tree_view->priv->model,
9145                         "rows-reordered",
9146                         G_CALLBACK (pspp_sheet_view_rows_reordered),
9147                         tree_view);
9148
9149       tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
9150
9151       /*  FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
9152       install_presize_handler (tree_view);
9153     }
9154
9155   g_object_notify (G_OBJECT (tree_view), "model");
9156
9157   if (tree_view->priv->selection)
9158     _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
9159
9160   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9161     gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9162 }
9163
9164 /**
9165  * pspp_sheet_view_get_selection:
9166  * @tree_view: A #PsppSheetView.
9167  *
9168  * Gets the #PsppSheetSelection associated with @tree_view.
9169  *
9170  * Return value: A #PsppSheetSelection object.
9171  **/
9172 PsppSheetSelection *
9173 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
9174 {
9175   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9176
9177   return tree_view->priv->selection;
9178 }
9179
9180 /**
9181  * pspp_sheet_view_get_hadjustment:
9182  * @tree_view: A #PsppSheetView
9183  *
9184  * Gets the #GtkAdjustment currently being used for the horizontal aspect.
9185  *
9186  * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9187  * used.
9188  **/
9189 GtkAdjustment *
9190 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
9191 {
9192   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9193
9194   return pspp_sheet_view_do_get_hadjustment (tree_view);
9195 }
9196
9197 static GtkAdjustment *
9198 pspp_sheet_view_do_get_hadjustment (PsppSheetView *tree_view)
9199 {
9200   return tree_view->priv->hadjustment;
9201 }
9202
9203 /**
9204  * pspp_sheet_view_set_hadjustment:
9205  * @tree_view: A #PsppSheetView
9206  * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9207  *
9208  * Sets the #GtkAdjustment for the current horizontal aspect.
9209  **/
9210 void
9211 pspp_sheet_view_set_hadjustment (PsppSheetView   *tree_view,
9212                                GtkAdjustment *adjustment)
9213 {
9214   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9215
9216   pspp_sheet_view_set_adjustments (tree_view,
9217                                  adjustment,
9218                                  tree_view->priv->vadjustment);
9219
9220   g_object_notify (G_OBJECT (tree_view), "hadjustment");
9221 }
9222
9223 static void
9224 pspp_sheet_view_do_set_hadjustment (PsppSheetView   *tree_view,
9225                                   GtkAdjustment *adjustment)
9226 {
9227   PsppSheetViewPrivate *priv = tree_view->priv;
9228
9229   if (adjustment && priv->hadjustment == adjustment)
9230     return;
9231
9232   if (priv->hadjustment != NULL)
9233     {
9234       g_signal_handlers_disconnect_by_func (priv->hadjustment,
9235                                             pspp_sheet_view_adjustment_changed,
9236                                             tree_view);
9237       g_object_unref (priv->hadjustment);
9238     }
9239
9240   if (adjustment == NULL)
9241     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9242                                      0.0, 0.0, 0.0);
9243
9244   g_signal_connect (adjustment, "value-changed",
9245                     G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9246   priv->hadjustment = g_object_ref_sink (adjustment);
9247   /* FIXME: Adjustment should probably be populated here with fresh values, but
9248    * internal details are too complicated for me to decipher right now.
9249    */
9250   pspp_sheet_view_adjustment_changed (NULL, tree_view);
9251
9252   g_object_notify (G_OBJECT (tree_view), "hadjustment");
9253 }
9254
9255 /**
9256  * pspp_sheet_view_get_vadjustment:
9257  * @tree_view: A #PsppSheetView
9258  *
9259  * Gets the #GtkAdjustment currently being used for the vertical aspect.
9260  *
9261  * Return value: (transfer none): A #GtkAdjustment object, or %NULL
9262  *     if none is currently being used.
9263  *
9264  * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
9265  **/
9266 GtkAdjustment *
9267 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9268 {
9269   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9270
9271   return pspp_sheet_view_do_get_vadjustment (tree_view);
9272 }
9273
9274 static GtkAdjustment *
9275 pspp_sheet_view_do_get_vadjustment (PsppSheetView *tree_view)
9276 {
9277   return tree_view->priv->vadjustment;
9278 }
9279
9280 /**
9281  * pspp_sheet_view_set_vadjustment:
9282  * @tree_view: A #PsppSheetView
9283  * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9284  *
9285  * Sets the #GtkAdjustment for the current vertical aspect.
9286  *
9287  * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
9288  **/
9289 void
9290 pspp_sheet_view_set_vadjustment (PsppSheetView   *tree_view,
9291                                GtkAdjustment *adjustment)
9292 {
9293   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9294   g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
9295
9296   pspp_sheet_view_do_set_vadjustment (tree_view, adjustment);
9297 }
9298
9299 static void
9300 pspp_sheet_view_do_set_vadjustment (PsppSheetView   *tree_view,
9301                                   GtkAdjustment *adjustment)
9302 {
9303   PsppSheetViewPrivate *priv = tree_view->priv;
9304
9305   if (adjustment && priv->vadjustment == adjustment)
9306     return;
9307
9308   if (priv->vadjustment != NULL)
9309     {
9310       g_signal_handlers_disconnect_by_func (priv->vadjustment,
9311                                             pspp_sheet_view_adjustment_changed,
9312                                             tree_view);
9313       g_object_unref (priv->vadjustment);
9314     }
9315
9316   if (adjustment == NULL)
9317     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9318                                      0.0, 0.0, 0.0);
9319
9320   g_signal_connect (adjustment, "value-changed",
9321                     G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9322   priv->vadjustment = g_object_ref_sink (adjustment);
9323   /* FIXME: Adjustment should probably be populated here with fresh values, but
9324    * internal details are too complicated for me to decipher right now.
9325    */
9326   pspp_sheet_view_adjustment_changed (NULL, tree_view);
9327   g_object_notify (G_OBJECT (tree_view), "vadjustment");
9328 }
9329
9330 /* Column and header operations */
9331
9332 /**
9333  * pspp_sheet_view_get_headers_visible:
9334  * @tree_view: A #PsppSheetView.
9335  *
9336  * Returns %TRUE if the headers on the @tree_view are visible.
9337  *
9338  * Return value: Whether the headers are visible or not.
9339  **/
9340 gboolean
9341 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9342 {
9343   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9344
9345   return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9346 }
9347
9348 /**
9349  * pspp_sheet_view_set_headers_visible:
9350  * @tree_view: A #PsppSheetView.
9351  * @headers_visible: %TRUE if the headers are visible
9352  *
9353  * Sets the visibility state of the headers.
9354  **/
9355 void
9356 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9357                                    gboolean     headers_visible)
9358 {
9359   gint x, y;
9360   GList *list;
9361   PsppSheetViewColumn *column;
9362   GtkAllocation allocation;
9363
9364   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9365
9366   gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9367
9368   headers_visible = !! headers_visible;
9369
9370   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9371     return;
9372
9373   if (headers_visible)
9374     PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9375   else
9376     PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9377
9378   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9379     {
9380       gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9381       if (headers_visible)
9382         {
9383           gdk_window_move_resize (tree_view->priv->bin_window, x, y  + TREE_VIEW_HEADER_HEIGHT (tree_view), 
9384                                   tree_view->priv->width, allocation.height -  + TREE_VIEW_HEADER_HEIGHT (tree_view));
9385
9386           if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9387             pspp_sheet_view_map_buttons (tree_view);
9388         }
9389       else
9390         {
9391           gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9392
9393           for (list = tree_view->priv->columns; list; list = list->next)
9394             {
9395               column = list->data;
9396               if (column->button)
9397                 gtk_widget_unmap (column->button);
9398             }
9399           gdk_window_hide (tree_view->priv->header_window);
9400         }
9401     }
9402
9403   gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view));
9404   gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2);
9405   gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
9406   gtk_adjustment_set_upper (tree_view->priv->vadjustment, tree_view->priv->height);
9407   gtk_adjustment_changed (tree_view->priv->vadjustment);
9408
9409   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9410
9411   g_object_notify (G_OBJECT (tree_view), "headers-visible");
9412 }
9413
9414 /**
9415  * pspp_sheet_view_columns_autosize:
9416  * @tree_view: A #PsppSheetView.
9417  *
9418  * Resizes all columns to their optimal width. Only works after the
9419  * treeview has been realized.
9420  **/
9421 void
9422 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9423 {
9424   gboolean dirty = FALSE;
9425   GList *list;
9426   PsppSheetViewColumn *column;
9427
9428   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9429
9430   for (list = tree_view->priv->columns; list; list = list->next)
9431     {
9432       column = list->data;
9433       _pspp_sheet_view_column_cell_set_dirty (column);
9434       dirty = TRUE;
9435     }
9436
9437   if (dirty)
9438     gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9439 }
9440
9441 /**
9442  * pspp_sheet_view_set_headers_clickable:
9443  * @tree_view: A #PsppSheetView.
9444  * @setting: %TRUE if the columns are clickable.
9445  *
9446  * Allow the column title buttons to be clicked.
9447  **/
9448 void
9449 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9450                                      gboolean   setting)
9451 {
9452   GList *list;
9453
9454   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9455
9456   for (list = tree_view->priv->columns; list; list = list->next)
9457     pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9458
9459   g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9460 }
9461
9462
9463 /**
9464  * pspp_sheet_view_get_headers_clickable:
9465  * @tree_view: A #PsppSheetView.
9466  *
9467  * Returns whether all header columns are clickable.
9468  *
9469  * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9470  *
9471  * Since: 2.10
9472  **/
9473 gboolean 
9474 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9475 {
9476   GList *list;
9477   
9478   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9479
9480   for (list = tree_view->priv->columns; list; list = list->next)
9481     if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9482       return FALSE;
9483
9484   return TRUE;
9485 }
9486
9487 /**
9488  * pspp_sheet_view_set_rules_hint
9489  * @tree_view: a #PsppSheetView
9490  * @setting: %TRUE if the tree requires reading across rows
9491  *
9492  * This function tells GTK+ that the user interface for your
9493  * application requires users to read across tree rows and associate
9494  * cells with one another. By default, GTK+ will then render the tree
9495  * with alternating row colors. Do <emphasis>not</emphasis> use it
9496  * just because you prefer the appearance of the ruled tree; that's a
9497  * question for the theme. Some themes will draw tree rows in
9498  * alternating colors even when rules are turned off, and users who
9499  * prefer that appearance all the time can choose those themes. You
9500  * should call this function only as a <emphasis>semantic</emphasis>
9501  * hint to the theme engine that your tree makes alternating colors
9502  * useful from a functional standpoint (since it has lots of columns,
9503  * generally).
9504  *
9505  **/
9506 void
9507 pspp_sheet_view_set_rules_hint (PsppSheetView  *tree_view,
9508                               gboolean      setting)
9509 {
9510   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9511
9512   setting = setting != FALSE;
9513
9514   if (tree_view->priv->has_rules != setting)
9515     {
9516       tree_view->priv->has_rules = setting;
9517       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9518     }
9519
9520   g_object_notify (G_OBJECT (tree_view), "rules-hint");
9521 }
9522
9523 /**
9524  * pspp_sheet_view_get_rules_hint
9525  * @tree_view: a #PsppSheetView
9526  *
9527  * Gets the setting set by pspp_sheet_view_set_rules_hint().
9528  *
9529  * Return value: %TRUE if rules are useful for the user of this tree
9530  **/
9531 gboolean
9532 pspp_sheet_view_get_rules_hint (PsppSheetView  *tree_view)
9533 {
9534   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9535
9536   return tree_view->priv->has_rules;
9537 }
9538
9539 /* Public Column functions
9540  */
9541
9542 /**
9543  * pspp_sheet_view_append_column:
9544  * @tree_view: A #PsppSheetView.
9545  * @column: The #PsppSheetViewColumn to add.
9546  *
9547  * Appends @column to the list of columns.
9548  *
9549  * Return value: The number of columns in @tree_view after appending.
9550  **/
9551 gint
9552 pspp_sheet_view_append_column (PsppSheetView       *tree_view,
9553                              PsppSheetViewColumn *column)
9554 {
9555   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9556   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9557   g_return_val_if_fail (column->tree_view == NULL, -1);
9558
9559   return pspp_sheet_view_insert_column (tree_view, column, -1);
9560 }
9561
9562
9563 /**
9564  * pspp_sheet_view_remove_column:
9565  * @tree_view: A #PsppSheetView.
9566  * @column: The #PsppSheetViewColumn to remove.
9567  *
9568  * Removes @column from @tree_view.
9569  *
9570  * Return value: The number of columns in @tree_view after removing.
9571  **/
9572 gint
9573 pspp_sheet_view_remove_column (PsppSheetView       *tree_view,
9574                              PsppSheetViewColumn *column)
9575 {
9576   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9577   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9578   g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9579
9580   if (tree_view->priv->focus_column == column)
9581     tree_view->priv->focus_column = NULL;
9582
9583   if (tree_view->priv->edited_column == column)
9584     {
9585       pspp_sheet_view_stop_editing (tree_view, TRUE);
9586
9587       /* no need to, but just to be sure ... */
9588       tree_view->priv->edited_column = NULL;
9589     }
9590
9591   _pspp_sheet_view_column_unset_tree_view (column);
9592
9593   tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9594   tree_view->priv->n_columns--;
9595
9596   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9597     {
9598       GList *list;
9599
9600       _pspp_sheet_view_column_unrealize_button (column);
9601       for (list = tree_view->priv->columns; list; list = list->next)
9602         {
9603           PsppSheetViewColumn *tmp_column;
9604
9605           tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9606           if (tmp_column->visible)
9607             _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9608         }
9609
9610       if (tree_view->priv->n_columns == 0 &&
9611           pspp_sheet_view_get_headers_visible (tree_view) && 
9612           tree_view->priv->header_window)
9613         gdk_window_hide (tree_view->priv->header_window);
9614
9615       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9616     }
9617
9618   g_object_unref (column);
9619   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9620
9621   return tree_view->priv->n_columns;
9622 }
9623
9624 /**
9625  * pspp_sheet_view_insert_column:
9626  * @tree_view: A #PsppSheetView.
9627  * @column: The #PsppSheetViewColumn to be inserted.
9628  * @position: The position to insert @column in.
9629  *
9630  * This inserts the @column into the @tree_view at @position.  If @position is
9631  * -1, then the column is inserted at the end.
9632  *
9633  * Return value: The number of columns in @tree_view after insertion.
9634  **/
9635 gint
9636 pspp_sheet_view_insert_column (PsppSheetView       *tree_view,
9637                              PsppSheetViewColumn *column,
9638                              gint               position)
9639 {
9640   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9641   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9642   g_return_val_if_fail (column->tree_view == NULL, -1);
9643
9644   g_object_ref_sink (column);
9645
9646   if (tree_view->priv->n_columns == 0 &&
9647       gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9648       pspp_sheet_view_get_headers_visible (tree_view))
9649     {
9650       gdk_window_show (tree_view->priv->header_window);
9651     }
9652
9653   tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9654                                             column, position);
9655   tree_view->priv->n_columns++;
9656
9657   _pspp_sheet_view_column_set_tree_view (column, tree_view);
9658
9659   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9660     {
9661       GList *list;
9662
9663       _pspp_sheet_view_column_realize_button (column);
9664
9665       for (list = tree_view->priv->columns; list; list = list->next)
9666         {
9667           column = PSPP_SHEET_VIEW_COLUMN (list->data);
9668           if (column->visible)
9669             _pspp_sheet_view_column_cell_set_dirty (column);
9670         }
9671       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9672     }
9673
9674   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9675
9676   return tree_view->priv->n_columns;
9677 }
9678
9679 /**
9680  * pspp_sheet_view_insert_column_with_attributes:
9681  * @tree_view: A #PsppSheetView
9682  * @position: The position to insert the new column in.
9683  * @title: The title to set the header to.
9684  * @cell: The #GtkCellRenderer.
9685  * @Varargs: A %NULL-terminated list of attributes.
9686  *
9687  * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9688  * @position.  If @position is -1, then the newly created column is inserted at
9689  * the end.  The column is initialized with the attributes given.
9690  *
9691  * Return value: The number of columns in @tree_view after insertion.
9692  **/
9693 gint
9694 pspp_sheet_view_insert_column_with_attributes (PsppSheetView     *tree_view,
9695                                              gint             position,
9696                                              const gchar     *title,
9697                                              GtkCellRenderer *cell,
9698                                              ...)
9699 {
9700   PsppSheetViewColumn *column;
9701   gchar *attribute;
9702   va_list args;
9703   gint column_id;
9704
9705   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9706
9707   column = pspp_sheet_view_column_new ();
9708   pspp_sheet_view_column_set_title (column, title);
9709   pspp_sheet_view_column_pack_start (column, cell, TRUE);
9710
9711   va_start (args, cell);
9712
9713   attribute = va_arg (args, gchar *);
9714
9715   while (attribute != NULL)
9716     {
9717       column_id = va_arg (args, gint);
9718       pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9719       attribute = va_arg (args, gchar *);
9720     }
9721
9722   va_end (args);
9723
9724   pspp_sheet_view_insert_column (tree_view, column, position);
9725
9726   return tree_view->priv->n_columns;
9727 }
9728
9729 /**
9730  * pspp_sheet_view_insert_column_with_data_func:
9731  * @tree_view: a #PsppSheetView
9732  * @position: Position to insert, -1 for append
9733  * @title: column title
9734  * @cell: cell renderer for column
9735  * @func: function to set attributes of cell renderer
9736  * @data: data for @func
9737  * @dnotify: destroy notifier for @data
9738  *
9739  * Convenience function that inserts a new column into the #PsppSheetView
9740  * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9741  * attributes (normally using data from the model). See also
9742  * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9743  *
9744  * Return value: number of columns in the tree view post-insert
9745  **/
9746 gint
9747 pspp_sheet_view_insert_column_with_data_func  (PsppSheetView               *tree_view,
9748                                              gint                       position,
9749                                              const gchar               *title,
9750                                              GtkCellRenderer           *cell,
9751                                              PsppSheetCellDataFunc        func,
9752                                              gpointer                   data,
9753                                              GDestroyNotify             dnotify)
9754 {
9755   PsppSheetViewColumn *column;
9756
9757   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9758
9759   column = pspp_sheet_view_column_new ();
9760   pspp_sheet_view_column_set_title (column, title);
9761   pspp_sheet_view_column_pack_start (column, cell, TRUE);
9762   pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9763
9764   pspp_sheet_view_insert_column (tree_view, column, position);
9765
9766   return tree_view->priv->n_columns;
9767 }
9768
9769 /**
9770  * pspp_sheet_view_get_column:
9771  * @tree_view: A #PsppSheetView.
9772  * @n: The position of the column, counting from 0.
9773  *
9774  * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9775  *
9776  * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9777  * range of columns.
9778  **/
9779 PsppSheetViewColumn *
9780 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9781                           gint         n)
9782 {
9783   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9784
9785   if (n < 0 || n >= tree_view->priv->n_columns)
9786     return NULL;
9787
9788   if (tree_view->priv->columns == NULL)
9789     return NULL;
9790
9791   return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9792 }
9793
9794 /**
9795  * pspp_sheet_view_get_columns:
9796  * @tree_view: A #PsppSheetView
9797  *
9798  * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9799  * The returned list must be freed with g_list_free ().
9800  *
9801  * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9802  **/
9803 GList *
9804 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9805 {
9806   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9807
9808   return g_list_copy (tree_view->priv->columns);
9809 }
9810
9811 /**
9812  * pspp_sheet_view_move_column_after:
9813  * @tree_view: A #PsppSheetView
9814  * @column: The #PsppSheetViewColumn to be moved.
9815  * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9816  *
9817  * Moves @column to be after to @base_column.  If @base_column is %NULL, then
9818  * @column is placed in the first position.
9819  **/
9820 void
9821 pspp_sheet_view_move_column_after (PsppSheetView       *tree_view,
9822                                  PsppSheetViewColumn *column,
9823                                  PsppSheetViewColumn *base_column)
9824 {
9825   GList *column_list_el, *base_el = NULL;
9826
9827   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9828
9829   column_list_el = g_list_find (tree_view->priv->columns, column);
9830   g_return_if_fail (column_list_el != NULL);
9831
9832   if (base_column)
9833     {
9834       base_el = g_list_find (tree_view->priv->columns, base_column);
9835       g_return_if_fail (base_el != NULL);
9836     }
9837
9838   if (column_list_el->prev == base_el)
9839     return;
9840
9841   tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9842   if (base_el == NULL)
9843     {
9844       column_list_el->prev = NULL;
9845       column_list_el->next = tree_view->priv->columns;
9846       if (column_list_el->next)
9847         column_list_el->next->prev = column_list_el;
9848       tree_view->priv->columns = column_list_el;
9849     }
9850   else
9851     {
9852       column_list_el->prev = base_el;
9853       column_list_el->next = base_el->next;
9854       if (column_list_el->next)
9855         column_list_el->next->prev = column_list_el;
9856       base_el->next = column_list_el;
9857     }
9858
9859   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9860     {
9861       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9862       pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9863     }
9864
9865   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9866 }
9867
9868 /**
9869  * pspp_sheet_view_set_column_drag_function:
9870  * @tree_view: A #PsppSheetView.
9871  * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9872  * @user_data: (allow-none): User data to be passed to @func, or %NULL
9873  * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9874  *
9875  * Sets a user function for determining where a column may be dropped when
9876  * dragged.  This function is called on every column pair in turn at the
9877  * beginning of a column drag to determine where a drop can take place.  The
9878  * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9879  * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9880  * @user_data.  If either of the #PsppSheetViewColumn arguments for the drop spot
9881  * are %NULL, then they indicate an edge.  If @func is set to be %NULL, then
9882  * @tree_view reverts to the default behavior of allowing all columns to be
9883  * dropped everywhere.
9884  **/
9885 void
9886 pspp_sheet_view_set_column_drag_function (PsppSheetView               *tree_view,
9887                                         PsppSheetViewColumnDropFunc  func,
9888                                         gpointer                   user_data,
9889                                         GDestroyNotify             destroy)
9890 {
9891   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9892
9893   if (tree_view->priv->column_drop_func_data_destroy)
9894     tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9895
9896   tree_view->priv->column_drop_func = func;
9897   tree_view->priv->column_drop_func_data = user_data;
9898   tree_view->priv->column_drop_func_data_destroy = destroy;
9899 }
9900
9901 /**
9902  * pspp_sheet_view_scroll_to_point:
9903  * @tree_view: a #PsppSheetView
9904  * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9905  * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9906  *
9907  * Scrolls the tree view such that the top-left corner of the visible
9908  * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9909  * in tree coordinates.  The @tree_view must be realized before
9910  * this function is called.  If it isn't, you probably want to be
9911  * using pspp_sheet_view_scroll_to_cell().
9912  *
9913  * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9914  **/
9915 void
9916 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9917                                gint         tree_x,
9918                                gint         tree_y)
9919 {
9920   GtkAdjustment *hadj;
9921   GtkAdjustment *vadj;
9922
9923   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9924   g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9925
9926   hadj = tree_view->priv->hadjustment;
9927   vadj = tree_view->priv->vadjustment;
9928
9929   if (tree_x != -1)
9930     gtk_adjustment_set_value (hadj, CLAMP (tree_x, gtk_adjustment_get_lower (hadj), gtk_adjustment_get_upper (hadj) - gtk_adjustment_get_page_size (hadj)));
9931   if (tree_y != -1)
9932     gtk_adjustment_set_value (vadj, CLAMP (tree_y, gtk_adjustment_get_lower (vadj), gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj)));
9933 }
9934
9935 /**
9936  * pspp_sheet_view_scroll_to_cell:
9937  * @tree_view: A #PsppSheetView.
9938  * @path: (allow-none): The path of the row to move to, or %NULL.
9939  * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9940  * @use_align: whether to use alignment arguments, or %FALSE.
9941  * @row_align: The vertical alignment of the row specified by @path.
9942  * @col_align: The horizontal alignment of the column specified by @column.
9943  *
9944  * Moves the alignments of @tree_view to the position specified by @column and
9945  * @path.  If @column is %NULL, then no horizontal scrolling occurs.  Likewise,
9946  * if @path is %NULL no vertical scrolling occurs.  At a minimum, one of @column
9947  * or @path need to be non-%NULL.  @row_align determines where the row is
9948  * placed, and @col_align determines where @column is placed.  Both are expected
9949  * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9950  * right/bottom alignment, 0.5 means center.
9951  *
9952  * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9953  * tree does the minimum amount of work to scroll the cell onto the screen.
9954  * This means that the cell will be scrolled to the edge closest to its current
9955  * position.  If the cell is currently visible on the screen, nothing is done.
9956  *
9957  * This function only works if the model is set, and @path is a valid row on the
9958  * model.  If the model changes before the @tree_view is realized, the centered
9959  * path will be modified to reflect this change.
9960  **/
9961 void
9962 pspp_sheet_view_scroll_to_cell (PsppSheetView       *tree_view,
9963                               GtkTreePath       *path,
9964                               PsppSheetViewColumn *column,
9965                               gboolean           use_align,
9966                               gfloat             row_align,
9967                               gfloat             col_align)
9968 {
9969   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9970   g_return_if_fail (tree_view->priv->model != NULL);
9971   g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9972   g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9973   g_return_if_fail (path != NULL || column != NULL);
9974
9975 #if 0
9976   g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9977            gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9978 #endif
9979   row_align = CLAMP (row_align, 0.0, 1.0);
9980   col_align = CLAMP (col_align, 0.0, 1.0);
9981
9982
9983   /* Note: Despite the benefits that come from having one code path for the
9984    * scrolling code, we short-circuit validate_visible_area's immplementation as
9985    * it is much slower than just going to the point.
9986    */
9987   if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9988       !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9989       /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9990     {
9991       if (tree_view->priv->scroll_to_path)
9992         gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9993
9994       tree_view->priv->scroll_to_path = NULL;
9995       tree_view->priv->scroll_to_column = NULL;
9996
9997       if (path)
9998         tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
9999       if (column)
10000         tree_view->priv->scroll_to_column = column;
10001       tree_view->priv->scroll_to_use_align = use_align;
10002       tree_view->priv->scroll_to_row_align = row_align;
10003       tree_view->priv->scroll_to_col_align = col_align;
10004
10005       install_presize_handler (tree_view);
10006     }
10007   else
10008     {
10009       GdkRectangle cell_rect;
10010       GdkRectangle vis_rect;
10011       gint dest_x, dest_y;
10012
10013       pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
10014       pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
10015
10016       cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
10017
10018       dest_x = vis_rect.x;
10019       dest_y = vis_rect.y;
10020
10021       if (column)
10022         {
10023           if (use_align)
10024             {
10025               dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
10026             }
10027           else
10028             {
10029               if (cell_rect.x < vis_rect.x)
10030                 dest_x = cell_rect.x;
10031               if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
10032                 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
10033             }
10034         }
10035
10036       if (path)
10037         {
10038           if (use_align)
10039             {
10040               dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
10041               dest_y = MAX (dest_y, 0);
10042             }
10043           else
10044             {
10045               if (cell_rect.y < vis_rect.y)
10046                 dest_y = cell_rect.y;
10047               if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
10048                 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
10049             }
10050         }
10051
10052       pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
10053     }
10054 }
10055
10056 /**
10057  * pspp_sheet_view_row_activated:
10058  * @tree_view: A #PsppSheetView
10059  * @path: The #GtkTreePath to be activated.
10060  * @column: The #PsppSheetViewColumn to be activated.
10061  *
10062  * Activates the cell determined by @path and @column.
10063  **/
10064 void
10065 pspp_sheet_view_row_activated (PsppSheetView       *tree_view,
10066                              GtkTreePath       *path,
10067                              PsppSheetViewColumn *column)
10068 {
10069   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10070
10071   g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
10072 }
10073
10074
10075 /**
10076  * pspp_sheet_view_get_reorderable:
10077  * @tree_view: a #PsppSheetView
10078  *
10079  * Retrieves whether the user can reorder the tree via drag-and-drop. See
10080  * pspp_sheet_view_set_reorderable().
10081  *
10082  * Return value: %TRUE if the tree can be reordered.
10083  **/
10084 gboolean
10085 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
10086 {
10087   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10088
10089   return tree_view->priv->reorderable;
10090 }
10091
10092 /**
10093  * pspp_sheet_view_set_reorderable:
10094  * @tree_view: A #PsppSheetView.
10095  * @reorderable: %TRUE, if the tree can be reordered.
10096  *
10097  * This function is a convenience function to allow you to reorder
10098  * models that support the #GtkDragSourceIface and the
10099  * #GtkDragDestIface.  Both #GtkTreeStore and #GtkListStore support
10100  * these.  If @reorderable is %TRUE, then the user can reorder the
10101  * model by dragging and dropping rows. The developer can listen to
10102  * these changes by connecting to the model's row_inserted and
10103  * row_deleted signals. The reordering is implemented by setting up
10104  * the tree view as a drag source and destination. Therefore, drag and
10105  * drop can not be used in a reorderable view for any other purpose.
10106  *
10107  * This function does not give you any degree of control over the order -- any
10108  * reordering is allowed.  If more control is needed, you should probably
10109  * handle drag and drop manually.
10110  **/
10111 void
10112 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
10113                                gboolean     reorderable)
10114 {
10115   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10116
10117   reorderable = reorderable != FALSE;
10118
10119   if (tree_view->priv->reorderable == reorderable)
10120     return;
10121
10122   if (reorderable)
10123     {
10124       const GtkTargetEntry row_targets[] = {
10125         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
10126       };
10127
10128       pspp_sheet_view_enable_model_drag_source (tree_view,
10129                                               GDK_BUTTON1_MASK,
10130                                               row_targets,
10131                                               G_N_ELEMENTS (row_targets),
10132                                               GDK_ACTION_MOVE);
10133       pspp_sheet_view_enable_model_drag_dest (tree_view,
10134                                             row_targets,
10135                                             G_N_ELEMENTS (row_targets),
10136                                             GDK_ACTION_MOVE);
10137     }
10138   else
10139     {
10140       pspp_sheet_view_unset_rows_drag_source (tree_view);
10141       pspp_sheet_view_unset_rows_drag_dest (tree_view);
10142     }
10143
10144   tree_view->priv->reorderable = reorderable;
10145
10146   g_object_notify (G_OBJECT (tree_view), "reorderable");
10147 }
10148
10149 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
10150    is pressed, other rows will be unselected.
10151
10152    If CLAMP_NODE is true, then the sheetview will scroll to make the row
10153    visible. */
10154 static void
10155 pspp_sheet_view_real_set_cursor (PsppSheetView     *tree_view,
10156                                GtkTreePath     *path,
10157                                gboolean         clear_and_select,
10158                                gboolean         clamp_node,
10159                                PsppSheetSelectMode mode)
10160 {
10161   int node = -1;
10162
10163   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10164     {
10165       GtkTreePath *cursor_path;
10166       cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10167       pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
10168       gtk_tree_path_free (cursor_path);
10169     }
10170
10171   gtk_tree_row_reference_free (tree_view->priv->cursor);
10172   tree_view->priv->cursor = NULL;
10173
10174   _pspp_sheet_view_find_node (tree_view, path, &node);
10175   tree_view->priv->cursor =
10176     gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
10177                                       tree_view->priv->model,
10178                                       path);
10179
10180   if (tree_view->priv->row_count > 0)
10181     {
10182       int new_node = -1;
10183
10184       if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
10185         _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
10186                                                     node, path, mode,
10187                                                     FALSE);
10188
10189       /* We have to re-find tree and node here again, somebody might have
10190        * cleared the node or the whole tree in the PsppSheetSelection::changed
10191        * callback. If the nodes differ we bail out here.
10192        */
10193       _pspp_sheet_view_find_node (tree_view, path, &new_node);
10194
10195       if (node != new_node)
10196         return;
10197
10198       if (clamp_node)
10199         {
10200           pspp_sheet_view_clamp_node_visible (tree_view, node);
10201           _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
10202         }
10203     }
10204
10205   g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
10206 }
10207
10208 /**
10209  * pspp_sheet_view_get_cursor:
10210  * @tree_view: A #PsppSheetView
10211  * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
10212  * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
10213  *
10214  * Fills in @path and @focus_column with the current path and focus column.  If
10215  * the cursor isn't currently set, then *@path will be %NULL.  If no column
10216  * currently has focus, then *@focus_column will be %NULL.
10217  *
10218  * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
10219  * you are done with it.
10220  **/
10221 void
10222 pspp_sheet_view_get_cursor (PsppSheetView        *tree_view,
10223                           GtkTreePath       **path,
10224                           PsppSheetViewColumn **focus_column)
10225 {
10226   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10227
10228   if (path)
10229     {
10230       if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10231         *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10232       else
10233         *path = NULL;
10234     }
10235
10236   if (focus_column)
10237     {
10238       *focus_column = tree_view->priv->focus_column;
10239     }
10240 }
10241
10242 /**
10243  * pspp_sheet_view_set_cursor:
10244  * @tree_view: A #PsppSheetView
10245  * @path: A #GtkTreePath
10246  * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10247  * @start_editing: %TRUE if the specified cell should start being edited.
10248  *
10249  * Sets the current keyboard focus to be at @path, and selects it.  This is
10250  * useful when you want to focus the user's attention on a particular row.  If
10251  * @focus_column is not %NULL, then focus is given to the column specified by 
10252  * it. Additionally, if @focus_column is specified, and @start_editing is 
10253  * %TRUE, then editing should be started in the specified cell.  
10254  * This function is often followed by @gtk_widget_grab_focus (@tree_view) 
10255  * in order to give keyboard focus to the widget.  Please note that editing 
10256  * can only happen when the widget is realized.
10257  *
10258  * If @path is invalid for @model, the current cursor (if any) will be unset
10259  * and the function will return without failing.
10260  **/
10261 void
10262 pspp_sheet_view_set_cursor (PsppSheetView       *tree_view,
10263                           GtkTreePath       *path,
10264                           PsppSheetViewColumn *focus_column,
10265                           gboolean           start_editing)
10266 {
10267   pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10268                                     NULL, start_editing);
10269 }
10270
10271 /**
10272  * pspp_sheet_view_set_cursor_on_cell:
10273  * @tree_view: A #PsppSheetView
10274  * @path: A #GtkTreePath
10275  * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10276  * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10277  * @start_editing: %TRUE if the specified cell should start being edited.
10278  *
10279  * Sets the current keyboard focus to be at @path, and selects it.  This is
10280  * useful when you want to focus the user's attention on a particular row.  If
10281  * @focus_column is not %NULL, then focus is given to the column specified by
10282  * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10283  * contains 2 or more editable or activatable cells, then focus is given to
10284  * the cell specified by @focus_cell. Additionally, if @focus_column is
10285  * specified, and @start_editing is %TRUE, then editing should be started in
10286  * the specified cell.  This function is often followed by
10287  * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10288  * widget.  Please note that editing can only happen when the widget is
10289  * realized.
10290  *
10291  * If @path is invalid for @model, the current cursor (if any) will be unset
10292  * and the function will return without failing.
10293  *
10294  * Since: 2.2
10295  **/
10296 void
10297 pspp_sheet_view_set_cursor_on_cell (PsppSheetView       *tree_view,
10298                                   GtkTreePath       *path,
10299                                   PsppSheetViewColumn *focus_column,
10300                                   GtkCellRenderer   *focus_cell,
10301                                   gboolean           start_editing)
10302 {
10303   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10304   g_return_if_fail (path != NULL);
10305   g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10306
10307   if (!tree_view->priv->model)
10308     return;
10309
10310   if (focus_cell)
10311     {
10312       g_return_if_fail (focus_column);
10313       g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10314     }
10315
10316   /* cancel the current editing, if it exists */
10317   if (tree_view->priv->edited_column &&
10318       tree_view->priv->edited_column->editable_widget)
10319     pspp_sheet_view_stop_editing (tree_view, TRUE);
10320
10321   pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
10322
10323   if (focus_column && focus_column->visible)
10324     {
10325       GList *list;
10326       gboolean column_in_tree = FALSE;
10327
10328       for (list = tree_view->priv->columns; list; list = list->next)
10329         if (list->data == focus_column)
10330           {
10331             column_in_tree = TRUE;
10332             break;
10333           }
10334       g_return_if_fail (column_in_tree);
10335       tree_view->priv->focus_column = focus_column;
10336       if (focus_cell)
10337         pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10338       if (start_editing)
10339         pspp_sheet_view_start_editing (tree_view, path);
10340
10341       pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10342       pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10343
10344     }
10345 }
10346
10347 /**
10348  * pspp_sheet_view_get_bin_window:
10349  * @tree_view: A #PsppSheetView
10350  * 
10351  * Returns the window that @tree_view renders to.  This is used primarily to
10352  * compare to <literal>event->window</literal> to confirm that the event on
10353  * @tree_view is on the right window.
10354  * 
10355  * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10356  **/
10357 GdkWindow *
10358 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10359 {
10360   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10361
10362   return tree_view->priv->bin_window;
10363 }
10364
10365 /**
10366  * pspp_sheet_view_get_path_at_pos:
10367  * @tree_view: A #PsppSheetView.
10368  * @x: The x position to be identified (relative to bin_window).
10369  * @y: The y position to be identified (relative to bin_window).
10370  * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10371  * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10372  * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10373  * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10374  *
10375  * Finds the path at the point (@x, @y), relative to bin_window coordinates
10376  * (please see pspp_sheet_view_get_bin_window()).
10377  * That is, @x and @y are relative to an events coordinates. @x and @y must
10378  * come from an event on the @tree_view only where <literal>event->window ==
10379  * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10380  * things like popup menus. If @path is non-%NULL, then it will be filled
10381  * with the #GtkTreePath at that point.  This path should be freed with
10382  * gtk_tree_path_free().  If @column is non-%NULL, then it will be filled
10383  * with the column at that point.  @cell_x and @cell_y return the coordinates
10384  * relative to the cell background (i.e. the @background_area passed to
10385  * gtk_cell_renderer_render()).  This function is only meaningful if
10386  * @tree_view is realized.  Therefore this function will always return %FALSE
10387  * if @tree_view is not realized or does not have a model.
10388  *
10389  * For converting widget coordinates (eg. the ones you get from
10390  * GtkWidget::query-tooltip), please see
10391  * pspp_sheet_view_convert_widget_to_bin_window_coords().
10392  *
10393  * Return value: %TRUE if a row exists at that coordinate.
10394  **/
10395 gboolean
10396 pspp_sheet_view_get_path_at_pos (PsppSheetView        *tree_view,
10397                                gint                x,
10398                                gint                y,
10399                                GtkTreePath       **path,
10400                                PsppSheetViewColumn **column,
10401                                gint               *cell_x,
10402                                gint               *cell_y)
10403 {
10404   int node;
10405   gint y_offset;
10406
10407   g_return_val_if_fail (tree_view != NULL, FALSE);
10408
10409   if (path)
10410     *path = NULL;
10411   if (column)
10412     *column = NULL;
10413
10414   if (tree_view->priv->bin_window == NULL)
10415     return FALSE;
10416
10417   if (tree_view->priv->row_count == 0)
10418     return FALSE;
10419
10420   if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment))
10421     return FALSE;
10422
10423   if (x < 0 || y < 0)
10424     return FALSE;
10425
10426   if (column || cell_x)
10427     {
10428       PsppSheetViewColumn *tmp_column;
10429       PsppSheetViewColumn *last_column = NULL;
10430       GList *list;
10431       gint remaining_x = x;
10432       gboolean found = FALSE;
10433       gboolean rtl;
10434
10435       rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10436       for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10437            list;
10438            list = (rtl ? list->prev : list->next))
10439         {
10440           tmp_column = list->data;
10441
10442           if (tmp_column->visible == FALSE)
10443             continue;
10444
10445           last_column = tmp_column;
10446           if (remaining_x <= tmp_column->width)
10447             {
10448               found = TRUE;
10449
10450               if (column)
10451                 *column = tmp_column;
10452
10453               if (cell_x)
10454                 *cell_x = remaining_x;
10455
10456               break;
10457             }
10458           remaining_x -= tmp_column->width;
10459         }
10460
10461       /* If found is FALSE and there is a last_column, then it the remainder
10462        * space is in that area
10463        */
10464       if (!found)
10465         {
10466           if (last_column)
10467             {
10468               if (column)
10469                 *column = last_column;
10470               
10471               if (cell_x)
10472                 *cell_x = last_column->width + remaining_x;
10473             }
10474           else
10475             {
10476               return FALSE;
10477             }
10478         }
10479     }
10480
10481   y_offset = pspp_sheet_view_find_offset (tree_view,
10482                                           TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10483                                           &node);
10484
10485   if (node < 0)
10486     return FALSE;
10487
10488   if (cell_y)
10489     *cell_y = y_offset;
10490
10491   if (path)
10492     *path = _pspp_sheet_view_find_path (tree_view, node);
10493
10494   return TRUE;
10495 }
10496
10497 /* Computes 'cell_area' from 'background_area', which must be the background
10498    area for a cell.  Set 'subtract_focus_rect' to TRUE to compute the cell area
10499    as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10500    the cell area as passed to _pspp_sheet_view_column_cell_render().
10501
10502    'column' is required to properly adjust 'cell_area->x' and
10503    'cell_area->width'.  It may be set to NULL if these values are not of
10504    interest.  In this case 'cell_area->x' and 'cell_area->width' will be
10505    returned as 0. */
10506 static void
10507 pspp_sheet_view_adjust_cell_area (PsppSheetView        *tree_view,
10508                                   PsppSheetViewColumn  *column,
10509                                   const GdkRectangle   *background_area,
10510                                   gboolean              subtract_focus_rect,
10511                                   GdkRectangle         *cell_area)
10512 {
10513   gint vertical_separator;
10514   gint horizontal_separator;
10515
10516   *cell_area = *background_area;
10517
10518   gtk_widget_style_get (GTK_WIDGET (tree_view),
10519                         "vertical-separator", &vertical_separator,
10520                         "horizontal-separator", &horizontal_separator,
10521                         NULL);
10522   cell_area->x += horizontal_separator / 2;
10523   cell_area->y += vertical_separator / 2;
10524   cell_area->width -= horizontal_separator;
10525   cell_area->height -= vertical_separator;
10526
10527   if (subtract_focus_rect)
10528     {
10529       int focus_line_width;
10530
10531       gtk_widget_style_get (GTK_WIDGET (tree_view),
10532                             "focus-line-width", &focus_line_width,
10533                             NULL);
10534       cell_area->x += focus_line_width;
10535       cell_area->y += focus_line_width;
10536       cell_area->width -= 2 * focus_line_width;
10537       cell_area->height -= 2 * focus_line_width;
10538     }
10539
10540   if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10541     {
10542       gint grid_line_width;
10543       gtk_widget_style_get (GTK_WIDGET (tree_view),
10544                             "grid-line-width", &grid_line_width,
10545                             NULL);
10546
10547       if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10548            || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10549           && column != NULL)
10550         {
10551           PsppSheetViewColumn *first_column, *last_column;
10552           GList *list;
10553
10554           /* Find the last visible column. */
10555           last_column = NULL;
10556           for (list = g_list_last (tree_view->priv->columns);
10557                list;
10558                list = list->prev)
10559             {
10560               PsppSheetViewColumn *c = list->data;
10561               if (c->visible)
10562                 {
10563                   last_column = c;
10564                   break;
10565                 }
10566             }
10567
10568           /* Find the first visible column. */
10569           first_column = NULL;
10570           for (list = g_list_first (tree_view->priv->columns);
10571                list;
10572                list = list->next)
10573             {
10574               PsppSheetViewColumn *c = list->data;
10575               if (c->visible)
10576                 {
10577                   first_column = c;
10578                   break;
10579                 }
10580             }
10581
10582           if (column == first_column)
10583             {
10584               cell_area->width -= grid_line_width / 2;
10585             }
10586           else if (column == last_column)
10587             {
10588               cell_area->x += grid_line_width / 2;
10589               cell_area->width -= grid_line_width / 2;
10590             }
10591           else
10592             {
10593               cell_area->x += grid_line_width / 2;
10594               cell_area->width -= grid_line_width;
10595             }
10596         }
10597
10598       if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10599           || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10600         {
10601           cell_area->y += grid_line_width / 2;
10602           cell_area->height -= grid_line_width;
10603         }
10604     }
10605
10606   if (column == NULL)
10607     {
10608       cell_area->x = 0;
10609       cell_area->width = 0;
10610     }
10611 }
10612
10613 /**
10614  * pspp_sheet_view_get_cell_area:
10615  * @tree_view: a #PsppSheetView
10616  * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10617  * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10618  * @rect: rectangle to fill with cell rect
10619  *
10620  * Fills the bounding rectangle in bin_window coordinates for the cell at the
10621  * row specified by @path and the column specified by @column.  If @path is
10622  * %NULL, or points to a path not currently displayed, the @y and @height fields
10623  * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10624  * fields will be filled with 0.  The sum of all cell rects does not cover the
10625  * entire tree; there are extra pixels in between rows, for example. The
10626  * returned rectangle is equivalent to the @cell_area passed to
10627  * gtk_cell_renderer_render().  This function is only valid if @tree_view is
10628  * realized.
10629  **/
10630 void
10631 pspp_sheet_view_get_cell_area (PsppSheetView        *tree_view,
10632                              GtkTreePath        *path,
10633                              PsppSheetViewColumn  *column,
10634                              GdkRectangle       *rect)
10635 {
10636   GdkRectangle background_area;
10637
10638   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10639   g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10640   g_return_if_fail (rect != NULL);
10641   g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10642   g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10643
10644   pspp_sheet_view_get_background_area (tree_view, path, column,
10645                                        &background_area);
10646   pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10647                                     FALSE, rect);
10648 }
10649
10650 /**
10651  * pspp_sheet_view_get_background_area:
10652  * @tree_view: a #PsppSheetView
10653  * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10654  * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10655  * @rect: rectangle to fill with cell background rect
10656  *
10657  * Fills the bounding rectangle in bin_window coordinates for the cell at the
10658  * row specified by @path and the column specified by @column.  If @path is
10659  * %NULL, or points to a node not found in the tree, the @y and @height fields of
10660  * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10661  * fields will be filled with 0.  The returned rectangle is equivalent to the
10662  * @background_area passed to gtk_cell_renderer_render().  These background
10663  * areas tile to cover the entire bin window.  Contrast with the @cell_area,
10664  * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10665  * itself, excluding surrounding borders.
10666  *
10667  **/
10668 void
10669 pspp_sheet_view_get_background_area (PsppSheetView        *tree_view,
10670                                    GtkTreePath        *path,
10671                                    PsppSheetViewColumn  *column,
10672                                    GdkRectangle       *rect)
10673 {
10674   int node = -1;
10675
10676   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10677   g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10678   g_return_if_fail (rect != NULL);
10679
10680   rect->x = 0;
10681   rect->y = 0;
10682   rect->width = 0;
10683   rect->height = 0;
10684
10685   if (path)
10686     {
10687       /* Get vertical coords */
10688
10689       _pspp_sheet_view_find_node (tree_view, path, &node);
10690       if (node < 0)
10691         return;
10692
10693       rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10694
10695       rect->height = ROW_HEIGHT (tree_view);
10696     }
10697
10698   if (column)
10699     {
10700       gint x2 = 0;
10701
10702       pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10703       rect->width = x2 - rect->x;
10704     }
10705 }
10706
10707 /**
10708  * pspp_sheet_view_get_visible_rect:
10709  * @tree_view: a #PsppSheetView
10710  * @visible_rect: rectangle to fill
10711  *
10712  * Fills @visible_rect with the currently-visible region of the
10713  * buffer, in tree coordinates. Convert to bin_window coordinates with
10714  * pspp_sheet_view_convert_tree_to_bin_window_coords().
10715  * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10716  * scrollable area of the tree.
10717  **/
10718 void
10719 pspp_sheet_view_get_visible_rect (PsppSheetView  *tree_view,
10720                                 GdkRectangle *visible_rect)
10721 {
10722   GtkWidget *widget;
10723
10724   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10725
10726   widget = GTK_WIDGET (tree_view);
10727
10728   if (visible_rect)
10729     {
10730       GtkAllocation allocation;
10731       gtk_widget_get_allocation (widget, &allocation);
10732       visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
10733       visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
10734       visible_rect->width  = allocation.width;
10735       visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10736     }
10737 }
10738
10739 /**
10740  * pspp_sheet_view_widget_to_tree_coords:
10741  * @tree_view: a #PsppSheetView
10742  * @wx: X coordinate relative to bin_window
10743  * @wy: Y coordinate relative to bin_window
10744  * @tx: return location for tree X coordinate
10745  * @ty: return location for tree Y coordinate
10746  *
10747  * Converts bin_window coordinates to coordinates for the
10748  * tree (the full scrollable area of the tree).
10749  *
10750  * Deprecated: 2.12: Due to historial reasons the name of this function is
10751  * incorrect.  For converting coordinates relative to the widget to
10752  * bin_window coordinates, please see
10753  * pspp_sheet_view_convert_widget_to_bin_window_coords().
10754  *
10755  **/
10756 void
10757 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10758                                       gint         wx,
10759                                       gint         wy,
10760                                       gint        *tx,
10761                                       gint        *ty)
10762 {
10763   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10764
10765   if (tx)
10766     *tx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10767   if (ty)
10768     *ty = wy + tree_view->priv->dy;
10769 }
10770
10771 /**
10772  * pspp_sheet_view_tree_to_widget_coords:
10773  * @tree_view: a #PsppSheetView
10774  * @tx: tree X coordinate
10775  * @ty: tree Y coordinate
10776  * @wx: return location for X coordinate relative to bin_window
10777  * @wy: return location for Y coordinate relative to bin_window
10778  *
10779  * Converts tree coordinates (coordinates in full scrollable area of the tree)
10780  * to bin_window coordinates.
10781  *
10782  * Deprecated: 2.12: Due to historial reasons the name of this function is
10783  * incorrect.  For converting bin_window coordinates to coordinates relative
10784  * to bin_window, please see
10785  * pspp_sheet_view_convert_bin_window_to_widget_coords().
10786  *
10787  **/
10788 void
10789 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10790                                      gint         tx,
10791                                      gint         ty,
10792                                      gint        *wx,
10793                                      gint        *wy)
10794 {
10795   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10796
10797   if (wx)
10798     *wx = tx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10799   if (wy)
10800     *wy = ty - tree_view->priv->dy;
10801 }
10802
10803
10804 /**
10805  * pspp_sheet_view_convert_widget_to_tree_coords:
10806  * @tree_view: a #PsppSheetView
10807  * @wx: X coordinate relative to the widget
10808  * @wy: Y coordinate relative to the widget
10809  * @tx: return location for tree X coordinate
10810  * @ty: return location for tree Y coordinate
10811  *
10812  * Converts widget coordinates to coordinates for the
10813  * tree (the full scrollable area of the tree).
10814  *
10815  * Since: 2.12
10816  **/
10817 void
10818 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10819                                              gint         wx,
10820                                              gint         wy,
10821                                              gint        *tx,
10822                                              gint        *ty)
10823 {
10824   gint x, y;
10825
10826   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10827
10828   pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10829                                                      wx, wy,
10830                                                      &x, &y);
10831   pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10832                                                    x, y,
10833                                                    tx, ty);
10834 }
10835
10836 /**
10837  * pspp_sheet_view_convert_tree_to_widget_coords:
10838  * @tree_view: a #PsppSheetView
10839  * @tx: X coordinate relative to the tree
10840  * @ty: Y coordinate relative to the tree
10841  * @wx: return location for widget X coordinate
10842  * @wy: return location for widget Y coordinate
10843  *
10844  * Converts tree coordinates (coordinates in full scrollable area of the tree)
10845  * to widget coordinates.
10846  *
10847  * Since: 2.12
10848  **/
10849 void
10850 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10851                                              gint         tx,
10852                                              gint         ty,
10853                                              gint        *wx,
10854                                              gint        *wy)
10855 {
10856   gint x, y;
10857
10858   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10859
10860   pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10861                                                    tx, ty,
10862                                                    &x, &y);
10863   pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10864                                                      x, y,
10865                                                      wx, wy);
10866 }
10867
10868 /**
10869  * pspp_sheet_view_convert_widget_to_bin_window_coords:
10870  * @tree_view: a #PsppSheetView
10871  * @wx: X coordinate relative to the widget
10872  * @wy: Y coordinate relative to the widget
10873  * @bx: return location for bin_window X coordinate
10874  * @by: return location for bin_window Y coordinate
10875  *
10876  * Converts widget coordinates to coordinates for the bin_window
10877  * (see pspp_sheet_view_get_bin_window()).
10878  *
10879  * Since: 2.12
10880  **/
10881 void
10882 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10883                                                    gint         wx,
10884                                                    gint         wy,
10885                                                    gint        *bx,
10886                                                    gint        *by)
10887 {
10888   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10889
10890   if (bx)
10891     *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10892   if (by)
10893     *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10894 }
10895
10896 /**
10897  * pspp_sheet_view_convert_bin_window_to_widget_coords:
10898  * @tree_view: a #PsppSheetView
10899  * @bx: bin_window X coordinate
10900  * @by: bin_window Y coordinate
10901  * @wx: return location for widget X coordinate
10902  * @wy: return location for widget Y coordinate
10903  *
10904  * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10905  * to widget relative coordinates.
10906  *
10907  * Since: 2.12
10908  **/
10909 void
10910 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10911                                                    gint         bx,
10912                                                    gint         by,
10913                                                    gint        *wx,
10914                                                    gint        *wy)
10915 {
10916   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10917
10918   if (wx)
10919     *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10920   if (wy)
10921     *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10922 }
10923
10924 /**
10925  * pspp_sheet_view_convert_tree_to_bin_window_coords:
10926  * @tree_view: a #PsppSheetView
10927  * @tx: tree X coordinate
10928  * @ty: tree Y coordinate
10929  * @bx: return location for X coordinate relative to bin_window
10930  * @by: return location for Y coordinate relative to bin_window
10931  *
10932  * Converts tree coordinates (coordinates in full scrollable area of the tree)
10933  * to bin_window coordinates.
10934  *
10935  * Since: 2.12
10936  **/
10937 void
10938 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10939                                                  gint         tx,
10940                                                  gint         ty,
10941                                                  gint        *bx,
10942                                                  gint        *by)
10943 {
10944   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10945
10946   if (bx)
10947     *bx = tx;
10948   if (by)
10949     *by = ty - tree_view->priv->dy;
10950 }
10951
10952 /**
10953  * pspp_sheet_view_convert_bin_window_to_tree_coords:
10954  * @tree_view: a #PsppSheetView
10955  * @bx: X coordinate relative to bin_window
10956  * @by: Y coordinate relative to bin_window
10957  * @tx: return location for tree X coordinate
10958  * @ty: return location for tree Y coordinate
10959  *
10960  * Converts bin_window coordinates to coordinates for the
10961  * tree (the full scrollable area of the tree).
10962  *
10963  * Since: 2.12
10964  **/
10965 void
10966 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10967                                                  gint         bx,
10968                                                  gint         by,
10969                                                  gint        *tx,
10970                                                  gint        *ty)
10971 {
10972   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10973
10974   if (tx)
10975     *tx = bx;
10976   if (ty)
10977     *ty = by + tree_view->priv->dy;
10978 }
10979
10980
10981
10982 /**
10983  * pspp_sheet_view_get_visible_range:
10984  * @tree_view: A #PsppSheetView
10985  * @start_path: (allow-none): Return location for start of region, or %NULL.
10986  * @end_path: (allow-none): Return location for end of region, or %NULL.
10987  *
10988  * Sets @start_path and @end_path to be the first and last visible path.
10989  * Note that there may be invisible paths in between.
10990  *
10991  * The paths should be freed with gtk_tree_path_free() after use.
10992  *
10993  * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
10994  *
10995  * Since: 2.8
10996  **/
10997 gboolean
10998 pspp_sheet_view_get_visible_range (PsppSheetView  *tree_view,
10999                                  GtkTreePath **start_path,
11000                                  GtkTreePath **end_path)
11001 {
11002   int node;
11003   gboolean retval;
11004   
11005   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11006
11007   if (!tree_view->priv->row_count)
11008     return FALSE;
11009
11010   retval = TRUE;
11011
11012   if (start_path)
11013     {
11014       pspp_sheet_view_find_offset (tree_view,
11015                                    TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
11016                                    &node);
11017       if (node >= 0)
11018         *start_path = _pspp_sheet_view_find_path (tree_view, node);
11019       else
11020         retval = FALSE;
11021     }
11022
11023   if (end_path)
11024     {
11025       gint y;
11026
11027       if (tree_view->priv->height < gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
11028         y = tree_view->priv->height - 1;
11029       else
11030         y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1;
11031
11032       pspp_sheet_view_find_offset (tree_view, y, &node);
11033       if (node >= 0)
11034         *end_path = _pspp_sheet_view_find_path (tree_view, node);
11035       else
11036         retval = FALSE;
11037     }
11038
11039   return retval;
11040 }
11041
11042 static void
11043 unset_reorderable (PsppSheetView *tree_view)
11044 {
11045   if (tree_view->priv->reorderable)
11046     {
11047       tree_view->priv->reorderable = FALSE;
11048       g_object_notify (G_OBJECT (tree_view), "reorderable");
11049     }
11050 }
11051
11052 /**
11053  * pspp_sheet_view_enable_model_drag_source:
11054  * @tree_view: a #PsppSheetView
11055  * @start_button_mask: Mask of allowed buttons to start drag
11056  * @targets: the table of targets that the drag will support
11057  * @n_targets: the number of items in @targets
11058  * @actions: the bitmask of possible actions for a drag from this
11059  *    widget
11060  *
11061  * Turns @tree_view into a drag source for automatic DND. Calling this
11062  * method sets #PsppSheetView:reorderable to %FALSE.
11063  **/
11064 void
11065 pspp_sheet_view_enable_model_drag_source (PsppSheetView              *tree_view,
11066                                         GdkModifierType           start_button_mask,
11067                                         const GtkTargetEntry     *targets,
11068                                         gint                      n_targets,
11069                                         GdkDragAction             actions)
11070 {
11071   TreeViewDragInfo *di;
11072
11073   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11074
11075   gtk_drag_source_set (GTK_WIDGET (tree_view),
11076                        0,
11077                        targets,
11078                        n_targets,
11079                        actions);
11080
11081   di = ensure_info (tree_view);
11082
11083   di->start_button_mask = start_button_mask;
11084   di->source_actions = actions;
11085   di->source_set = TRUE;
11086
11087   unset_reorderable (tree_view);
11088 }
11089
11090 /**
11091  * pspp_sheet_view_enable_model_drag_dest:
11092  * @tree_view: a #PsppSheetView
11093  * @targets: the table of targets that the drag will support
11094  * @n_targets: the number of items in @targets
11095  * @actions: the bitmask of possible actions for a drag from this
11096  *    widget
11097  * 
11098  * Turns @tree_view into a drop destination for automatic DND. Calling
11099  * this method sets #PsppSheetView:reorderable to %FALSE.
11100  **/
11101 void
11102 pspp_sheet_view_enable_model_drag_dest (PsppSheetView              *tree_view,
11103                                       const GtkTargetEntry     *targets,
11104                                       gint                      n_targets,
11105                                       GdkDragAction             actions)
11106 {
11107   TreeViewDragInfo *di;
11108
11109   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11110
11111   gtk_drag_dest_set (GTK_WIDGET (tree_view),
11112                      0,
11113                      targets,
11114                      n_targets,
11115                      actions);
11116
11117   di = ensure_info (tree_view);
11118   di->dest_set = TRUE;
11119
11120   unset_reorderable (tree_view);
11121 }
11122
11123 /**
11124  * pspp_sheet_view_unset_rows_drag_source:
11125  * @tree_view: a #PsppSheetView
11126  *
11127  * Undoes the effect of
11128  * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
11129  * #PsppSheetView:reorderable to %FALSE.
11130  **/
11131 void
11132 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
11133 {
11134   TreeViewDragInfo *di;
11135
11136   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11137
11138   di = get_info (tree_view);
11139
11140   if (di)
11141     {
11142       if (di->source_set)
11143         {
11144           gtk_drag_source_unset (GTK_WIDGET (tree_view));
11145           di->source_set = FALSE;
11146         }
11147
11148       if (!di->dest_set && !di->source_set)
11149         remove_info (tree_view);
11150     }
11151   
11152   unset_reorderable (tree_view);
11153 }
11154
11155 /**
11156  * pspp_sheet_view_unset_rows_drag_dest:
11157  * @tree_view: a #PsppSheetView
11158  *
11159  * Undoes the effect of
11160  * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
11161  * #PsppSheetView:reorderable to %FALSE.
11162  **/
11163 void
11164 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
11165 {
11166   TreeViewDragInfo *di;
11167
11168   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11169
11170   di = get_info (tree_view);
11171
11172   if (di)
11173     {
11174       if (di->dest_set)
11175         {
11176           gtk_drag_dest_unset (GTK_WIDGET (tree_view));
11177           di->dest_set = FALSE;
11178         }
11179
11180       if (!di->dest_set && !di->source_set)
11181         remove_info (tree_view);
11182     }
11183
11184   unset_reorderable (tree_view);
11185 }
11186
11187 /**
11188  * pspp_sheet_view_set_drag_dest_row:
11189  * @tree_view: a #PsppSheetView
11190  * @path: (allow-none): The path of the row to highlight, or %NULL.
11191  * @pos: Specifies whether to drop before, after or into the row
11192  * 
11193  * Sets the row that is highlighted for feedback.
11194  **/
11195 void
11196 pspp_sheet_view_set_drag_dest_row (PsppSheetView            *tree_view,
11197                                  GtkTreePath            *path,
11198                                  PsppSheetViewDropPosition pos)
11199 {
11200   GtkTreePath *current_dest;
11201
11202   /* Note; this function is exported to allow a custom DND
11203    * implementation, so it can't touch TreeViewDragInfo
11204    */
11205
11206   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11207
11208   current_dest = NULL;
11209
11210   if (tree_view->priv->drag_dest_row)
11211     {
11212       current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11213       gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
11214     }
11215
11216   /* special case a drop on an empty model */
11217   tree_view->priv->empty_view_drop = 0;
11218
11219   if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
11220       && gtk_tree_path_get_depth (path) == 1
11221       && gtk_tree_path_get_indices (path)[0] == 0)
11222     {
11223       gint n_children;
11224
11225       n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
11226                                                    NULL);
11227
11228       if (!n_children)
11229         tree_view->priv->empty_view_drop = 1;
11230     }
11231
11232   tree_view->priv->drag_dest_pos = pos;
11233
11234   if (path)
11235     {
11236       tree_view->priv->drag_dest_row =
11237         gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
11238       pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
11239     }
11240   else
11241     tree_view->priv->drag_dest_row = NULL;
11242
11243   if (current_dest)
11244     {
11245       int node, new_node;
11246
11247       _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11248       _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11249
11250       if (node >= 0)
11251         {
11252           new_node = pspp_sheet_view_node_next (tree_view, node);
11253           if (new_node >= 0)
11254             _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11255
11256           new_node = pspp_sheet_view_node_prev (tree_view, node);
11257           if (new_node >= 0)
11258             _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11259         }
11260       gtk_tree_path_free (current_dest);
11261     }
11262 }
11263
11264 /**
11265  * pspp_sheet_view_get_drag_dest_row:
11266  * @tree_view: a #PsppSheetView
11267  * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11268  * @pos: (allow-none): Return location for the drop position, or %NULL
11269  * 
11270  * Gets information about the row that is highlighted for feedback.
11271  **/
11272 void
11273 pspp_sheet_view_get_drag_dest_row (PsppSheetView              *tree_view,
11274                                  GtkTreePath             **path,
11275                                  PsppSheetViewDropPosition  *pos)
11276 {
11277   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11278
11279   if (path)
11280     {
11281       if (tree_view->priv->drag_dest_row)
11282         *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11283       else
11284         {
11285           if (tree_view->priv->empty_view_drop)
11286             *path = gtk_tree_path_new_from_indices (0, -1);
11287           else
11288             *path = NULL;
11289         }
11290     }
11291
11292   if (pos)
11293     *pos = tree_view->priv->drag_dest_pos;
11294 }
11295
11296 /**
11297  * pspp_sheet_view_get_dest_row_at_pos:
11298  * @tree_view: a #PsppSheetView
11299  * @drag_x: the position to determine the destination row for
11300  * @drag_y: the position to determine the destination row for
11301  * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11302  * @pos: (allow-none): Return location for the drop position, or %NULL
11303  * 
11304  * Determines the destination row for a given position.  @drag_x and
11305  * @drag_y are expected to be in widget coordinates.  This function is only
11306  * meaningful if @tree_view is realized.  Therefore this function will always
11307  * return %FALSE if @tree_view is not realized or does not have a model.
11308  * 
11309  * Return value: whether there is a row at the given position, %TRUE if this
11310  * is indeed the case.
11311  **/
11312 gboolean
11313 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView             *tree_view,
11314                                    gint                     drag_x,
11315                                    gint                     drag_y,
11316                                    GtkTreePath            **path,
11317                                    PsppSheetViewDropPosition *pos)
11318 {
11319   gint cell_y;
11320   gint bin_x, bin_y;
11321   gdouble offset_into_row;
11322   gdouble third;
11323   GdkRectangle cell;
11324   PsppSheetViewColumn *column = NULL;
11325   GtkTreePath *tmp_path = NULL;
11326
11327   /* Note; this function is exported to allow a custom DND
11328    * implementation, so it can't touch TreeViewDragInfo
11329    */
11330
11331   g_return_val_if_fail (tree_view != NULL, FALSE);
11332   g_return_val_if_fail (drag_x >= 0, FALSE);
11333   g_return_val_if_fail (drag_y >= 0, FALSE);
11334
11335   if (path)
11336     *path = NULL;
11337
11338   if (tree_view->priv->bin_window == NULL)
11339     return FALSE;
11340
11341   if (tree_view->priv->row_count == 0)
11342     return FALSE;
11343
11344   /* If in the top third of a row, we drop before that row; if
11345    * in the bottom third, drop after that row; if in the middle,
11346    * and the row has children, drop into the row.
11347    */
11348   pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11349                                                      &bin_x, &bin_y);
11350
11351   if (!pspp_sheet_view_get_path_at_pos (tree_view,
11352                                       bin_x,
11353                                       bin_y,
11354                                       &tmp_path,
11355                                       &column,
11356                                       NULL,
11357                                       &cell_y))
11358     return FALSE;
11359
11360   pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11361                                      &cell);
11362
11363   offset_into_row = cell_y;
11364
11365   if (path)
11366     *path = tmp_path;
11367   else
11368     gtk_tree_path_free (tmp_path);
11369
11370   tmp_path = NULL;
11371
11372   third = cell.height / 3.0;
11373
11374   if (pos)
11375     {
11376       if (offset_into_row < third)
11377         {
11378           *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11379         }
11380       else if (offset_into_row < (cell.height / 2.0))
11381         {
11382           *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11383         }
11384       else if (offset_into_row < third * 2.0)
11385         {
11386           *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11387         }
11388       else
11389         {
11390           *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11391         }
11392     }
11393
11394   return TRUE;
11395 }
11396
11397
11398 #if GTK3_TRANSITION
11399 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11400 /**
11401  * pspp_sheet_view_create_row_drag_icon:
11402  * @tree_view: a #PsppSheetView
11403  * @path: a #GtkTreePath in @tree_view
11404  *
11405  * Creates a #GdkPixmap representation of the row at @path.  
11406  * This image is used for a drag icon.
11407  *
11408  * Return value: a newly-allocated pixmap of the drag icon.
11409  **/
11410 GdkPixmap *
11411 pspp_sheet_view_create_row_drag_icon (PsppSheetView  *tree_view,
11412                                     GtkTreePath  *path)
11413 {
11414   GtkTreeIter   iter;
11415   int node;
11416   gint cell_offset;
11417   GList *list;
11418   GdkRectangle background_area;
11419   GdkRectangle expose_area;
11420   GtkWidget *widget;
11421   /* start drawing inside the black outline */
11422   gint x = 1, y = 1;
11423   GdkDrawable *drawable;
11424   gint bin_window_width;
11425   gboolean rtl;
11426
11427   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11428   g_return_val_if_fail (path != NULL, NULL);
11429
11430   widget = GTK_WIDGET (tree_view);
11431
11432   if (!gtk_widget_get_realized (widget))
11433     return NULL;
11434
11435   _pspp_sheet_view_find_node (tree_view,
11436                             path,
11437                             &node);
11438
11439   if (node < 0)
11440     return NULL;
11441
11442   if (!gtk_tree_model_get_iter (tree_view->priv->model,
11443                                 &iter,
11444                                 path))
11445     return NULL;
11446   
11447   cell_offset = x;
11448
11449   background_area.y = y;
11450   background_area.height = ROW_HEIGHT (tree_view);
11451
11452   bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
11453
11454   drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11455                              bin_window_width + 2,
11456                              background_area.height + 2,
11457                              -1);
11458
11459   expose_area.x = 0;
11460   expose_area.y = 0;
11461   expose_area.width = bin_window_width + 2;
11462   expose_area.height = background_area.height + 2;
11463
11464 #if GTK3_TRANSITION
11465   gdk_draw_rectangle (drawable,
11466                       widget->style->base_gc [gtk_widget_get_state (widget)],
11467                       TRUE,
11468                       0, 0,
11469                       bin_window_width + 2,
11470                       background_area.height + 2);
11471 #endif
11472
11473   rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11474
11475   for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11476       list;
11477       list = (rtl ? list->prev : list->next))
11478     {
11479       PsppSheetViewColumn *column = list->data;
11480       GdkRectangle cell_area;
11481       gint vertical_separator;
11482
11483       if (!column->visible)
11484         continue;
11485
11486       pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11487
11488       background_area.x = cell_offset;
11489       background_area.width = column->width;
11490
11491       gtk_widget_style_get (widget,
11492                             "vertical-separator", &vertical_separator,
11493                             NULL);
11494
11495       cell_area = background_area;
11496
11497       cell_area.y += vertical_separator / 2;
11498       cell_area.height -= vertical_separator;
11499
11500       if (pspp_sheet_view_column_cell_is_visible (column))
11501         _pspp_sheet_view_column_cell_render (column,
11502                                              drawable,
11503                                              &background_area,
11504                                              &cell_area,
11505                                              &expose_area,
11506                                              0);
11507       cell_offset += column->width;
11508     }
11509
11510 #if GTK3_TRANSITION
11511   gdk_draw_rectangle (drawable,
11512                       widget->style->black_gc,
11513                       FALSE,
11514                       0, 0,
11515                       bin_window_width + 1,
11516                       background_area.height + 1);
11517 #endif
11518
11519   return drawable;
11520 }
11521 #endif
11522
11523 /**
11524  * pspp_sheet_view_set_destroy_count_func:
11525  * @tree_view: A #PsppSheetView
11526  * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11527  * @data: (allow-none): User data to be passed to @func, or %NULL
11528  * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11529  *
11530  * This function should almost never be used.  It is meant for private use by
11531  * ATK for determining the number of visible children that are removed when a row is deleted.
11532  **/
11533 void
11534 pspp_sheet_view_set_destroy_count_func (PsppSheetView             *tree_view,
11535                                       PsppSheetDestroyCountFunc  func,
11536                                       gpointer                 data,
11537                                       GDestroyNotify           destroy)
11538 {
11539   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11540
11541   if (tree_view->priv->destroy_count_destroy)
11542     tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11543
11544   tree_view->priv->destroy_count_func = func;
11545   tree_view->priv->destroy_count_data = data;
11546   tree_view->priv->destroy_count_destroy = destroy;
11547 }
11548
11549
11550 /*
11551  * Interactive search
11552  */
11553
11554 /**
11555  * pspp_sheet_view_set_enable_search:
11556  * @tree_view: A #PsppSheetView
11557  * @enable_search: %TRUE, if the user can search interactively
11558  *
11559  * If @enable_search is set, then the user can type in text to search through
11560  * the tree interactively (this is sometimes called "typeahead find").
11561  * 
11562  * Note that even if this is %FALSE, the user can still initiate a search 
11563  * using the "start-interactive-search" key binding.
11564  */
11565 void
11566 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11567                                  gboolean     enable_search)
11568 {
11569   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11570
11571   enable_search = !!enable_search;
11572   
11573   if (tree_view->priv->enable_search != enable_search)
11574     {
11575        tree_view->priv->enable_search = enable_search;
11576        g_object_notify (G_OBJECT (tree_view), "enable-search");
11577     }
11578 }
11579
11580 /**
11581  * pspp_sheet_view_get_enable_search:
11582  * @tree_view: A #PsppSheetView
11583  *
11584  * Returns whether or not the tree allows to start interactive searching 
11585  * by typing in text.
11586  *
11587  * Return value: whether or not to let the user search interactively
11588  */
11589 gboolean
11590 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11591 {
11592   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11593
11594   return tree_view->priv->enable_search;
11595 }
11596
11597
11598 /**
11599  * pspp_sheet_view_get_search_column:
11600  * @tree_view: A #PsppSheetView
11601  *
11602  * Gets the column searched on by the interactive search code.
11603  *
11604  * Return value: the column the interactive search code searches in.
11605  */
11606 gint
11607 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11608 {
11609   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11610
11611   return (tree_view->priv->search_column);
11612 }
11613
11614 /**
11615  * pspp_sheet_view_set_search_column:
11616  * @tree_view: A #PsppSheetView
11617  * @column: the column of the model to search in, or -1 to disable searching
11618  *
11619  * Sets @column as the column where the interactive search code should
11620  * search in for the current model. 
11621  * 
11622  * If the search column is set, users can use the "start-interactive-search"
11623  * key binding to bring up search popup. The enable-search property controls
11624  * whether simply typing text will also start an interactive search.
11625  *
11626  * Note that @column refers to a column of the current model. The search 
11627  * column is reset to -1 when the model is changed.
11628  */
11629 void
11630 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11631                                  gint         column)
11632 {
11633   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11634   g_return_if_fail (column >= -1);
11635
11636   if (tree_view->priv->search_column == column)
11637     return;
11638
11639   tree_view->priv->search_column = column;
11640   g_object_notify (G_OBJECT (tree_view), "search-column");
11641 }
11642
11643 /**
11644  * pspp_sheet_view_get_search_equal_func:
11645  * @tree_view: A #PsppSheetView
11646  *
11647  * Returns the compare function currently in use.
11648  *
11649  * Return value: the currently used compare function for the search code.
11650  */
11651
11652 PsppSheetViewSearchEqualFunc
11653 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11654 {
11655   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11656
11657   return tree_view->priv->search_equal_func;
11658 }
11659
11660 /**
11661  * pspp_sheet_view_set_search_equal_func:
11662  * @tree_view: A #PsppSheetView
11663  * @search_equal_func: the compare function to use during the search
11664  * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11665  * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11666  *
11667  * Sets the compare function for the interactive search capabilities; note
11668  * that somewhat like strcmp() returning 0 for equality
11669  * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11670  **/
11671 void
11672 pspp_sheet_view_set_search_equal_func (PsppSheetView                *tree_view,
11673                                      PsppSheetViewSearchEqualFunc  search_equal_func,
11674                                      gpointer                    search_user_data,
11675                                      GDestroyNotify              search_destroy)
11676 {
11677   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11678   g_return_if_fail (search_equal_func != NULL);
11679
11680   if (tree_view->priv->search_destroy)
11681     tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11682
11683   tree_view->priv->search_equal_func = search_equal_func;
11684   tree_view->priv->search_user_data = search_user_data;
11685   tree_view->priv->search_destroy = search_destroy;
11686   if (tree_view->priv->search_equal_func == NULL)
11687     tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11688 }
11689
11690 /**
11691  * pspp_sheet_view_get_search_entry:
11692  * @tree_view: A #PsppSheetView
11693  *
11694  * Returns the #GtkEntry which is currently in use as interactive search
11695  * entry for @tree_view.  In case the built-in entry is being used, %NULL
11696  * will be returned.
11697  *
11698  * Return value: the entry currently in use as search entry.
11699  *
11700  * Since: 2.10
11701  */
11702 GtkEntry *
11703 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11704 {
11705   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11706
11707   if (tree_view->priv->search_custom_entry_set)
11708     return GTK_ENTRY (tree_view->priv->search_entry);
11709
11710   return NULL;
11711 }
11712
11713 /**
11714  * pspp_sheet_view_set_search_entry:
11715  * @tree_view: A #PsppSheetView
11716  * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11717  *
11718  * Sets the entry which the interactive search code will use for this
11719  * @tree_view.  This is useful when you want to provide a search entry
11720  * in our interface at all time at a fixed position.  Passing %NULL for
11721  * @entry will make the interactive search code use the built-in popup
11722  * entry again.
11723  *
11724  * Since: 2.10
11725  */
11726 void
11727 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11728                                 GtkEntry    *entry)
11729 {
11730   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11731   g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11732
11733   if (tree_view->priv->search_custom_entry_set)
11734     {
11735       if (tree_view->priv->search_entry_changed_id)
11736         {
11737           g_signal_handler_disconnect (tree_view->priv->search_entry,
11738                                        tree_view->priv->search_entry_changed_id);
11739           tree_view->priv->search_entry_changed_id = 0;
11740         }
11741       g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11742                                             G_CALLBACK (pspp_sheet_view_search_key_press_event),
11743                                             tree_view);
11744
11745       g_object_unref (tree_view->priv->search_entry);
11746     }
11747   else if (tree_view->priv->search_window)
11748     {
11749       gtk_widget_destroy (tree_view->priv->search_window);
11750
11751       tree_view->priv->search_window = NULL;
11752     }
11753
11754   if (entry)
11755     {
11756       tree_view->priv->search_entry = g_object_ref (entry);
11757       tree_view->priv->search_custom_entry_set = TRUE;
11758
11759       if (tree_view->priv->search_entry_changed_id == 0)
11760         {
11761           tree_view->priv->search_entry_changed_id =
11762             g_signal_connect (tree_view->priv->search_entry, "changed",
11763                               G_CALLBACK (pspp_sheet_view_search_init),
11764                               tree_view);
11765         }
11766       
11767         g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11768                           G_CALLBACK (pspp_sheet_view_search_key_press_event),
11769                           tree_view);
11770
11771         pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11772     }
11773   else
11774     {
11775       tree_view->priv->search_entry = NULL;
11776       tree_view->priv->search_custom_entry_set = FALSE;
11777     }
11778 }
11779
11780 /**
11781  * pspp_sheet_view_set_search_position_func:
11782  * @tree_view: A #PsppSheetView
11783  * @func: (allow-none): the function to use to position the search dialog, or %NULL
11784  *    to use the default search position function
11785  * @data: (allow-none): user data to pass to @func, or %NULL
11786  * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11787  *
11788  * Sets the function to use when positioning the search dialog.
11789  *
11790  * Since: 2.10
11791  **/
11792 void
11793 pspp_sheet_view_set_search_position_func (PsppSheetView                   *tree_view,
11794                                         PsppSheetViewSearchPositionFunc  func,
11795                                         gpointer                       user_data,
11796                                         GDestroyNotify                 destroy)
11797 {
11798   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11799
11800   if (tree_view->priv->search_position_destroy)
11801     tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11802
11803   tree_view->priv->search_position_func = func;
11804   tree_view->priv->search_position_user_data = user_data;
11805   tree_view->priv->search_position_destroy = destroy;
11806   if (tree_view->priv->search_position_func == NULL)
11807     tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11808 }
11809
11810 /**
11811  * pspp_sheet_view_get_search_position_func:
11812  * @tree_view: A #PsppSheetView
11813  *
11814  * Returns the positioning function currently in use.
11815  *
11816  * Return value: the currently used function for positioning the search dialog.
11817  *
11818  * Since: 2.10
11819  */
11820 PsppSheetViewSearchPositionFunc
11821 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11822 {
11823   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11824
11825   return tree_view->priv->search_position_func;
11826 }
11827
11828
11829 static void
11830 pspp_sheet_view_search_dialog_hide (GtkWidget   *search_dialog,
11831                                   PsppSheetView *tree_view)
11832 {
11833   if (tree_view->priv->disable_popdown)
11834     return;
11835
11836   if (tree_view->priv->search_entry_changed_id)
11837     {
11838       g_signal_handler_disconnect (tree_view->priv->search_entry,
11839                                    tree_view->priv->search_entry_changed_id);
11840       tree_view->priv->search_entry_changed_id = 0;
11841     }
11842   if (tree_view->priv->typeselect_flush_timeout)
11843     {
11844       g_source_remove (tree_view->priv->typeselect_flush_timeout);
11845       tree_view->priv->typeselect_flush_timeout = 0;
11846     }
11847         
11848   if (gtk_widget_get_visible (search_dialog))
11849     {
11850       /* send focus-in event */
11851       send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11852       gtk_widget_hide (search_dialog);
11853       gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11854       send_focus_change (GTK_WIDGET (tree_view), TRUE);
11855     }
11856 }
11857
11858 static void
11859 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11860                                     GtkWidget   *search_dialog,
11861                                     gpointer     user_data)
11862 {
11863   gint x, y;
11864   gint tree_x, tree_y;
11865   gint tree_width, tree_height;
11866   GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
11867   GdkScreen *screen = gdk_window_get_screen (tree_window);
11868   GtkRequisition requisition;
11869   gint monitor_num;
11870   GdkRectangle monitor;
11871
11872   monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11873   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11874
11875   gtk_widget_realize (search_dialog);
11876
11877   gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11878   tree_width = gdk_window_get_width (tree_window);
11879   tree_height = gdk_window_get_height (tree_window);
11880
11881   gtk_widget_size_request (search_dialog, &requisition);
11882
11883   if (tree_x + tree_width > gdk_screen_get_width (screen))
11884     x = gdk_screen_get_width (screen) - requisition.width;
11885   else if (tree_x + tree_width - requisition.width < 0)
11886     x = 0;
11887   else
11888     x = tree_x + tree_width - requisition.width;
11889
11890   if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11891     y = gdk_screen_get_height (screen) - requisition.height;
11892   else if (tree_y + tree_height < 0) /* isn't really possible ... */
11893     y = 0;
11894   else
11895     y = tree_y + tree_height;
11896
11897   gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11898 }
11899
11900 static void
11901 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11902                                       GtkMenu  *menu,
11903                                       gpointer  data)
11904 {
11905   PsppSheetView *tree_view = (PsppSheetView *)data;
11906
11907   tree_view->priv->disable_popdown = 1;
11908   g_signal_connect (menu, "hide",
11909                     G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11910 }
11911
11912 #if GTK3_TRANSITION
11913 /* Because we're visible but offscreen, we just set a flag in the preedit
11914  * callback.
11915  */
11916 static void
11917 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11918                                       PsppSheetView  *tree_view)
11919 {
11920   tree_view->priv->imcontext_changed = 1;
11921   if (tree_view->priv->typeselect_flush_timeout)
11922     {
11923       g_source_remove (tree_view->priv->typeselect_flush_timeout);
11924       tree_view->priv->typeselect_flush_timeout =
11925         gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11926                        (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11927                        tree_view);
11928     }
11929
11930 }
11931 #endif
11932
11933 static void
11934 pspp_sheet_view_search_activate (GtkEntry    *entry,
11935                                PsppSheetView *tree_view)
11936 {
11937   GtkTreePath *path;
11938   int node;
11939
11940   pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11941                                     tree_view);
11942
11943   /* If we have a row selected and it's the cursor row, we activate
11944    * the row XXX */
11945   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11946     {
11947       path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11948       
11949       _pspp_sheet_view_find_node (tree_view, path, &node);
11950       
11951       if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11952         pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11953       
11954       gtk_tree_path_free (path);
11955     }
11956 }
11957
11958 static gboolean
11959 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11960 {
11961   PsppSheetView *tree_view = (PsppSheetView *)data;
11962
11963   tree_view->priv->disable_popdown = 0;
11964
11965   return FALSE;
11966 }
11967
11968 static void
11969 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11970                                      gpointer   data)
11971 {
11972   gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11973 }
11974
11975 static gboolean
11976 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11977                                    GdkEventAny *event,
11978                                    PsppSheetView *tree_view)
11979 {
11980   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11981
11982   pspp_sheet_view_search_dialog_hide (widget, tree_view);
11983
11984   return TRUE;
11985 }
11986
11987 static gboolean
11988 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11989                                          GdkEventButton *event,
11990                                          PsppSheetView *tree_view)
11991 {
11992   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11993
11994   pspp_sheet_view_search_dialog_hide (widget, tree_view);
11995
11996   if (event->window == tree_view->priv->bin_window)
11997     pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
11998
11999   return TRUE;
12000 }
12001
12002 static gboolean
12003 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
12004                                    GdkEventScroll *event,
12005                                    PsppSheetView *tree_view)
12006 {
12007   gboolean retval = FALSE;
12008
12009   if (event->direction == GDK_SCROLL_UP)
12010     {
12011       pspp_sheet_view_search_move (widget, tree_view, TRUE);
12012       retval = TRUE;
12013     }
12014   else if (event->direction == GDK_SCROLL_DOWN)
12015     {
12016       pspp_sheet_view_search_move (widget, tree_view, FALSE);
12017       retval = TRUE;
12018     }
12019
12020   /* renew the flush timeout */
12021   if (retval && tree_view->priv->typeselect_flush_timeout
12022       && !tree_view->priv->search_custom_entry_set)
12023     {
12024       g_source_remove (tree_view->priv->typeselect_flush_timeout);
12025       tree_view->priv->typeselect_flush_timeout =
12026         gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12027                        (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12028                        tree_view);
12029     }
12030
12031   return retval;
12032 }
12033
12034 static gboolean
12035 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
12036                                       GdkEventKey *event,
12037                                       PsppSheetView *tree_view)
12038 {
12039   gboolean retval = FALSE;
12040
12041   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
12042   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12043
12044   /* close window and cancel the search */
12045   if (!tree_view->priv->search_custom_entry_set
12046       && (event->keyval == GDK_Escape ||
12047           event->keyval == GDK_Tab ||
12048             event->keyval == GDK_KP_Tab ||
12049             event->keyval == GDK_ISO_Left_Tab))
12050     {
12051       pspp_sheet_view_search_dialog_hide (widget, tree_view);
12052       return TRUE;
12053     }
12054
12055   /* select previous matching iter */
12056   if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
12057     {
12058       if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
12059         gtk_widget_error_bell (widget);
12060
12061       retval = TRUE;
12062     }
12063
12064   if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
12065       && (event->keyval == GDK_g || event->keyval == GDK_G))
12066     {
12067       if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
12068         gtk_widget_error_bell (widget);
12069
12070       retval = TRUE;
12071     }
12072
12073   /* select next matching iter */
12074   if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
12075     {
12076       if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
12077         gtk_widget_error_bell (widget);
12078
12079       retval = TRUE;
12080     }
12081
12082   if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
12083       && (event->keyval == GDK_g || event->keyval == GDK_G))
12084     {
12085       if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
12086         gtk_widget_error_bell (widget);
12087
12088       retval = TRUE;
12089     }
12090
12091   /* renew the flush timeout */
12092   if (retval && tree_view->priv->typeselect_flush_timeout
12093       && !tree_view->priv->search_custom_entry_set)
12094     {
12095       g_source_remove (tree_view->priv->typeselect_flush_timeout);
12096       tree_view->priv->typeselect_flush_timeout =
12097         gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12098                        (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12099                        tree_view);
12100     }
12101
12102   return retval;
12103 }
12104
12105 /*  this function returns FALSE if there is a search string but
12106  *  nothing was found, and TRUE otherwise.
12107  */
12108 static gboolean
12109 pspp_sheet_view_search_move (GtkWidget   *window,
12110                            PsppSheetView *tree_view,
12111                            gboolean     up)
12112 {
12113   gboolean ret;
12114   gint len;
12115   gint count = 0;
12116   const gchar *text;
12117   GtkTreeIter iter;
12118   GtkTreeModel *model;
12119   PsppSheetSelection *selection;
12120
12121   text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
12122
12123   g_return_val_if_fail (text != NULL, FALSE);
12124
12125   len = strlen (text);
12126
12127   if (up && tree_view->priv->selected_iter == 1)
12128     return strlen (text) < 1;
12129
12130   len = strlen (text);
12131
12132   if (len < 1)
12133     return TRUE;
12134
12135   model = pspp_sheet_view_get_model (tree_view);
12136   selection = pspp_sheet_view_get_selection (tree_view);
12137
12138   /* search */
12139   pspp_sheet_selection_unselect_all (selection);
12140   if (!gtk_tree_model_get_iter_first (model, &iter))
12141     return TRUE;
12142
12143   ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
12144                                    &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
12145
12146   if (ret)
12147     {
12148       /* found */
12149       tree_view->priv->selected_iter += up?(-1):(1);
12150       return TRUE;
12151     }
12152   else
12153     {
12154       /* return to old iter */
12155       count = 0;
12156       gtk_tree_model_get_iter_first (model, &iter);
12157       pspp_sheet_view_search_iter (model, selection,
12158                                  &iter, text,
12159                                  &count, tree_view->priv->selected_iter);
12160       return FALSE;
12161     }
12162 }
12163
12164 static gboolean
12165 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
12166                                  gint          column,
12167                                  const gchar  *key,
12168                                  GtkTreeIter  *iter,
12169                                  gpointer      search_data)
12170 {
12171   gboolean retval = TRUE;
12172   const gchar *str;
12173   gchar *normalized_string;
12174   gchar *normalized_key;
12175   gchar *case_normalized_string = NULL;
12176   gchar *case_normalized_key = NULL;
12177   GValue value = {0,};
12178   GValue transformed = {0,};
12179
12180   gtk_tree_model_get_value (model, iter, column, &value);
12181
12182   g_value_init (&transformed, G_TYPE_STRING);
12183
12184   if (!g_value_transform (&value, &transformed))
12185     {
12186       g_value_unset (&value);
12187       return TRUE;
12188     }
12189
12190   g_value_unset (&value);
12191
12192   str = g_value_get_string (&transformed);
12193   if (!str)
12194     {
12195       g_value_unset (&transformed);
12196       return TRUE;
12197     }
12198
12199   normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
12200   normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
12201
12202   if (normalized_string && normalized_key)
12203     {
12204       case_normalized_string = g_utf8_casefold (normalized_string, -1);
12205       case_normalized_key = g_utf8_casefold (normalized_key, -1);
12206
12207       if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
12208         retval = FALSE;
12209     }
12210
12211   g_value_unset (&transformed);
12212   g_free (normalized_key);
12213   g_free (normalized_string);
12214   g_free (case_normalized_key);
12215   g_free (case_normalized_string);
12216
12217   return retval;
12218 }
12219
12220 static gboolean
12221 pspp_sheet_view_search_iter (GtkTreeModel     *model,
12222                              PsppSheetSelection *selection,
12223                              GtkTreeIter      *iter,
12224                              const gchar      *text,
12225                              gint             *count,
12226                              gint              n)
12227 {
12228   int node = -1;
12229   GtkTreePath *path;
12230
12231   PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
12232
12233   path = gtk_tree_model_get_path (model, iter);
12234   _pspp_sheet_view_find_node (tree_view, path, &node);
12235
12236   do
12237     {
12238       gboolean done = FALSE;
12239
12240       if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
12241         {
12242           (*count)++;
12243           if (*count == n)
12244             {
12245               pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
12246                                               TRUE, 0.5, 0.0);
12247               pspp_sheet_selection_select_iter (selection, iter);
12248               pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12249
12250               if (path)
12251                 gtk_tree_path_free (path);
12252
12253               return TRUE;
12254             }
12255         }
12256
12257
12258       do
12259         {
12260           node = pspp_sheet_view_node_next (tree_view, node);
12261
12262           if (node >= 0)
12263             {
12264               gboolean has_next;
12265
12266               has_next = gtk_tree_model_iter_next (model, iter);
12267
12268               done = TRUE;
12269               gtk_tree_path_next (path);
12270
12271               /* sanity check */
12272               TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12273             }
12274           else
12275             {
12276               if (path)
12277                 gtk_tree_path_free (path);
12278
12279               /* we've run out of tree, done with this func */
12280               return FALSE;
12281             }
12282         }
12283       while (!done);
12284     }
12285   while (1);
12286
12287   return FALSE;
12288 }
12289
12290 static void
12291 pspp_sheet_view_search_init (GtkWidget   *entry,
12292                            PsppSheetView *tree_view)
12293 {
12294   gint ret;
12295   gint count = 0;
12296   const gchar *text;
12297   GtkTreeIter iter;
12298   GtkTreeModel *model;
12299   PsppSheetSelection *selection;
12300
12301   g_return_if_fail (GTK_IS_ENTRY (entry));
12302   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12303
12304   text = gtk_entry_get_text (GTK_ENTRY (entry));
12305
12306   model = pspp_sheet_view_get_model (tree_view);
12307   selection = pspp_sheet_view_get_selection (tree_view);
12308
12309   /* search */
12310   pspp_sheet_selection_unselect_all (selection);
12311   if (tree_view->priv->typeselect_flush_timeout
12312       && !tree_view->priv->search_custom_entry_set)
12313     {
12314       g_source_remove (tree_view->priv->typeselect_flush_timeout);
12315       tree_view->priv->typeselect_flush_timeout =
12316         gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12317                        (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12318                        tree_view);
12319     }
12320
12321   if (*text == '\0')
12322     return;
12323
12324   if (!gtk_tree_model_get_iter_first (model, &iter))
12325     return;
12326
12327   ret = pspp_sheet_view_search_iter (model, selection,
12328                                    &iter, text,
12329                                    &count, 1);
12330
12331   if (ret)
12332     tree_view->priv->selected_iter = 1;
12333 }
12334
12335 static void
12336 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12337                              PsppSheetView     *tree_view)
12338 {
12339   if (tree_view->priv->edited_column == NULL)
12340     return;
12341
12342   _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12343   tree_view->priv->edited_column = NULL;
12344
12345   if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12346     gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12347
12348   g_signal_handlers_disconnect_by_func (cell_editable,
12349                                         pspp_sheet_view_remove_widget,
12350                                         tree_view);
12351   g_signal_handlers_disconnect_by_func (cell_editable,
12352                                         pspp_sheet_view_editable_button_press_event,
12353                                         tree_view);
12354   g_signal_handlers_disconnect_by_func (cell_editable,
12355                                         pspp_sheet_view_editable_clicked,
12356                                         tree_view);
12357
12358   gtk_container_remove (GTK_CONTAINER (tree_view),
12359                         GTK_WIDGET (cell_editable));  
12360
12361   /* FIXME should only redraw a single node */
12362   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12363 }
12364
12365 static gboolean
12366 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12367                              GtkTreePath *cursor_path)
12368 {
12369   GtkTreeIter iter;
12370   GdkRectangle background_area;
12371   GdkRectangle cell_area;
12372   GtkCellEditable *editable_widget = NULL;
12373   gchar *path_string;
12374   guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12375   gint retval = FALSE;
12376   int cursor_node;
12377
12378   g_assert (tree_view->priv->focus_column);
12379
12380   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12381     return FALSE;
12382
12383   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12384   if (cursor_node < 0)
12385     return FALSE;
12386
12387   path_string = gtk_tree_path_to_string (cursor_path);
12388   gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12389
12390   pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12391                                            tree_view->priv->model,
12392                                            &iter);
12393   pspp_sheet_view_get_background_area (tree_view,
12394                                      cursor_path,
12395                                      tree_view->priv->focus_column,
12396                                      &background_area);
12397   pspp_sheet_view_get_cell_area (tree_view,
12398                                cursor_path,
12399                                tree_view->priv->focus_column,
12400                                &cell_area);
12401
12402   if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12403                                         &editable_widget,
12404                                         NULL,
12405                                         path_string,
12406                                         &background_area,
12407                                         &cell_area,
12408                                         flags))
12409     {
12410       retval = TRUE;
12411       if (editable_widget != NULL)
12412         {
12413           gint left, right;
12414           GdkRectangle area;
12415           GtkCellRenderer *cell;
12416
12417           area = cell_area;
12418           cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12419
12420           _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12421
12422           area.x += left;
12423           area.width -= right + left;
12424
12425           pspp_sheet_view_real_start_editing (tree_view,
12426                                             tree_view->priv->focus_column,
12427                                             cursor_path,
12428                                             editable_widget,
12429                                             &area,
12430                                             NULL,
12431                                             flags);
12432         }
12433
12434     }
12435   g_free (path_string);
12436   return retval;
12437 }
12438
12439 static gboolean
12440 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12441                                              GdkEventButton *event,
12442                                              PsppSheetView *sheet_view)
12443 {
12444   gint node;
12445
12446   node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12447                                              "pspp-sheet-view-node"));
12448   return pspp_sheet_view_row_head_clicked (sheet_view,
12449                                            node,
12450                                            sheet_view->priv->edited_column,
12451                                            event);
12452 }
12453
12454 static void
12455 pspp_sheet_view_editable_clicked (GtkButton *button,
12456                                   PsppSheetView *sheet_view)
12457 {
12458   pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12459                                                sheet_view);
12460 }
12461
12462 static gboolean
12463 is_all_selected (GtkWidget *widget)
12464 {
12465   GtkEntryBuffer *buffer;
12466   gint start_pos, end_pos;
12467
12468   if (!GTK_IS_ENTRY (widget))
12469     return FALSE;
12470
12471   buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12472   return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12473                                              &start_pos, &end_pos)
12474           && start_pos == 0
12475           && end_pos == gtk_entry_buffer_get_length (buffer));
12476 }
12477
12478 static gboolean
12479 is_at_left (GtkWidget *widget)
12480 {
12481   return (GTK_IS_ENTRY (widget)
12482           && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12483 }
12484
12485 static gboolean
12486 is_at_right (GtkWidget *widget)
12487 {
12488   GtkEntryBuffer *buffer;
12489   gint length;
12490
12491   if (!GTK_IS_ENTRY (widget))
12492     return FALSE;
12493
12494   buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12495   length = gtk_entry_buffer_get_length (buffer);
12496   return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12497 }
12498
12499 static gboolean
12500 pspp_sheet_view_event (GtkWidget *widget,
12501                        GdkEventKey *event,
12502                        PsppSheetView *tree_view)
12503 {
12504   PsppSheetViewColumn *column;
12505   GtkTreePath *path;
12506   gboolean handled;
12507   gboolean cancel;
12508   guint keyval;
12509   gint row;
12510
12511   /* Intercept only key press events.
12512      It would make sense to use "key-press-event" instead of "event", but
12513      GtkEntry attaches its own signal handler to "key-press-event" that runs
12514      before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12515   */
12516   if (event->type != GDK_KEY_PRESS)
12517     return FALSE;
12518
12519   keyval = event->keyval;
12520   cancel = FALSE;
12521   switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12522     {
12523     case 0:
12524       switch (event->keyval)
12525         {
12526         case GDK_Left:      case GDK_KP_Left:
12527         case GDK_Home:      case GDK_KP_Home:
12528           if (!is_all_selected (widget) && !is_at_left (widget))
12529             return FALSE;
12530           break;
12531
12532         case GDK_Right:     case GDK_KP_Right:
12533         case GDK_End:       case GDK_KP_End:
12534           if (!is_all_selected (widget) && !is_at_right (widget))
12535             return FALSE;
12536           break;
12537
12538         case GDK_Up:        case GDK_KP_Up:
12539         case GDK_Down:      case GDK_KP_Down:
12540           break;
12541
12542         case GDK_Page_Up:   case GDK_KP_Page_Up:
12543         case GDK_Page_Down: case GDK_KP_Page_Down:
12544           break;
12545
12546         case GDK_Escape:
12547           cancel = TRUE;
12548           break;
12549
12550         case GDK_Return:
12551           keyval = GDK_Down;
12552           break;
12553
12554         case GDK_Tab:       case GDK_KP_Tab:
12555         case GDK_ISO_Left_Tab:
12556           keyval = GDK_Tab;
12557           break;
12558
12559         default:
12560           return FALSE;
12561         }
12562       break;
12563
12564     case GDK_SHIFT_MASK:
12565       switch (event->keyval)
12566         {
12567         case GDK_Tab:
12568         case GDK_ISO_Left_Tab:
12569           keyval = GDK_Tab;
12570           break;
12571
12572         default:
12573           return FALSE;
12574         }
12575       break;
12576
12577     case GDK_CONTROL_MASK:
12578       switch (event->keyval)
12579         {
12580         case GDK_Left:      case GDK_KP_Left:
12581           if (!is_all_selected (widget) && !is_at_left (widget))
12582             return FALSE;
12583           break;
12584
12585         case GDK_Right:     case GDK_KP_Right:
12586           if (!is_all_selected (widget) && !is_at_right (widget))
12587             return FALSE;
12588           break;
12589
12590         case GDK_Up:        case GDK_KP_Up:
12591         case GDK_Down:      case GDK_KP_Down:
12592           break;
12593
12594         default:
12595           return FALSE;
12596         }
12597       break;
12598
12599     default:
12600       return FALSE;
12601     }
12602
12603   row = tree_view->priv->edited_row;
12604   column = tree_view->priv->edited_column;
12605   path = gtk_tree_path_new_from_indices (row, -1);
12606
12607   pspp_sheet_view_stop_editing (tree_view, cancel);
12608   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12609
12610   pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12611   gtk_tree_path_free (path);
12612
12613   handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12614                                       G_OBJECT (tree_view));
12615   if (handled)
12616     g_signal_stop_emission_by_name (widget, "event");
12617
12618   pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12619   pspp_sheet_view_start_editing (tree_view, path);
12620   gtk_tree_path_free (path);
12621
12622   return handled;
12623 }
12624
12625 static void
12626 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12627                                           gpointer data)
12628 {
12629   PsppSheetView *sheet_view = data;
12630
12631   g_signal_connect (widget, "event",
12632                     G_CALLBACK (pspp_sheet_view_event),
12633                     sheet_view);
12634
12635   if (GTK_IS_CONTAINER (widget))
12636     gtk_container_foreach (GTK_CONTAINER (widget),
12637                            pspp_sheet_view_override_cell_keypresses,
12638                            data);
12639 }
12640
12641 static void
12642 pspp_sheet_view_real_start_editing (PsppSheetView       *tree_view,
12643                                   PsppSheetViewColumn *column,
12644                                   GtkTreePath       *path,
12645                                   GtkCellEditable   *cell_editable,
12646                                   GdkRectangle      *cell_area,
12647                                   GdkEvent          *event,
12648                                   guint              flags)
12649 {
12650   PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12651   gint pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
12652   GtkRequisition requisition;
12653   gint row;
12654
12655   g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12656
12657   tree_view->priv->edited_column = column;
12658   _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12659
12660   row = gtk_tree_path_get_indices (path)[0];
12661   tree_view->priv->edited_row = row;
12662   pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12663   cell_area->y += pre_val - (int)gtk_adjustment_get_value (tree_view->priv->vadjustment);
12664
12665   pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12666   pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12667   tree_view->priv->anchor_column = column;
12668
12669   gtk_widget_size_request (GTK_WIDGET (cell_editable), &requisition);
12670
12671   PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12672
12673   if (requisition.height < cell_area->height)
12674     {
12675       gint diff = cell_area->height - requisition.height;
12676       pspp_sheet_view_put (tree_view,
12677                          GTK_WIDGET (cell_editable),
12678                          cell_area->x, cell_area->y + diff/2,
12679                          cell_area->width, requisition.height);
12680     }
12681   else
12682     {
12683       pspp_sheet_view_put (tree_view,
12684                          GTK_WIDGET (cell_editable),
12685                          cell_area->x, cell_area->y,
12686                          cell_area->width, cell_area->height);
12687     }
12688
12689   gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12690                                    (GdkEvent *)event);
12691
12692   gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12693   g_signal_connect (cell_editable, "remove-widget",
12694                     G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12695   if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12696       GTK_IS_BUTTON (cell_editable))
12697     {
12698       g_signal_connect (cell_editable, "button-press-event",
12699                         G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12700                         tree_view);
12701       g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12702                          GINT_TO_POINTER (row));
12703       g_signal_connect (cell_editable, "clicked",
12704                         G_CALLBACK (pspp_sheet_view_editable_clicked),
12705                         tree_view);
12706     }
12707
12708   pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12709                                             tree_view);
12710 }
12711
12712 void
12713 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12714                               gboolean     cancel_editing)
12715 {
12716   PsppSheetViewColumn *column;
12717   GtkCellRenderer *cell;
12718
12719   if (tree_view->priv->edited_column == NULL)
12720     return;
12721
12722   /*
12723    * This is very evil. We need to do this, because
12724    * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12725    * later on. If pspp_sheet_view_row_changed notices
12726    * tree_view->priv->edited_column != NULL, it'll call
12727    * pspp_sheet_view_stop_editing again. Bad things will happen then.
12728    *
12729    * Please read that again if you intend to modify anything here.
12730    */
12731
12732   column = tree_view->priv->edited_column;
12733   tree_view->priv->edited_column = NULL;
12734
12735   cell = _pspp_sheet_view_column_get_edited_cell (column);
12736   gtk_cell_renderer_stop_editing (cell, cancel_editing);
12737
12738   if (!cancel_editing)
12739     gtk_cell_editable_editing_done (column->editable_widget);
12740
12741   tree_view->priv->edited_column = column;
12742
12743   gtk_cell_editable_remove_widget (column->editable_widget);
12744 }
12745
12746
12747 /**
12748  * pspp_sheet_view_set_hover_selection:
12749  * @tree_view: a #PsppSheetView
12750  * @hover: %TRUE to enable hover selection mode
12751  *
12752  * Enables of disables the hover selection mode of @tree_view.
12753  * Hover selection makes the selected row follow the pointer.
12754  * Currently, this works only for the selection modes 
12755  * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12756  * 
12757  * Since: 2.6
12758  **/
12759 void     
12760 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12761                                    gboolean     hover)
12762 {
12763   hover = hover != FALSE;
12764
12765   if (hover != tree_view->priv->hover_selection)
12766     {
12767       tree_view->priv->hover_selection = hover;
12768
12769       g_object_notify (G_OBJECT (tree_view), "hover-selection");
12770     }
12771 }
12772
12773 /**
12774  * pspp_sheet_view_get_hover_selection:
12775  * @tree_view: a #PsppSheetView
12776  * 
12777  * Returns whether hover selection mode is turned on for @tree_view.
12778  * 
12779  * Return value: %TRUE if @tree_view is in hover selection mode
12780  *
12781  * Since: 2.6 
12782  **/
12783 gboolean 
12784 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12785 {
12786   return tree_view->priv->hover_selection;
12787 }
12788
12789 /**
12790  * pspp_sheet_view_set_rubber_banding:
12791  * @tree_view: a #PsppSheetView
12792  * @enable: %TRUE to enable rubber banding
12793  *
12794  * Enables or disables rubber banding in @tree_view.  If the selection mode is
12795  * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12796  * banding will allow the user to select multiple rows by dragging the mouse.
12797  * 
12798  * Since: 2.10
12799  **/
12800 void
12801 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12802                                   gboolean     enable)
12803 {
12804   enable = enable != FALSE;
12805
12806   if (enable != tree_view->priv->rubber_banding_enable)
12807     {
12808       tree_view->priv->rubber_banding_enable = enable;
12809
12810       g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12811     }
12812 }
12813
12814 /**
12815  * pspp_sheet_view_get_rubber_banding:
12816  * @tree_view: a #PsppSheetView
12817  * 
12818  * Returns whether rubber banding is turned on for @tree_view.  If the
12819  * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12820  * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12821  * select multiple rows by dragging the mouse.
12822  * 
12823  * Return value: %TRUE if rubber banding in @tree_view is enabled.
12824  *
12825  * Since: 2.10
12826  **/
12827 gboolean
12828 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12829 {
12830   return tree_view->priv->rubber_banding_enable;
12831 }
12832
12833 /**
12834  * pspp_sheet_view_is_rubber_banding_active:
12835  * @tree_view: a #PsppSheetView
12836  * 
12837  * Returns whether a rubber banding operation is currently being done
12838  * in @tree_view.
12839  *
12840  * Return value: %TRUE if a rubber banding operation is currently being
12841  * done in @tree_view.
12842  *
12843  * Since: 2.12
12844  **/
12845 gboolean
12846 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12847 {
12848   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12849
12850   if (tree_view->priv->rubber_banding_enable
12851       && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12852     return TRUE;
12853
12854   return FALSE;
12855 }
12856
12857 static void
12858 pspp_sheet_view_grab_notify (GtkWidget *widget,
12859                            gboolean   was_grabbed)
12860 {
12861   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12862
12863   tree_view->priv->in_grab = !was_grabbed;
12864
12865   if (!was_grabbed)
12866     {
12867       tree_view->priv->pressed_button = -1;
12868
12869       if (tree_view->priv->rubber_band_status)
12870         pspp_sheet_view_stop_rubber_band (tree_view);
12871     }
12872 }
12873
12874 static void
12875 pspp_sheet_view_state_changed (GtkWidget      *widget,
12876                              GtkStateType    previous_state)
12877 {
12878   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12879
12880   if (gtk_widget_get_realized (widget))
12881     {
12882       GtkStyle *style = gtk_widget_get_style (widget);
12883       gdk_window_set_background (tree_view->priv->bin_window, &style->base[gtk_widget_get_state (widget)]);
12884     }
12885
12886   gtk_widget_queue_draw (widget);
12887 }
12888
12889 /**
12890  * pspp_sheet_view_get_grid_lines:
12891  * @tree_view: a #PsppSheetView
12892  *
12893  * Returns which grid lines are enabled in @tree_view.
12894  *
12895  * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12896  * are enabled.
12897  *
12898  * Since: 2.10
12899  */
12900 PsppSheetViewGridLines
12901 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12902 {
12903   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12904
12905   return tree_view->priv->grid_lines;
12906 }
12907
12908 /**
12909  * pspp_sheet_view_set_grid_lines:
12910  * @tree_view: a #PsppSheetView
12911  * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12912  * enable.
12913  *
12914  * Sets which grid lines to draw in @tree_view.
12915  *
12916  * Since: 2.10
12917  */
12918 void
12919 pspp_sheet_view_set_grid_lines (PsppSheetView           *tree_view,
12920                               PsppSheetViewGridLines   grid_lines)
12921 {
12922   PsppSheetViewPrivate *priv;
12923   PsppSheetViewGridLines old_grid_lines;
12924
12925   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12926
12927   priv = tree_view->priv;
12928
12929   old_grid_lines = priv->grid_lines;
12930   priv->grid_lines = grid_lines;
12931   
12932   if (old_grid_lines != grid_lines)
12933     {
12934       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12935       
12936       g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12937     }
12938 }
12939
12940 /**
12941  * pspp_sheet_view_get_special_cells:
12942  * @tree_view: a #PsppSheetView
12943  *
12944  * Returns which grid lines are enabled in @tree_view.
12945  *
12946  * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12947  * the sheet view contain special cells.
12948  */
12949 PsppSheetViewSpecialCells
12950 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12951 {
12952   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12953
12954   return tree_view->priv->special_cells;
12955 }
12956
12957 /**
12958  * pspp_sheet_view_set_special_cells:
12959  * @tree_view: a #PsppSheetView
12960  * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12961  * the sheet view contain special cells.
12962  *
12963  * Sets whether rows in the sheet view contain special cells, controlling the
12964  * rendering of row selections.
12965  */
12966 void
12967 pspp_sheet_view_set_special_cells (PsppSheetView           *tree_view,
12968                               PsppSheetViewSpecialCells   special_cells)
12969 {
12970   PsppSheetViewPrivate *priv;
12971
12972   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12973
12974   priv = tree_view->priv;
12975
12976   if (priv->special_cells != special_cells)
12977     {
12978       priv->special_cells = special_cells;
12979       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12980       g_object_notify (G_OBJECT (tree_view), "special-cells");
12981     }
12982 }
12983
12984 int
12985 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12986 {
12987   /* XXX (re)calculate fixed_height if necessary */
12988   return tree_view->priv->fixed_height;
12989 }
12990
12991 void
12992 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
12993                                   int fixed_height)
12994 {
12995   g_return_if_fail (fixed_height > 0);
12996
12997   if (tree_view->priv->fixed_height != fixed_height)
12998     {
12999       tree_view->priv->fixed_height = fixed_height;
13000       g_object_notify (G_OBJECT (tree_view), "fixed-height");
13001     }
13002   if (!tree_view->priv->fixed_height_set)
13003     {
13004       tree_view->priv->fixed_height_set = TRUE;
13005       g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
13006     }
13007 }
13008
13009 /**
13010  * pspp_sheet_view_set_tooltip_row:
13011  * @tree_view: a #PsppSheetView
13012  * @tooltip: a #GtkTooltip
13013  * @path: a #GtkTreePath
13014  *
13015  * Sets the tip area of @tooltip to be the area covered by the row at @path.
13016  * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13017  * See also gtk_tooltip_set_tip_area().
13018  *
13019  * Since: 2.12
13020  */
13021 void
13022 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
13023                                GtkTooltip  *tooltip,
13024                                GtkTreePath *path)
13025 {
13026   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13027   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
13028
13029   pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
13030 }
13031
13032 /**
13033  * pspp_sheet_view_set_tooltip_cell:
13034  * @tree_view: a #PsppSheetView
13035  * @tooltip: a #GtkTooltip
13036  * @path: (allow-none): a #GtkTreePath or %NULL
13037  * @column: (allow-none): a #PsppSheetViewColumn or %NULL
13038  * @cell: (allow-none): a #GtkCellRenderer or %NULL
13039  *
13040  * Sets the tip area of @tooltip to the area @path, @column and @cell have
13041  * in common.  For example if @path is %NULL and @column is set, the tip
13042  * area will be set to the full area covered by @column.  See also
13043  * gtk_tooltip_set_tip_area().
13044  *
13045  * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13046  *
13047  * Since: 2.12
13048  */
13049 void
13050 pspp_sheet_view_set_tooltip_cell (PsppSheetView       *tree_view,
13051                                 GtkTooltip        *tooltip,
13052                                 GtkTreePath       *path,
13053                                 PsppSheetViewColumn *column,
13054                                 GtkCellRenderer   *cell)
13055 {
13056   GdkRectangle rect;
13057
13058   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13059   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
13060   g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
13061   g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
13062
13063   /* Determine x values. */
13064   if (column && cell)
13065     {
13066       GdkRectangle tmp;
13067       gint start, width;
13068
13069       pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
13070       pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
13071
13072       pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13073                                                          tmp.x + start, 0,
13074                                                          &rect.x, NULL);
13075       rect.width = width;
13076     }
13077   else if (column)
13078     {
13079       GdkRectangle tmp;
13080
13081       pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
13082       pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13083                                                          tmp.x, 0,
13084                                                          &rect.x, NULL);
13085       rect.width = tmp.width;
13086     }
13087   else
13088     {
13089       GtkAllocation allocation;
13090       gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
13091       rect.x = 0;
13092       rect.width = allocation.width;
13093     }
13094
13095   /* Determine y values. */
13096   if (path)
13097     {
13098       GdkRectangle tmp;
13099
13100       pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
13101       pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13102                                                          0, tmp.y,
13103                                                          NULL, &rect.y);
13104       rect.height = tmp.height;
13105     }
13106   else
13107     {
13108       rect.y = 0;
13109       rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
13110     }
13111
13112   gtk_tooltip_set_tip_area (tooltip, &rect);
13113 }
13114
13115 /**
13116  * pspp_sheet_view_get_tooltip_context:
13117  * @tree_view: a #PsppSheetView
13118  * @x: the x coordinate (relative to widget coordinates)
13119  * @y: the y coordinate (relative to widget coordinates)
13120  * @keyboard_tip: whether this is a keyboard tooltip or not
13121  * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
13122  * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
13123  * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
13124  *
13125  * This function is supposed to be used in a #GtkWidget::query-tooltip
13126  * signal handler for #PsppSheetView.  The @x, @y and @keyboard_tip values
13127  * which are received in the signal handler, should be passed to this
13128  * function without modification.
13129  *
13130  * The return value indicates whether there is a tree view row at the given
13131  * coordinates (%TRUE) or not (%FALSE) for mouse tooltips.  For keyboard
13132  * tooltips the row returned will be the cursor row.  When %TRUE, then any of
13133  * @model, @path and @iter which have been provided will be set to point to
13134  * that row and the corresponding model.  @x and @y will always be converted
13135  * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
13136  *
13137  * Return value: whether or not the given tooltip context points to a row.
13138  *
13139  * Since: 2.12
13140  */
13141 gboolean
13142 pspp_sheet_view_get_tooltip_context (PsppSheetView   *tree_view,
13143                                    gint          *x,
13144                                    gint          *y,
13145                                    gboolean       keyboard_tip,
13146                                    GtkTreeModel **model,
13147                                    GtkTreePath  **path,
13148                                    GtkTreeIter   *iter)
13149 {
13150   GtkTreePath *tmppath = NULL;
13151
13152   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
13153   g_return_val_if_fail (x != NULL, FALSE);
13154   g_return_val_if_fail (y != NULL, FALSE);
13155
13156   if (keyboard_tip)
13157     {
13158       pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
13159
13160       if (!tmppath)
13161         return FALSE;
13162     }
13163   else
13164     {
13165       pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
13166                                                          x, y);
13167
13168       if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
13169                                           &tmppath, NULL, NULL, NULL))
13170         return FALSE;
13171     }
13172
13173   if (model)
13174     *model = pspp_sheet_view_get_model (tree_view);
13175
13176   if (iter)
13177     gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
13178                              iter, tmppath);
13179
13180   if (path)
13181     *path = tmppath;
13182   else
13183     gtk_tree_path_free (tmppath);
13184
13185   return TRUE;
13186 }
13187
13188 static gboolean
13189 pspp_sheet_view_set_tooltip_query_cb (GtkWidget  *widget,
13190                                     gint        x,
13191                                     gint        y,
13192                                     gboolean    keyboard_tip,
13193                                     GtkTooltip *tooltip,
13194                                     gpointer    data)
13195 {
13196   GValue value = { 0, };
13197   GValue transformed = { 0, };
13198   GtkTreeIter iter;
13199   GtkTreePath *path;
13200   GtkTreeModel *model;
13201   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
13202
13203   if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
13204                                           &x, &y,
13205                                           keyboard_tip,
13206                                           &model, &path, &iter))
13207     return FALSE;
13208
13209   gtk_tree_model_get_value (model, &iter,
13210                             tree_view->priv->tooltip_column, &value);
13211
13212   g_value_init (&transformed, G_TYPE_STRING);
13213
13214   if (!g_value_transform (&value, &transformed))
13215     {
13216       g_value_unset (&value);
13217       gtk_tree_path_free (path);
13218
13219       return FALSE;
13220     }
13221
13222   g_value_unset (&value);
13223
13224   if (!g_value_get_string (&transformed))
13225     {
13226       g_value_unset (&transformed);
13227       gtk_tree_path_free (path);
13228
13229       return FALSE;
13230     }
13231
13232   gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
13233   pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
13234
13235   gtk_tree_path_free (path);
13236   g_value_unset (&transformed);
13237
13238   return TRUE;
13239 }
13240
13241 /**
13242  * pspp_sheet_view_set_tooltip_column:
13243  * @tree_view: a #PsppSheetView
13244  * @column: an integer, which is a valid column number for @tree_view's model
13245  *
13246  * If you only plan to have simple (text-only) tooltips on full rows, you
13247  * can use this function to have #PsppSheetView handle these automatically
13248  * for you. @column should be set to the column in @tree_view's model
13249  * containing the tooltip texts, or -1 to disable this feature.
13250  *
13251  * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
13252  * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
13253  *
13254  * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
13255  * so &amp;, &lt;, etc have to be escaped in the text.
13256  *
13257  * Since: 2.12
13258  */
13259 void
13260 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
13261                                   gint         column)
13262 {
13263   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13264
13265   if (column == tree_view->priv->tooltip_column)
13266     return;
13267
13268   if (column == -1)
13269     {
13270       g_signal_handlers_disconnect_by_func (tree_view,
13271                                             pspp_sheet_view_set_tooltip_query_cb,
13272                                             NULL);
13273       gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13274     }
13275   else
13276     {
13277       if (tree_view->priv->tooltip_column == -1)
13278         {
13279           g_signal_connect (tree_view, "query-tooltip",
13280                             G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13281           gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13282         }
13283     }
13284
13285   tree_view->priv->tooltip_column = column;
13286   g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13287 }
13288
13289 /**
13290  * pspp_sheet_view_get_tooltip_column:
13291  * @tree_view: a #PsppSheetView
13292  *
13293  * Returns the column of @tree_view's model which is being used for
13294  * displaying tooltips on @tree_view's rows.
13295  *
13296  * Return value: the index of the tooltip column that is currently being
13297  * used, or -1 if this is disabled.
13298  *
13299  * Since: 2.12
13300  */
13301 gint
13302 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13303 {
13304   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13305
13306   return tree_view->priv->tooltip_column;
13307 }
13308
13309 gboolean
13310 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13311                                   GValue                *return_accu,
13312                                   const GValue          *handler_return,
13313                                   gpointer               dummy)
13314 {
13315   gboolean continue_emission;
13316   gboolean signal_handled;
13317   
13318   signal_handled = g_value_get_boolean (handler_return);
13319   g_value_set_boolean (return_accu, signal_handled);
13320   continue_emission = !signal_handled;
13321   
13322   return continue_emission;
13323 }
13324
13325
13326 GType
13327 pspp_sheet_view_grid_lines_get_type (void)
13328 {
13329     static GType etype = 0;
13330     if (G_UNLIKELY(etype == 0)) {
13331         static const GEnumValue values[] = {
13332             { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13333             { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13334             { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13335             { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13336             { 0, NULL, NULL }
13337         };
13338         etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13339     }
13340     return etype;
13341 }
13342
13343 GType
13344 pspp_sheet_view_special_cells_get_type (void)
13345 {
13346     static GType etype = 0;
13347     if (G_UNLIKELY(etype == 0)) {
13348         static const GEnumValue values[] = {
13349             { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13350             { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13351             { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13352             { 0, NULL, NULL }
13353         };
13354         etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);
13355     }
13356     return etype;
13357 }