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