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