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