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