38c609669c4f292412278e4852c7b16d27406db0
[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->visible &&
6716               pspp_sheet_view_column_can_focus (column))
6717             {
6718               pspp_sheet_view_column_set_need_button (column, TRUE);
6719               if (column->button)
6720                 {
6721                   pspp_sheet_view_focus_column (tree_view, column,
6722                                                 clamp_column_visible);
6723                   return TRUE;
6724                 }
6725             }
6726         }
6727       return FALSE;
6728
6729     default:
6730       g_assert_not_reached ();
6731       break;
6732     }
6733
6734   return FALSE;
6735 }
6736
6737 /* This function returns in 'path' the first focusable path, if the given path
6738  * is already focusable, it's the returned one.
6739  *
6740  */
6741 static gboolean
6742 search_first_focusable_path (PsppSheetView  *tree_view,
6743                              GtkTreePath **path,
6744                              gboolean      search_forward,
6745                              int *new_node)
6746 {
6747   /* XXX this function is trivial given that the sheetview doesn't support
6748      separator rows */
6749   int node = -1;
6750
6751   if (!path || !*path)
6752     return FALSE;
6753
6754   _pspp_sheet_view_find_node (tree_view, *path, &node);
6755
6756   if (node < 0)
6757     return FALSE;
6758
6759   if (new_node)
6760     *new_node = node;
6761
6762   return (*path != NULL);
6763 }
6764
6765 static gint
6766 pspp_sheet_view_focus (GtkWidget        *widget,
6767                      GtkDirectionType  direction)
6768 {
6769   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6770   GtkContainer *container = GTK_CONTAINER (widget);
6771   GtkWidget *focus_child;
6772
6773   if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6774     return FALSE;
6775
6776   focus_child = container->focus_child;
6777
6778   pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6779   /* Case 1.  Headers currently have focus. */
6780   if (focus_child)
6781     {
6782       switch (direction)
6783         {
6784         case GTK_DIR_LEFT:
6785         case GTK_DIR_RIGHT:
6786           pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6787           return TRUE;
6788         case GTK_DIR_TAB_BACKWARD:
6789         case GTK_DIR_UP:
6790           return FALSE;
6791         case GTK_DIR_TAB_FORWARD:
6792         case GTK_DIR_DOWN:
6793           gtk_widget_grab_focus (widget);
6794           return TRUE;
6795         default:
6796           g_assert_not_reached ();
6797           return FALSE;
6798         }
6799     }
6800
6801   /* Case 2. We don't have focus at all. */
6802   if (!gtk_widget_has_focus (widget))
6803     {
6804       if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6805         gtk_widget_grab_focus (widget);
6806       return TRUE;
6807     }
6808
6809   /* Case 3. We have focus already. */
6810   if (direction == GTK_DIR_TAB_BACKWARD)
6811     return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6812   else if (direction == GTK_DIR_TAB_FORWARD)
6813     return FALSE;
6814
6815   /* Other directions caught by the keybindings */
6816   gtk_widget_grab_focus (widget);
6817   return TRUE;
6818 }
6819
6820 static void
6821 pspp_sheet_view_grab_focus (GtkWidget *widget)
6822 {
6823   GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6824
6825   pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6826 }
6827
6828 static void
6829 pspp_sheet_view_style_set (GtkWidget *widget,
6830                          GtkStyle *previous_style)
6831 {
6832   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6833   GList *list;
6834   PsppSheetViewColumn *column;
6835
6836   if (gtk_widget_get_realized (widget))
6837     {
6838       gint i;
6839       PsppSheetViewPrivate *priv = PSPP_SHEET_VIEW (widget)->priv;
6840
6841       gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
6842       gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
6843       gtk_style_set_background (widget->style, tree_view->priv->header_window, GTK_STATE_NORMAL);
6844       for (i = 0; i < 5 ; ++i)
6845         {
6846           g_object_unref (priv->grid_line_gc[i]);
6847           priv->grid_line_gc[i] = gdk_gc_new (widget->window);
6848           gdk_gc_copy (priv->grid_line_gc[i], widget->style->text_aa_gc[i]);
6849         }
6850
6851       pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6852     }
6853
6854   gtk_widget_style_get (widget,
6855                         "expander-size", &tree_view->priv->expander_size,
6856                         NULL);
6857   tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6858
6859   for (list = tree_view->priv->columns; list; list = list->next)
6860     {
6861       column = list->data;
6862       _pspp_sheet_view_column_cell_set_dirty (column);
6863     }
6864
6865   tree_view->priv->fixed_height = -1;
6866
6867   /* Invalidate cached button style. */
6868   if (tree_view->priv->button_style)
6869     {
6870       g_object_unref (tree_view->priv->button_style);
6871       tree_view->priv->button_style = NULL;
6872     }
6873
6874   gtk_widget_queue_resize (widget);
6875 }
6876
6877
6878 static void
6879 pspp_sheet_view_set_focus_child (GtkContainer *container,
6880                                GtkWidget    *child)
6881 {
6882   PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6883   GList *list;
6884
6885   for (list = tree_view->priv->columns; list; list = list->next)
6886     {
6887       if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
6888         {
6889           tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
6890           break;
6891         }
6892     }
6893
6894   GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
6895 }
6896
6897 static void
6898 pspp_sheet_view_set_adjustments (PsppSheetView   *tree_view,
6899                                GtkAdjustment *hadj,
6900                                GtkAdjustment *vadj)
6901 {
6902   gboolean need_adjust = FALSE;
6903
6904   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
6905
6906   if (hadj)
6907     g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
6908   else
6909     hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6910   if (vadj)
6911     g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
6912   else
6913     vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6914
6915   if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
6916     {
6917       g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
6918                                             pspp_sheet_view_adjustment_changed,
6919                                             tree_view);
6920       g_object_unref (tree_view->priv->hadjustment);
6921     }
6922
6923   if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
6924     {
6925       g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
6926                                             pspp_sheet_view_adjustment_changed,
6927                                             tree_view);
6928       g_object_unref (tree_view->priv->vadjustment);
6929     }
6930
6931   if (tree_view->priv->hadjustment != hadj)
6932     {
6933       tree_view->priv->hadjustment = hadj;
6934       g_object_ref_sink (tree_view->priv->hadjustment);
6935
6936       g_signal_connect (tree_view->priv->hadjustment, "value-changed",
6937                         G_CALLBACK (pspp_sheet_view_adjustment_changed),
6938                         tree_view);
6939       need_adjust = TRUE;
6940     }
6941
6942   if (tree_view->priv->vadjustment != vadj)
6943     {
6944       tree_view->priv->vadjustment = vadj;
6945       g_object_ref_sink (tree_view->priv->vadjustment);
6946
6947       g_signal_connect (tree_view->priv->vadjustment, "value-changed",
6948                         G_CALLBACK (pspp_sheet_view_adjustment_changed),
6949                         tree_view);
6950       need_adjust = TRUE;
6951     }
6952
6953   if (need_adjust)
6954     pspp_sheet_view_adjustment_changed (NULL, tree_view);
6955 }
6956
6957
6958 static gboolean
6959 pspp_sheet_view_real_move_cursor (PsppSheetView       *tree_view,
6960                                 GtkMovementStep    step,
6961                                 gint               count)
6962 {
6963   GdkModifierType state;
6964
6965   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
6966   g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
6967                         step == GTK_MOVEMENT_VISUAL_POSITIONS ||
6968                         step == GTK_MOVEMENT_DISPLAY_LINES ||
6969                         step == GTK_MOVEMENT_PAGES ||
6970                         step == GTK_MOVEMENT_BUFFER_ENDS, FALSE);
6971
6972   if (tree_view->priv->row_count == 0)
6973     return FALSE;
6974   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
6975     return FALSE;
6976
6977   pspp_sheet_view_stop_editing (tree_view, FALSE);
6978   PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
6979   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
6980
6981   if (gtk_get_current_event_state (&state))
6982     {
6983       if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
6984         tree_view->priv->ctrl_pressed = TRUE;
6985       if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
6986         tree_view->priv->shift_pressed = TRUE;
6987     }
6988   /* else we assume not pressed */
6989
6990   switch (step)
6991     {
6992     case GTK_MOVEMENT_LOGICAL_POSITIONS:
6993       pspp_sheet_view_move_cursor_tab (tree_view, count);
6994       break;
6995     case GTK_MOVEMENT_VISUAL_POSITIONS:
6996       pspp_sheet_view_move_cursor_left_right (tree_view, count);
6997       break;
6998     case GTK_MOVEMENT_DISPLAY_LINES:
6999       pspp_sheet_view_move_cursor_up_down (tree_view, count);
7000       break;
7001     case GTK_MOVEMENT_PAGES:
7002       pspp_sheet_view_move_cursor_page_up_down (tree_view, count);
7003       break;
7004     case GTK_MOVEMENT_BUFFER_ENDS:
7005       pspp_sheet_view_move_cursor_start_end (tree_view, count);
7006       break;
7007     default:
7008       g_assert_not_reached ();
7009     }
7010
7011   tree_view->priv->ctrl_pressed = FALSE;
7012   tree_view->priv->shift_pressed = FALSE;
7013
7014   return TRUE;
7015 }
7016
7017 static void
7018 pspp_sheet_view_put (PsppSheetView *tree_view,
7019                    GtkWidget   *child_widget,
7020                    /* in bin_window coordinates */
7021                    gint         x,
7022                    gint         y,
7023                    gint         width,
7024                    gint         height)
7025 {
7026   PsppSheetViewChild *child;
7027   
7028   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7029   g_return_if_fail (GTK_IS_WIDGET (child_widget));
7030
7031   child = g_slice_new (PsppSheetViewChild);
7032
7033   child->widget = child_widget;
7034   child->x = x;
7035   child->y = y;
7036   child->width = width;
7037   child->height = height;
7038
7039   tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7040
7041   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7042     gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7043   
7044   gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7045 }
7046
7047 void
7048 _pspp_sheet_view_child_move_resize (PsppSheetView *tree_view,
7049                                   GtkWidget   *widget,
7050                                   /* in tree coordinates */
7051                                   gint         x,
7052                                   gint         y,
7053                                   gint         width,
7054                                   gint         height)
7055 {
7056   PsppSheetViewChild *child = NULL;
7057   GList *list;
7058   GdkRectangle allocation;
7059
7060   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7061   g_return_if_fail (GTK_IS_WIDGET (widget));
7062
7063   for (list = tree_view->priv->children; list; list = list->next)
7064     {
7065       if (((PsppSheetViewChild *)list->data)->widget == widget)
7066         {
7067           child = list->data;
7068           break;
7069         }
7070     }
7071   if (child == NULL)
7072     return;
7073
7074   allocation.x = child->x = x;
7075   allocation.y = child->y = y;
7076   allocation.width = child->width = width;
7077   allocation.height = child->height = height;
7078
7079   if (gtk_widget_get_realized (widget))
7080     gtk_widget_size_allocate (widget, &allocation);
7081 }
7082
7083
7084 /* TreeModel Callbacks
7085  */
7086
7087 static void
7088 pspp_sheet_view_row_changed (GtkTreeModel *model,
7089                            GtkTreePath  *path,
7090                            GtkTreeIter  *iter,
7091                            gpointer      data)
7092 {
7093   PsppSheetView *tree_view = (PsppSheetView *)data;
7094   int node;
7095   gboolean free_path = FALSE;
7096   GtkTreePath *cursor_path;
7097
7098   g_return_if_fail (path != NULL || iter != NULL);
7099
7100   if (tree_view->priv->cursor != NULL)
7101     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7102   else
7103     cursor_path = NULL;
7104
7105   if (tree_view->priv->edited_column &&
7106       (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7107     pspp_sheet_view_stop_editing (tree_view, TRUE);
7108
7109   if (cursor_path != NULL)
7110     gtk_tree_path_free (cursor_path);
7111
7112   if (path == NULL)
7113     {
7114       path = gtk_tree_model_get_path (model, iter);
7115       free_path = TRUE;
7116     }
7117   else if (iter == NULL)
7118     gtk_tree_model_get_iter (model, iter, path);
7119
7120   _pspp_sheet_view_find_node (tree_view,
7121                               path,
7122                               &node);
7123
7124   if (node >= 0)
7125     {
7126       if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7127         pspp_sheet_view_node_queue_redraw (tree_view, node);
7128     }
7129   
7130   if (free_path)
7131     gtk_tree_path_free (path);
7132 }
7133
7134 static void
7135 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7136                             GtkTreePath  *path,
7137                             GtkTreeIter  *iter,
7138                             gpointer      data)
7139 {
7140   PsppSheetView *tree_view = (PsppSheetView *) data;
7141   gint *indices;
7142   int tmpnode = -1;
7143   gint height = tree_view->priv->fixed_height;
7144   gboolean free_path = FALSE;
7145   gboolean node_visible = TRUE;
7146
7147   g_return_if_fail (path != NULL || iter != NULL);
7148
7149   if (path == NULL)
7150     {
7151       path = gtk_tree_model_get_path (model, iter);
7152       free_path = TRUE;
7153     }
7154   else if (iter == NULL)
7155     gtk_tree_model_get_iter (model, iter, path);
7156
7157   tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7158
7159   /* Update all row-references */
7160   gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7161   indices = gtk_tree_path_get_indices (path);
7162   tmpnode = indices[0];
7163
7164   range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7165
7166   if (height > 0)
7167     {
7168       if (node_visible && node_is_visible (tree_view, tmpnode))
7169         gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7170       else
7171         gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7172     }
7173   else
7174     install_presize_handler (tree_view);
7175   if (free_path)
7176     gtk_tree_path_free (path);
7177 }
7178
7179 static void
7180 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7181                            GtkTreePath  *path,
7182                            gpointer      data)
7183 {
7184   PsppSheetView *tree_view = (PsppSheetView *)data;
7185   int node;
7186
7187   g_return_if_fail (path != NULL);
7188
7189   gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7190
7191   _pspp_sheet_view_find_node (tree_view, path, &node);
7192
7193   if (node < 0)
7194     return;
7195
7196   range_tower_delete (tree_view->priv->selected, node, 1);
7197
7198   /* Ensure we don't have a dangling pointer to a dead node */
7199   ensure_unprelighted (tree_view);
7200
7201   /* Cancel editting if we've started */
7202   pspp_sheet_view_stop_editing (tree_view, TRUE);
7203
7204   if (tree_view->priv->destroy_count_func)
7205     {
7206       gint child_count = 0;
7207       tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7208     }
7209
7210   tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7211
7212   if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7213     {
7214       gtk_tree_row_reference_free (tree_view->priv->top_row);
7215       tree_view->priv->top_row = NULL;
7216     }
7217
7218   install_scroll_sync_handler (tree_view);
7219
7220   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7221
7222 #if 0
7223   if (helper_data.changed)
7224     g_signal_emit_by_name (tree_view->priv->selection, "changed");
7225 #endif
7226 }
7227
7228 static void
7229 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7230                               GtkTreePath  *parent,
7231                               GtkTreeIter  *iter,
7232                               gint         *new_order,
7233                               gpointer      data)
7234 {
7235   PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7236   gint len;
7237
7238   /* XXX need to adjust selection */
7239   len = gtk_tree_model_iter_n_children (model, iter);
7240
7241   if (len < 2)
7242     return;
7243
7244   gtk_tree_row_reference_reordered (G_OBJECT (data),
7245                                     parent,
7246                                     iter,
7247                                     new_order);
7248
7249   if (gtk_tree_path_get_depth (parent) != 0)
7250     return;
7251
7252   if (tree_view->priv->edited_column)
7253     pspp_sheet_view_stop_editing (tree_view, TRUE);
7254
7255   /* we need to be unprelighted */
7256   ensure_unprelighted (tree_view);
7257
7258   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7259
7260   pspp_sheet_view_dy_to_top_row (tree_view);
7261 }
7262
7263
7264 /* Internal tree functions
7265  */
7266
7267
7268 static void
7269 pspp_sheet_view_get_background_xrange (PsppSheetView       *tree_view,
7270                                      PsppSheetViewColumn *column,
7271                                      gint              *x1,
7272                                      gint              *x2)
7273 {
7274   PsppSheetViewColumn *tmp_column = NULL;
7275   gint total_width;
7276   GList *list;
7277   gboolean rtl;
7278
7279   if (x1)
7280     *x1 = 0;
7281
7282   if (x2)
7283     *x2 = 0;
7284
7285   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7286
7287   total_width = 0;
7288   for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7289        list;
7290        list = (rtl ? list->prev : list->next))
7291     {
7292       tmp_column = list->data;
7293
7294       if (tmp_column == column)
7295         break;
7296
7297       if (tmp_column->visible)
7298         total_width += tmp_column->width;
7299     }
7300
7301   if (tmp_column != column)
7302     {
7303       g_warning (G_STRLOC": passed-in column isn't in the tree");
7304       return;
7305     }
7306
7307   if (x1)
7308     *x1 = total_width;
7309
7310   if (x2)
7311     {
7312       if (column->visible)
7313         *x2 = total_width + column->width;
7314       else
7315         *x2 = total_width; /* width of 0 */
7316     }
7317 }
7318
7319 /* Make sure the node is visible vertically */
7320 static void
7321 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7322                                     int node)
7323 {
7324   gint node_dy, height;
7325   GtkTreePath *path = NULL;
7326
7327   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7328     return;
7329
7330   /* just return if the node is visible, avoiding a costly expose */
7331   node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7332   height = ROW_HEIGHT (tree_view);
7333   if (node_dy >= tree_view->priv->vadjustment->value
7334       && node_dy + height <= (tree_view->priv->vadjustment->value
7335                               + tree_view->priv->vadjustment->page_size))
7336     return;
7337
7338   path = _pspp_sheet_view_find_path (tree_view, node);
7339   if (path)
7340     {
7341       /* We process updates because we want to clear old selected items when we scroll.
7342        * if this is removed, we get a "selection streak" at the bottom. */
7343       gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7344       pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7345       gtk_tree_path_free (path);
7346     }
7347 }
7348
7349 static void
7350 pspp_sheet_view_clamp_column_visible (PsppSheetView       *tree_view,
7351                                     PsppSheetViewColumn *column,
7352                                     gboolean           focus_to_cell)
7353 {
7354   gint x, width;
7355
7356   if (column == NULL)
7357     return;
7358
7359   x = column->allocation.x;
7360   width = column->allocation.width;
7361
7362   if (width > tree_view->priv->hadjustment->page_size)
7363     {
7364       /* The column is larger than the horizontal page size.  If the
7365        * column has cells which can be focussed individually, then we make
7366        * sure the cell which gets focus is fully visible (if even the
7367        * focus cell is bigger than the page size, we make sure the
7368        * left-hand side of the cell is visible).
7369        *
7370        * If the column does not have those so-called special cells, we
7371        * make sure the left-hand side of the column is visible.
7372        */
7373
7374       if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7375         {
7376           GtkTreePath *cursor_path;
7377           GdkRectangle background_area, cell_area, focus_area;
7378
7379           cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7380
7381           pspp_sheet_view_get_cell_area (tree_view,
7382                                        cursor_path, column, &cell_area);
7383           pspp_sheet_view_get_background_area (tree_view,
7384                                              cursor_path, column,
7385                                              &background_area);
7386
7387           gtk_tree_path_free (cursor_path);
7388
7389           _pspp_sheet_view_column_get_focus_area (column,
7390                                                 &background_area,
7391                                                 &cell_area,
7392                                                 &focus_area);
7393
7394           x = focus_area.x;
7395           width = focus_area.width;
7396
7397           if (width < tree_view->priv->hadjustment->page_size)
7398             {
7399               if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) < (x + width))
7400                 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7401                                           x + width - tree_view->priv->hadjustment->page_size);
7402               else if (tree_view->priv->hadjustment->value > x)
7403                 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7404             }
7405         }
7406
7407       gtk_adjustment_set_value (tree_view->priv->hadjustment,
7408                                 CLAMP (x,
7409                                        tree_view->priv->hadjustment->lower,
7410                                        tree_view->priv->hadjustment->upper
7411                                        - tree_view->priv->hadjustment->page_size));
7412     }
7413   else
7414     {
7415       if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) < (x + width))
7416           gtk_adjustment_set_value (tree_view->priv->hadjustment,
7417                                     x + width - tree_view->priv->hadjustment->page_size);
7418       else if (tree_view->priv->hadjustment->value > x)
7419         gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7420   }
7421 }
7422
7423 GtkTreePath *
7424 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7425                             int node)
7426 {
7427   GtkTreePath *path;
7428
7429   path = gtk_tree_path_new ();
7430   if (node >= 0)
7431     gtk_tree_path_append_index (path, node);
7432   return path;
7433 }
7434
7435 void
7436 _pspp_sheet_view_find_node (PsppSheetView  *tree_view,
7437                           GtkTreePath  *path,
7438                           int *node)
7439 {
7440   gint *indices = gtk_tree_path_get_indices (path);
7441   gint depth = gtk_tree_path_get_depth (path);
7442
7443   *node = -1;
7444   if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7445     return;
7446   *node = indices[0];
7447 }
7448
7449 static void
7450 pspp_sheet_view_add_move_binding (GtkBindingSet  *binding_set,
7451                                 guint           keyval,
7452                                 guint           modmask,
7453                                 gboolean        add_shifted_binding,
7454                                 GtkMovementStep step,
7455                                 gint            count)
7456 {
7457   
7458   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7459                                 "move-cursor", 2,
7460                                 G_TYPE_ENUM, step,
7461                                 G_TYPE_INT, count);
7462
7463   if (add_shifted_binding)
7464     gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7465                                   "move-cursor", 2,
7466                                   G_TYPE_ENUM, step,
7467                                   G_TYPE_INT, count);
7468
7469   if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7470    return;
7471
7472   gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7473                                 "move-cursor", 2,
7474                                 G_TYPE_ENUM, step,
7475                                 G_TYPE_INT, count);
7476
7477   gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7478                                 "move-cursor", 2,
7479                                 G_TYPE_ENUM, step,
7480                                 G_TYPE_INT, count);
7481 }
7482
7483 static void
7484 pspp_sheet_view_set_column_drag_info (PsppSheetView       *tree_view,
7485                                     PsppSheetViewColumn *column)
7486 {
7487   PsppSheetViewColumn *left_column;
7488   PsppSheetViewColumn *cur_column = NULL;
7489   PsppSheetViewColumnReorder *reorder;
7490   gboolean rtl;
7491   GList *tmp_list;
7492   gint left;
7493
7494   /* We want to precalculate the motion list such that we know what column slots
7495    * are available.
7496    */
7497   left_column = NULL;
7498   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7499
7500   /* First, identify all possible drop spots */
7501   if (rtl)
7502     tmp_list = g_list_last (tree_view->priv->columns);
7503   else
7504     tmp_list = g_list_first (tree_view->priv->columns);
7505
7506   while (tmp_list)
7507     {
7508       cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7509       tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7510
7511       if (cur_column->visible == FALSE)
7512         continue;
7513
7514       /* If it's not the column moving and func tells us to skip over the column, we continue. */
7515       if (left_column != column && cur_column != column &&
7516           tree_view->priv->column_drop_func &&
7517           ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7518         {
7519           left_column = cur_column;
7520           continue;
7521         }
7522       reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7523       reorder->left_column = left_column;
7524       left_column = reorder->right_column = cur_column;
7525
7526       tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7527     }
7528
7529   /* Add the last one */
7530   if (tree_view->priv->column_drop_func == NULL ||
7531       ((left_column != column) &&
7532        tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7533     {
7534       reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7535       reorder->left_column = left_column;
7536       reorder->right_column = NULL;
7537       tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7538     }
7539
7540   /* We quickly check to see if it even makes sense to reorder columns. */
7541   /* If there is nothing that can be moved, then we return */
7542
7543   if (tree_view->priv->column_drag_info == NULL)
7544     return;
7545
7546   /* We know there are always 2 slots possbile, as you can always return column. */
7547   /* If that's all there is, return */
7548   if (tree_view->priv->column_drag_info->next == NULL || 
7549       (tree_view->priv->column_drag_info->next->next == NULL &&
7550        ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7551        ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7552     {
7553       for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7554         g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7555       g_list_free (tree_view->priv->column_drag_info);
7556       tree_view->priv->column_drag_info = NULL;
7557       return;
7558     }
7559   /* We fill in the ranges for the columns, now that we've isolated them */
7560   left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7561
7562   for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7563     {
7564       reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7565
7566       reorder->left_align = left;
7567       if (tmp_list->next != NULL)
7568         {
7569           g_assert (tmp_list->next->data);
7570           left = reorder->right_align = (reorder->right_column->allocation.x +
7571                                          reorder->right_column->allocation.width +
7572                                          ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7573         }
7574       else
7575         {
7576           gint width;
7577
7578           gdk_drawable_get_size (tree_view->priv->header_window, &width, NULL);
7579           reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7580         }
7581     }
7582 }
7583
7584 void
7585 _pspp_sheet_view_column_start_drag (PsppSheetView       *tree_view,
7586                                   PsppSheetViewColumn *column)
7587 {
7588   GdkEvent *send_event;
7589   GtkAllocation allocation;
7590   gint x, y, width, height;
7591   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7592   GdkDisplay *display = gdk_screen_get_display (screen);
7593
7594   g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7595   g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7596   g_return_if_fail (column->button);
7597
7598   pspp_sheet_view_set_column_drag_info (tree_view, column);
7599
7600   if (tree_view->priv->column_drag_info == NULL)
7601     return;
7602
7603   if (tree_view->priv->drag_window == NULL)
7604     {
7605       GdkWindowAttr attributes;
7606       guint attributes_mask;
7607
7608       attributes.window_type = GDK_WINDOW_CHILD;
7609       attributes.wclass = GDK_INPUT_OUTPUT;
7610       attributes.x = column->allocation.x;
7611       attributes.y = 0;
7612       attributes.width = column->allocation.width;
7613       attributes.height = column->allocation.height;
7614       attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7615       attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
7616       attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7617       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
7618
7619       tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7620                                                      &attributes,
7621                                                      attributes_mask);
7622       gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7623     }
7624
7625   gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7626   gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7627
7628   gtk_grab_remove (column->button);
7629
7630   send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7631   send_event->crossing.send_event = TRUE;
7632   send_event->crossing.window = g_object_ref (GTK_BUTTON (column->button)->event_window);
7633   send_event->crossing.subwindow = NULL;
7634   send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7635   send_event->crossing.time = GDK_CURRENT_TIME;
7636
7637   gtk_propagate_event (column->button, send_event);
7638   gdk_event_free (send_event);
7639
7640   send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7641   send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7642   send_event->button.send_event = TRUE;
7643   send_event->button.time = GDK_CURRENT_TIME;
7644   send_event->button.x = -1;
7645   send_event->button.y = -1;
7646   send_event->button.axes = NULL;
7647   send_event->button.state = 0;
7648   send_event->button.button = 1;
7649   send_event->button.device = gdk_display_get_core_pointer (display);
7650   send_event->button.x_root = 0;
7651   send_event->button.y_root = 0;
7652
7653   gtk_propagate_event (column->button, send_event);
7654   gdk_event_free (send_event);
7655
7656   /* Kids, don't try this at home */
7657   g_object_ref (column->button);
7658   gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7659   gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7660   gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7661   g_object_unref (column->button);
7662
7663   tree_view->priv->drag_column_x = column->allocation.x;
7664   allocation = column->allocation;
7665   allocation.x = 0;
7666   gtk_widget_size_allocate (column->button, &allocation);
7667   gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7668
7669   tree_view->priv->drag_column = column;
7670   gdk_window_show (tree_view->priv->drag_window);
7671
7672   gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7673   gdk_drawable_get_size (tree_view->priv->header_window, &width, &height);
7674
7675   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7676   while (gtk_events_pending ())
7677     gtk_main_iteration ();
7678
7679   PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7680   gdk_pointer_grab (tree_view->priv->drag_window,
7681                     FALSE,
7682                     GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7683                     NULL, NULL, GDK_CURRENT_TIME);
7684   gdk_keyboard_grab (tree_view->priv->drag_window,
7685                      FALSE,
7686                      GDK_CURRENT_TIME);
7687 }
7688
7689 void
7690 _pspp_sheet_view_queue_draw_node (PsppSheetView        *tree_view,
7691                                 int node,
7692                                 const GdkRectangle *clip_rect)
7693 {
7694   GdkRectangle rect;
7695
7696   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7697     return;
7698
7699   rect.x = 0;
7700   rect.width = MAX (tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.width);
7701
7702   rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7703   rect.height = ROW_HEIGHT (tree_view);
7704
7705   if (clip_rect)
7706     {
7707       GdkRectangle new_rect;
7708
7709       gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7710
7711       gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7712     }
7713   else
7714     {
7715       gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7716     }
7717 }
7718
7719 static void
7720 pspp_sheet_view_queue_draw_path (PsppSheetView        *tree_view,
7721                                GtkTreePath        *path,
7722                                const GdkRectangle *clip_rect)
7723 {
7724   int node = -1;
7725
7726   _pspp_sheet_view_find_node (tree_view, path, &node);
7727
7728   if (node)
7729     _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7730 }
7731
7732 static void
7733 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7734
7735 {
7736   GtkTreePath *cursor_path;
7737
7738   if ((tree_view->priv->row_count == 0) ||
7739       (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7740     return;
7741
7742   cursor_path = NULL;
7743   if (tree_view->priv->cursor)
7744     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7745
7746   if (cursor_path == NULL)
7747     {
7748       /* There's no cursor.  Move the cursor to the first selected row, if any
7749        * are selected, otherwise to the first row in the sheetview.
7750        */
7751       GList *selected_rows;
7752       GtkTreeModel *model;
7753       PsppSheetSelection *selection;
7754
7755       selection = pspp_sheet_view_get_selection (tree_view);
7756       selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7757
7758       if (selected_rows)
7759         {
7760           /* XXX we could avoid doing O(n) work to get this result */
7761           cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7762           g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7763           g_list_free (selected_rows);
7764         }
7765       else
7766         {
7767           cursor_path = gtk_tree_path_new_first ();
7768           search_first_focusable_path (tree_view, &cursor_path,
7769                                        TRUE, NULL);
7770         }
7771
7772       gtk_tree_row_reference_free (tree_view->priv->cursor);
7773       tree_view->priv->cursor = NULL;
7774
7775       if (cursor_path)
7776         {
7777           if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7778               tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7779             pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE);
7780           else
7781             pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
7782         }
7783     }
7784
7785   if (cursor_path)
7786     {
7787       /* Now find a column for the cursor. */
7788       PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7789
7790       pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7791       gtk_tree_path_free (cursor_path);
7792
7793       if (tree_view->priv->focus_column == NULL)
7794         {
7795           GList *list;
7796           for (list = tree_view->priv->columns; list; list = list->next)
7797             {
7798               if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7799                 {
7800                   tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7801                   pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7802                   pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7803                   break;
7804                 }
7805             }
7806
7807         }
7808     }
7809 }
7810
7811 static gboolean
7812 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7813                                    gint         count)
7814 {
7815   gint selection_count;
7816   int cursor_node = -1;
7817   int new_cursor_node = -1;
7818   GtkTreePath *cursor_path = NULL;
7819   gboolean grab_focus = TRUE;
7820
7821   if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7822     return FALSE;
7823
7824   cursor_path = NULL;
7825   if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7826     /* FIXME: we lost the cursor; should we get the first? */
7827     return FALSE;
7828
7829   cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7830   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7831
7832   if (cursor_node < 0)
7833     /* FIXME: we lost the cursor; should we get the first? */
7834     return FALSE;
7835
7836   selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7837
7838   if (selection_count == 0
7839       && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7840       && !tree_view->priv->ctrl_pressed)
7841     {
7842       /* Don't move the cursor, but just select the current node */
7843       new_cursor_node = cursor_node;
7844     }
7845   else
7846     {
7847       if (count == -1)
7848         new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7849       else
7850         new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7851     }
7852
7853   gtk_tree_path_free (cursor_path);
7854
7855   if (new_cursor_node)
7856     {
7857       cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7858
7859       search_first_focusable_path (tree_view, &cursor_path,
7860                                    (count != -1),
7861                                    &new_cursor_node);
7862
7863       if (cursor_path)
7864         gtk_tree_path_free (cursor_path);
7865     }
7866
7867   /*
7868    * If the list has only one item and multi-selection is set then select
7869    * the row (if not yet selected).
7870    */
7871   if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7872        tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7873       new_cursor_node < 0)
7874     {
7875       if (count == -1)
7876         new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7877       else
7878         new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7879
7880       if (new_cursor_node < 0
7881           && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
7882         {
7883           new_cursor_node = cursor_node;
7884         }
7885       else
7886         {
7887           new_cursor_node = -1;
7888         }
7889     }
7890
7891   if (new_cursor_node >= 0)
7892     {
7893       cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7894       pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE);
7895       gtk_tree_path_free (cursor_path);
7896     }
7897   else
7898     {
7899       pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7900
7901       if (!tree_view->priv->shift_pressed)
7902         {
7903           if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
7904                                           count < 0 ?
7905                                           GTK_DIR_UP : GTK_DIR_DOWN))
7906             {
7907               GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
7908
7909               if (toplevel)
7910                 gtk_widget_child_focus (toplevel,
7911                                         count < 0 ?
7912                                         GTK_DIR_TAB_BACKWARD :
7913                                         GTK_DIR_TAB_FORWARD);
7914
7915               grab_focus = FALSE;
7916             }
7917         }
7918       else
7919         {
7920           gtk_widget_error_bell (GTK_WIDGET (tree_view));
7921         }
7922     }
7923
7924   if (grab_focus)
7925     gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7926
7927   return new_cursor_node >= 0;
7928 }
7929
7930 static void
7931 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
7932                                         gint         count)
7933 {
7934   int cursor_node = -1;
7935   GtkTreePath *old_cursor_path = NULL;
7936   GtkTreePath *cursor_path = NULL;
7937   int start_cursor_node = -1;
7938   gint y;
7939   gint window_y;
7940   gint vertical_separator;
7941
7942   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7943     return;
7944
7945   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
7946     old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7947   else
7948     /* This is sorta weird.  Focus in should give us a cursor */
7949     return;
7950
7951   gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
7952   _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
7953
7954   if (cursor_node < 0)
7955     {
7956       /* FIXME: we lost the cursor.  Should we try to get one? */
7957       gtk_tree_path_free (old_cursor_path);
7958       return;
7959     }
7960
7961   y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7962   window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
7963   y += tree_view->priv->cursor_offset;
7964   y += count * (int)tree_view->priv->vadjustment->page_increment;
7965   y = CLAMP (y, (gint)tree_view->priv->vadjustment->lower,  (gint)tree_view->priv->vadjustment->upper - vertical_separator);
7966
7967   if (y >= tree_view->priv->height)
7968     y = tree_view->priv->height - 1;
7969
7970   tree_view->priv->cursor_offset =
7971     pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
7972
7973   if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
7974     {
7975       cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7976       tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
7977     }
7978
7979   y -= tree_view->priv->cursor_offset;
7980   cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7981
7982   start_cursor_node = cursor_node;
7983
7984   if (! search_first_focusable_path (tree_view, &cursor_path,
7985                                      (count != -1),
7986                                      &cursor_node))
7987     {
7988       /* It looks like we reached the end of the view without finding
7989        * a focusable row.  We will step backwards to find the last
7990        * focusable row.
7991        */
7992       cursor_node = start_cursor_node;
7993       cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7994
7995       search_first_focusable_path (tree_view, &cursor_path,
7996                                    (count == -1),
7997                                    &cursor_node);
7998     }
7999
8000   if (!cursor_path)
8001     goto cleanup;
8002
8003   /* update y */
8004   y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8005
8006   pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
8007
8008   y -= window_y;
8009   pspp_sheet_view_scroll_to_point (tree_view, -1, y);
8010   pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8011   _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8012
8013   if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
8014     gtk_widget_error_bell (GTK_WIDGET (tree_view));
8015
8016   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8017
8018 cleanup:
8019   gtk_tree_path_free (old_cursor_path);
8020   gtk_tree_path_free (cursor_path);
8021 }
8022
8023 static void
8024 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8025                                       gint         count)
8026 {
8027   int cursor_node = -1;
8028   GtkTreePath *cursor_path = NULL;
8029   PsppSheetViewColumn *column;
8030   GtkTreeIter iter;
8031   GList *list;
8032   gboolean found_column = FALSE;
8033   gboolean rtl;
8034
8035   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8036
8037   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8038     return;
8039
8040   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8041     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8042   else
8043     return;
8044
8045   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8046   if (cursor_node < 0)
8047     return;
8048   if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8049     {
8050       gtk_tree_path_free (cursor_path);
8051       return;
8052     }
8053   gtk_tree_path_free (cursor_path);
8054
8055   list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8056   if (tree_view->priv->focus_column)
8057     {
8058       for (; list; list = (rtl ? list->prev : list->next))
8059         {
8060           if (list->data == tree_view->priv->focus_column)
8061             break;
8062         }
8063     }
8064
8065   while (list)
8066     {
8067       gboolean left, right;
8068
8069       column = list->data;
8070       if (column->visible == FALSE || column->row_head)
8071         goto loop_end;
8072
8073       pspp_sheet_view_column_cell_set_cell_data (column,
8074                                                tree_view->priv->model,
8075                                                &iter);
8076
8077       if (rtl)
8078         {
8079           right = list->prev ? TRUE : FALSE;
8080           left = list->next ? TRUE : FALSE;
8081         }
8082       else
8083         {
8084           left = list->prev ? TRUE : FALSE;
8085           right = list->next ? TRUE : FALSE;
8086         }
8087
8088       if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8089         {
8090           tree_view->priv->focus_column = column;
8091           found_column = TRUE;
8092           break;
8093         }
8094     loop_end:
8095       if (count == 1)
8096         list = rtl ? list->prev : list->next;
8097       else
8098         list = rtl ? list->next : list->prev;
8099     }
8100
8101   if (found_column)
8102     {
8103       _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8104       g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8105       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8106     }
8107   else
8108     {
8109       gtk_widget_error_bell (GTK_WIDGET (tree_view));
8110     }
8111
8112   pspp_sheet_view_clamp_column_visible (tree_view,
8113                                       tree_view->priv->focus_column, TRUE);
8114 }
8115
8116 static gboolean
8117 try_move_cursor_tab (PsppSheetView *tree_view,
8118                      gboolean start_at_focus_column,
8119                      gint count)
8120 {
8121   PsppSheetViewColumn *column;
8122   GtkTreeIter iter;
8123   int cursor_node = -1;
8124   GtkTreePath *cursor_path = NULL;
8125   gboolean rtl;
8126   GList *list;
8127
8128   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8129     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8130   else
8131     return TRUE;
8132
8133   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8134   if (cursor_node < 0)
8135     return TRUE;
8136   if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8137     {
8138       gtk_tree_path_free (cursor_path);
8139       return TRUE;
8140     }
8141   gtk_tree_path_free (cursor_path);
8142
8143   rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8144   if (start_at_focus_column)
8145     {
8146       list = (rtl
8147               ? g_list_last (tree_view->priv->columns)
8148               : g_list_first (tree_view->priv->columns));
8149       if (tree_view->priv->focus_column)
8150         {
8151           for (; list; list = (rtl ? list->prev : list->next))
8152             {
8153               if (list->data == tree_view->priv->focus_column)
8154                 break;
8155             }
8156         }
8157     }
8158   else
8159     {
8160       list = (rtl ^ (count == 1)
8161               ? g_list_first (tree_view->priv->columns)
8162               : g_list_last (tree_view->priv->columns));
8163     }
8164
8165   while (list)
8166     {
8167       gboolean left, right;
8168
8169       column = list->data;
8170       if (column->visible == FALSE || !column->tabbable)
8171         goto loop_end;
8172
8173       pspp_sheet_view_column_cell_set_cell_data (column,
8174                                                  tree_view->priv->model,
8175                                                  &iter);
8176
8177       if (rtl)
8178         {
8179           right = list->prev ? TRUE : FALSE;
8180           left = list->next ? TRUE : FALSE;
8181         }
8182       else
8183         {
8184           left = list->prev ? TRUE : FALSE;
8185           right = list->next ? TRUE : FALSE;
8186         }
8187
8188       if (column->tabbable
8189           && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8190         {
8191           tree_view->priv->focus_column = column;
8192           _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8193           g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8194           gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8195           return TRUE;
8196         }
8197     loop_end:
8198       if (count == 1)
8199         list = rtl ? list->prev : list->next;
8200       else
8201         list = rtl ? list->next : list->prev;
8202     }
8203
8204   return FALSE;
8205 }
8206
8207 static void
8208 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8209                                  gint         count)
8210 {
8211   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8212     return;
8213
8214   if (!try_move_cursor_tab (tree_view, TRUE, count))
8215     {
8216       /* Shift+Tab goes backward, but Shift isn't supposed to act as Shift does
8217          for other movement commands, that is, it shouldn't cause the selection
8218          to be extended, so we need to act as though it is off. */
8219       tree_view->priv->shift_pressed = FALSE;
8220
8221       if (pspp_sheet_view_move_cursor_up_down (tree_view, count)
8222           && !try_move_cursor_tab (tree_view, FALSE, count))
8223         gtk_widget_error_bell (GTK_WIDGET (tree_view));
8224     }
8225
8226   pspp_sheet_view_clamp_column_visible (tree_view,
8227                                         tree_view->priv->focus_column, TRUE);
8228 }
8229
8230 static void
8231 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8232                                      gint         count)
8233 {
8234   int cursor_node;
8235   GtkTreePath *path;
8236   GtkTreePath *old_path;
8237
8238   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8239     return;
8240
8241   g_return_if_fail (tree_view->priv->row_count > 0);
8242
8243   pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8244
8245   if (count == -1)
8246     {
8247       /* Now go forward to find the first focusable row. */
8248       path = _pspp_sheet_view_find_path (tree_view, 0);
8249       search_first_focusable_path (tree_view, &path,
8250                                    TRUE, &cursor_node);
8251     }
8252   else
8253     {
8254       /* Now go backwards to find last focusable row. */
8255       path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8256       search_first_focusable_path (tree_view, &path,
8257                                    FALSE, &cursor_node);
8258     }
8259
8260   if (!path)
8261     goto cleanup;
8262
8263   if (gtk_tree_path_compare (old_path, path))
8264     {
8265       pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE);
8266       gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8267     }
8268   else
8269     {
8270       gtk_widget_error_bell (GTK_WIDGET (tree_view));
8271     }
8272
8273 cleanup:
8274   gtk_tree_path_free (old_path);
8275   gtk_tree_path_free (path);
8276 }
8277
8278 static gboolean
8279 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8280 {
8281   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8282     return FALSE;
8283
8284   if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8285       tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8286     return FALSE;
8287
8288   pspp_sheet_selection_select_all (tree_view->priv->selection);
8289
8290   return TRUE;
8291 }
8292
8293 static gboolean
8294 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8295 {
8296   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8297     return FALSE;
8298
8299   if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8300       tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8301     return FALSE;
8302
8303   pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8304
8305   return TRUE;
8306 }
8307
8308 static gboolean
8309 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8310                                       gboolean     start_editing)
8311 {
8312   int new_node = -1;
8313   int cursor_node = -1;
8314   GtkTreePath *cursor_path = NULL;
8315   GtkTreeSelectMode mode = 0;
8316
8317   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8318     return FALSE;
8319
8320   if (tree_view->priv->cursor)
8321     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8322
8323   if (cursor_path == NULL)
8324     return FALSE;
8325
8326   _pspp_sheet_view_find_node (tree_view, cursor_path,
8327                               &cursor_node);
8328
8329   if (cursor_node < 0)
8330     {
8331       gtk_tree_path_free (cursor_path);
8332       return FALSE;
8333     }
8334
8335   if (!tree_view->priv->shift_pressed && start_editing &&
8336       tree_view->priv->focus_column)
8337     {
8338       if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8339         {
8340           gtk_tree_path_free (cursor_path);
8341           return TRUE;
8342         }
8343     }
8344
8345   if (tree_view->priv->ctrl_pressed)
8346     mode |= GTK_TREE_SELECT_MODE_TOGGLE;
8347   if (tree_view->priv->shift_pressed)
8348     mode |= GTK_TREE_SELECT_MODE_EXTEND;
8349
8350   _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8351                                             cursor_node,
8352                                             cursor_path,
8353                                             mode,
8354                                             FALSE);
8355
8356   /* We bail out if the original (tree, node) don't exist anymore after
8357    * handling the selection-changed callback.  We do return TRUE because
8358    * the key press has been handled at this point.
8359    */
8360   _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8361
8362   if (cursor_node != new_node)
8363     return FALSE;
8364
8365   pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8366
8367   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8368   _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8369
8370   if (!tree_view->priv->shift_pressed)
8371     pspp_sheet_view_row_activated (tree_view, cursor_path,
8372                                  tree_view->priv->focus_column);
8373     
8374   gtk_tree_path_free (cursor_path);
8375
8376   return TRUE;
8377 }
8378
8379 static gboolean
8380 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8381 {
8382   int new_node = -1;
8383   int cursor_node = -1;
8384   GtkTreePath *cursor_path = NULL;
8385
8386   if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8387     return FALSE;
8388
8389   cursor_path = NULL;
8390   if (tree_view->priv->cursor)
8391     cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8392
8393   if (cursor_path == NULL)
8394     return FALSE;
8395
8396   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8397   if (cursor_node < 0)
8398     {
8399       gtk_tree_path_free (cursor_path);
8400       return FALSE;
8401     }
8402
8403   _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8404                                             cursor_node,
8405                                             cursor_path,
8406                                             GTK_TREE_SELECT_MODE_TOGGLE,
8407                                             FALSE);
8408
8409   /* We bail out if the original (tree, node) don't exist anymore after
8410    * handling the selection-changed callback.  We do return TRUE because
8411    * the key press has been handled at this point.
8412    */
8413   _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8414
8415   if (cursor_node != new_node)
8416     return FALSE;
8417
8418   pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8419
8420   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8421   pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8422   gtk_tree_path_free (cursor_path);
8423
8424   return TRUE;
8425 }
8426
8427 static gboolean
8428 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8429 {
8430   pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8431   tree_view->priv->typeselect_flush_timeout = 0;
8432
8433   return FALSE;
8434 }
8435
8436 /* Cut and paste from gtkwindow.c */
8437 static void
8438 send_focus_change (GtkWidget *widget,
8439                    gboolean   in)
8440 {
8441   GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8442
8443   g_object_ref (widget);
8444    
8445  if (in)
8446     GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
8447   else
8448     GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
8449
8450   fevent->focus_change.type = GDK_FOCUS_CHANGE;
8451   fevent->focus_change.window = g_object_ref (widget->window);
8452   fevent->focus_change.in = in;
8453   
8454   gtk_widget_event (widget, fevent);
8455   
8456   g_object_notify (G_OBJECT (widget), "has-focus");
8457
8458   g_object_unref (widget);
8459   gdk_event_free (fevent);
8460 }
8461
8462 static void
8463 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8464 {
8465   GtkWidget *frame, *vbox, *toplevel;
8466   GdkScreen *screen;
8467
8468   if (tree_view->priv->search_custom_entry_set)
8469     return;
8470
8471   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8472   screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8473
8474    if (tree_view->priv->search_window != NULL)
8475      {
8476        if (GTK_WINDOW (toplevel)->group)
8477          gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
8478                                       GTK_WINDOW (tree_view->priv->search_window));
8479        else if (GTK_WINDOW (tree_view->priv->search_window)->group)
8480          gtk_window_group_remove_window (GTK_WINDOW (tree_view->priv->search_window)->group,
8481                                          GTK_WINDOW (tree_view->priv->search_window));
8482        gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8483        return;
8484      }
8485    
8486   tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8487   gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8488
8489   if (GTK_WINDOW (toplevel)->group)
8490     gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
8491                                  GTK_WINDOW (tree_view->priv->search_window));
8492
8493   gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8494                             GDK_WINDOW_TYPE_HINT_UTILITY);
8495   gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8496   g_signal_connect (tree_view->priv->search_window, "delete-event",
8497                     G_CALLBACK (pspp_sheet_view_search_delete_event),
8498                     tree_view);
8499   g_signal_connect (tree_view->priv->search_window, "key-press-event",
8500                     G_CALLBACK (pspp_sheet_view_search_key_press_event),
8501                     tree_view);
8502   g_signal_connect (tree_view->priv->search_window, "button-press-event",
8503                     G_CALLBACK (pspp_sheet_view_search_button_press_event),
8504                     tree_view);
8505   g_signal_connect (tree_view->priv->search_window, "scroll-event",
8506                     G_CALLBACK (pspp_sheet_view_search_scroll_event),
8507                     tree_view);
8508
8509   frame = gtk_frame_new (NULL);
8510   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8511   gtk_widget_show (frame);
8512   gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8513
8514   vbox = gtk_vbox_new (FALSE, 0);
8515   gtk_widget_show (vbox);
8516   gtk_container_add (GTK_CONTAINER (frame), vbox);
8517   gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8518
8519   /* add entry */
8520   tree_view->priv->search_entry = gtk_entry_new ();
8521   gtk_widget_show (tree_view->priv->search_entry);
8522   g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8523                     G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8524                     tree_view);
8525   g_signal_connect (tree_view->priv->search_entry,
8526                     "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8527                     tree_view);
8528   g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8529                     "preedit-changed",
8530                     G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8531                     tree_view);
8532   gtk_container_add (GTK_CONTAINER (vbox),
8533                      tree_view->priv->search_entry);
8534
8535   gtk_widget_realize (tree_view->priv->search_entry);
8536 }
8537
8538 /* Pops up the interactive search entry.  If keybinding is TRUE then the user
8539  * started this by typing the start_interactive_search keybinding.  Otherwise, it came from 
8540  */
8541 static gboolean
8542 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8543                                              gboolean     keybinding)
8544 {
8545   /* We only start interactive search if we have focus or the columns
8546    * have focus.  If one of our children have focus, we don't want to
8547    * start the search.
8548    */
8549   GList *list;
8550   gboolean found_focus = FALSE;
8551   GtkWidgetClass *entry_parent_class;
8552   
8553   if (!tree_view->priv->enable_search && !keybinding)
8554     return FALSE;
8555
8556   if (tree_view->priv->search_custom_entry_set)
8557     return FALSE;
8558
8559   if (tree_view->priv->search_window != NULL &&
8560       gtk_widget_get_visible (tree_view->priv->search_window))
8561     return TRUE;
8562
8563   for (list = tree_view->priv->columns; list; list = list->next)
8564     {
8565       PsppSheetViewColumn *column;
8566
8567       column = list->data;
8568       if (! column->visible)
8569         continue;
8570
8571       if (column->button && gtk_widget_has_focus (column->button))
8572         {
8573           found_focus = TRUE;
8574           break;
8575         }
8576     }
8577   
8578   if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8579     found_focus = TRUE;
8580
8581   if (!found_focus)
8582     return FALSE;
8583
8584   if (tree_view->priv->search_column < 0)
8585     return FALSE;
8586
8587   pspp_sheet_view_ensure_interactive_directory (tree_view);
8588
8589   if (keybinding)
8590     gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8591
8592   /* done, show it */
8593   tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8594   gtk_widget_show (tree_view->priv->search_window);
8595   if (tree_view->priv->search_entry_changed_id == 0)
8596     {
8597       tree_view->priv->search_entry_changed_id =
8598         g_signal_connect (tree_view->priv->search_entry, "changed",
8599                           G_CALLBACK (pspp_sheet_view_search_init),
8600                           tree_view);
8601     }
8602
8603   tree_view->priv->typeselect_flush_timeout =
8604     gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8605                    (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8606                    tree_view);
8607
8608   /* Grab focus will select all the text.  We don't want that to happen, so we
8609    * call the parent instance and bypass the selection change.  This is probably
8610    * really non-kosher. */
8611   entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8612   (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8613
8614   /* send focus-in event */
8615   send_focus_change (tree_view->priv->search_entry, TRUE);
8616
8617   /* search first matching iter */
8618   pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8619
8620   return TRUE;
8621 }
8622
8623 static gboolean
8624 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8625 {
8626   return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8627 }
8628
8629 /* this function returns the new width of the column being resized given
8630  * the column and x position of the cursor; the x cursor position is passed
8631  * in as a pointer and automagicly corrected if it's beyond min/max limits
8632  */
8633 static gint
8634 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8635                                 gint       i,
8636                                 gint      *x)
8637 {
8638   PsppSheetViewColumn *column;
8639   gint width;
8640   gboolean rtl;
8641
8642   /* first translate the x position from widget->window
8643    * to clist->clist_window
8644    */
8645   rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8646   column = g_list_nth (tree_view->priv->columns, i)->data;
8647   width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8648  
8649   /* Clamp down the value */
8650   if (column->min_width == -1)
8651     width = MAX (column->button_request, width);
8652   else
8653     width = MAX (column->min_width, width);
8654   if (column->max_width != -1)
8655     width = MIN (width, column->max_width);
8656
8657   *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8658  
8659   return width;
8660 }
8661
8662
8663 /* FIXME this adjust_allocation is a big cut-and-paste from
8664  * GtkCList, needs to be some "official" way to do this
8665  * factored out.
8666  */
8667 typedef struct
8668 {
8669   GdkWindow *window;
8670   int dx;
8671   int dy;
8672 } ScrollData;
8673
8674 /* The window to which widget->window is relative */
8675 #define ALLOCATION_WINDOW(widget)               \
8676    (!gtk_widget_get_has_window (widget) ?               \
8677     (widget)->window :                          \
8678      gdk_window_get_parent ((widget)->window))
8679
8680 static void
8681 adjust_allocation_recurse (GtkWidget *widget,
8682                            gpointer   data)
8683 {
8684   ScrollData *scroll_data = data;
8685
8686   /* Need to really size allocate instead of just poking
8687    * into widget->allocation if the widget is not realized.
8688    * FIXME someone figure out why this was.
8689    */
8690   if (!gtk_widget_get_realized (widget))
8691     {
8692       if (gtk_widget_get_visible (widget))
8693         {
8694           GdkRectangle tmp_rectangle = widget->allocation;
8695           tmp_rectangle.x += scroll_data->dx;
8696           tmp_rectangle.y += scroll_data->dy;
8697           
8698           gtk_widget_size_allocate (widget, &tmp_rectangle);
8699         }
8700     }
8701   else
8702     {
8703       if (ALLOCATION_WINDOW (widget) == scroll_data->window)
8704         {
8705           widget->allocation.x += scroll_data->dx;
8706           widget->allocation.y += scroll_data->dy;
8707           
8708           if (GTK_IS_CONTAINER (widget))
8709             gtk_container_forall (GTK_CONTAINER (widget),
8710                                   adjust_allocation_recurse,
8711                                   data);
8712         }
8713     }
8714 }
8715
8716 static void
8717 adjust_allocation (GtkWidget *widget,
8718                    int        dx,
8719                    int        dy)
8720 {
8721   ScrollData scroll_data;
8722
8723   if (gtk_widget_get_realized (widget))
8724     scroll_data.window = ALLOCATION_WINDOW (widget);
8725   else
8726     scroll_data.window = NULL;
8727     
8728   scroll_data.dx = dx;
8729   scroll_data.dy = dy;
8730   
8731   adjust_allocation_recurse (widget, &scroll_data);
8732 }
8733
8734 void 
8735 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8736
8737 /* Callbacks */
8738 static void
8739 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8740                                   PsppSheetView   *tree_view)
8741 {
8742   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8743     {
8744       GList *list;
8745       gint dy;
8746         
8747       gdk_window_move (tree_view->priv->bin_window,
8748                        - tree_view->priv->hadjustment->value,
8749                        TREE_VIEW_HEADER_HEIGHT (tree_view));
8750       gdk_window_move (tree_view->priv->header_window,
8751                        - tree_view->priv->hadjustment->value,
8752                        0);
8753       dy = tree_view->priv->dy - (int) tree_view->priv->vadjustment->value;
8754       if (dy)
8755         {
8756           update_prelight (tree_view,
8757                            tree_view->priv->event_last_x,
8758                            tree_view->priv->event_last_y - dy);
8759
8760           if (tree_view->priv->edited_column &&
8761               GTK_IS_WIDGET (tree_view->priv->edited_column->editable_widget))
8762             {
8763               GList *list;
8764               GtkWidget *widget;
8765               PsppSheetViewChild *child = NULL;
8766
8767               widget = GTK_WIDGET (tree_view->priv->edited_column->editable_widget);
8768               adjust_allocation (widget, 0, dy); 
8769               
8770               for (list = tree_view->priv->children; list; list = list->next)
8771                 {
8772                   child = (PsppSheetViewChild *)list->data;
8773                   if (child->widget == widget)
8774                     {
8775                       child->y += dy;
8776                       break;
8777                     }
8778                 }
8779             }
8780         }
8781       gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8782
8783       if (tree_view->priv->dy != (int) tree_view->priv->vadjustment->value)
8784         {
8785           /* update our dy and top_row */
8786           tree_view->priv->dy = (int) tree_view->priv->vadjustment->value;
8787
8788           if (!tree_view->priv->in_top_row_to_dy)
8789             pspp_sheet_view_dy_to_top_row (tree_view);
8790         }
8791
8792       for (list = tree_view->priv->columns; list; list = list->next)
8793         {
8794           PsppSheetViewColumn *column = list->data;
8795           GtkAllocation *allocation = &column->allocation;
8796
8797           if (span_intersects (allocation->x, allocation->width,
8798                                tree_view->priv->hadjustment->value,
8799                                GTK_WIDGET (tree_view)->allocation.width))
8800             {
8801               pspp_sheet_view_column_set_need_button (column, TRUE);
8802               if (!column->button)
8803                 pspp_sheet_view_column_update_button (column);
8804             }
8805         }
8806     }
8807 }
8808
8809 \f
8810
8811 /* Public methods
8812  */
8813
8814 /**
8815  * pspp_sheet_view_new:
8816  *
8817  * Creates a new #PsppSheetView widget.
8818  *
8819  * Return value: A newly created #PsppSheetView widget.
8820  **/
8821 GtkWidget *
8822 pspp_sheet_view_new (void)
8823 {
8824   return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
8825 }
8826
8827 /**
8828  * pspp_sheet_view_new_with_model:
8829  * @model: the model.
8830  *
8831  * Creates a new #PsppSheetView widget with the model initialized to @model.
8832  *
8833  * Return value: A newly created #PsppSheetView widget.
8834  **/
8835 GtkWidget *
8836 pspp_sheet_view_new_with_model (GtkTreeModel *model)
8837 {
8838   return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
8839 }
8840
8841 /* Public Accessors
8842  */
8843
8844 /**
8845  * pspp_sheet_view_get_model:
8846  * @tree_view: a #PsppSheetView
8847  *
8848  * Returns the model the #PsppSheetView is based on.  Returns %NULL if the
8849  * model is unset.
8850  *
8851  * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
8852  **/
8853 GtkTreeModel *
8854 pspp_sheet_view_get_model (PsppSheetView *tree_view)
8855 {
8856   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8857
8858   return tree_view->priv->model;
8859 }
8860
8861 /**
8862  * pspp_sheet_view_set_model:
8863  * @tree_view: A #GtkTreeNode.
8864  * @model: (allow-none): The model.
8865  *
8866  * Sets the model for a #PsppSheetView.  If the @tree_view already has a model
8867  * set, it will remove it before setting the new model.  If @model is %NULL,
8868  * then it will unset the old model.
8869  **/
8870 void
8871 pspp_sheet_view_set_model (PsppSheetView  *tree_view,
8872                          GtkTreeModel *model)
8873 {
8874   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8875   g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
8876
8877   if (model == tree_view->priv->model)
8878     return;
8879
8880   if (tree_view->priv->scroll_to_path)
8881     {
8882       gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8883       tree_view->priv->scroll_to_path = NULL;
8884     }
8885
8886   if (tree_view->priv->model)
8887     {
8888       GList *tmplist = tree_view->priv->columns;
8889
8890       if (tree_view->priv->selected)
8891         range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
8892       pspp_sheet_view_stop_editing (tree_view, TRUE);
8893
8894       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8895                                             pspp_sheet_view_row_changed,
8896                                             tree_view);
8897       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8898                                             pspp_sheet_view_row_inserted,
8899                                             tree_view);
8900       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8901                                             pspp_sheet_view_row_deleted,
8902                                             tree_view);
8903       g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8904                                             pspp_sheet_view_rows_reordered,
8905                                             tree_view);
8906
8907       for (; tmplist; tmplist = tmplist->next)
8908         _pspp_sheet_view_column_unset_model (tmplist->data,
8909                                            tree_view->priv->model);
8910
8911       tree_view->priv->prelight_node = -1;
8912
8913       gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
8914       tree_view->priv->drag_dest_row = NULL;
8915       gtk_tree_row_reference_free (tree_view->priv->cursor);
8916       tree_view->priv->cursor = NULL;
8917       gtk_tree_row_reference_free (tree_view->priv->anchor);
8918       tree_view->priv->anchor = NULL;
8919       gtk_tree_row_reference_free (tree_view->priv->top_row);
8920       tree_view->priv->top_row = NULL;
8921       gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8922       tree_view->priv->scroll_to_path = NULL;
8923
8924       tree_view->priv->scroll_to_column = NULL;
8925
8926       g_object_unref (tree_view->priv->model);
8927
8928       tree_view->priv->search_column = -1;
8929       tree_view->priv->fixed_height = -1;
8930       tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
8931       tree_view->priv->last_button_x = -1;
8932       tree_view->priv->last_button_y = -1;
8933     }
8934
8935   tree_view->priv->model = model;
8936
8937   if (tree_view->priv->model)
8938     {
8939       gint i;
8940
8941       if (tree_view->priv->search_column == -1)
8942         {
8943           for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
8944             {
8945               GType type = gtk_tree_model_get_column_type (model, i);
8946
8947               if (g_value_type_transformable (type, G_TYPE_STRING))
8948                 {
8949                   tree_view->priv->search_column = i;
8950                   break;
8951                 }
8952             }
8953         }
8954
8955       g_object_ref (tree_view->priv->model);
8956       g_signal_connect (tree_view->priv->model,
8957                         "row-changed",
8958                         G_CALLBACK (pspp_sheet_view_row_changed),
8959                         tree_view);
8960       g_signal_connect (tree_view->priv->model,
8961                         "row-inserted",
8962                         G_CALLBACK (pspp_sheet_view_row_inserted),
8963                         tree_view);
8964       g_signal_connect (tree_view->priv->model,
8965                         "row-deleted",
8966                         G_CALLBACK (pspp_sheet_view_row_deleted),
8967                         tree_view);
8968       g_signal_connect (tree_view->priv->model,
8969                         "rows-reordered",
8970                         G_CALLBACK (pspp_sheet_view_rows_reordered),
8971                         tree_view);
8972
8973       tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
8974
8975       /*  FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
8976       install_presize_handler (tree_view);
8977     }
8978
8979   g_object_notify (G_OBJECT (tree_view), "model");
8980
8981   if (tree_view->priv->selection)
8982     _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
8983
8984   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8985     gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8986 }
8987
8988 /**
8989  * pspp_sheet_view_get_selection:
8990  * @tree_view: A #PsppSheetView.
8991  *
8992  * Gets the #PsppSheetSelection associated with @tree_view.
8993  *
8994  * Return value: A #PsppSheetSelection object.
8995  **/
8996 PsppSheetSelection *
8997 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
8998 {
8999   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9000
9001   return tree_view->priv->selection;
9002 }
9003
9004 /**
9005  * pspp_sheet_view_get_hadjustment:
9006  * @tree_view: A #PsppSheetView
9007  *
9008  * Gets the #GtkAdjustment currently being used for the horizontal aspect.
9009  *
9010  * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9011  * used.
9012  **/
9013 GtkAdjustment *
9014 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
9015 {
9016   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9017
9018   if (tree_view->priv->hadjustment == NULL)
9019     pspp_sheet_view_set_hadjustment (tree_view, NULL);
9020
9021   return tree_view->priv->hadjustment;
9022 }
9023
9024 /**
9025  * pspp_sheet_view_set_hadjustment:
9026  * @tree_view: A #PsppSheetView
9027  * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9028  *
9029  * Sets the #GtkAdjustment for the current horizontal aspect.
9030  **/
9031 void
9032 pspp_sheet_view_set_hadjustment (PsppSheetView   *tree_view,
9033                                GtkAdjustment *adjustment)
9034 {
9035   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9036
9037   pspp_sheet_view_set_adjustments (tree_view,
9038                                  adjustment,
9039                                  tree_view->priv->vadjustment);
9040
9041   g_object_notify (G_OBJECT (tree_view), "hadjustment");
9042 }
9043
9044 /**
9045  * pspp_sheet_view_get_vadjustment:
9046  * @tree_view: A #PsppSheetView
9047  *
9048  * Gets the #GtkAdjustment currently being used for the vertical aspect.
9049  *
9050  * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9051  * used.
9052  **/
9053 GtkAdjustment *
9054 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9055 {
9056   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9057
9058   if (tree_view->priv->vadjustment == NULL)
9059     pspp_sheet_view_set_vadjustment (tree_view, NULL);
9060
9061   return tree_view->priv->vadjustment;
9062 }
9063
9064 /**
9065  * pspp_sheet_view_set_vadjustment:
9066  * @tree_view: A #PsppSheetView
9067  * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9068  *
9069  * Sets the #GtkAdjustment for the current vertical aspect.
9070  **/
9071 void
9072 pspp_sheet_view_set_vadjustment (PsppSheetView   *tree_view,
9073                                GtkAdjustment *adjustment)
9074 {
9075   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9076
9077   pspp_sheet_view_set_adjustments (tree_view,
9078                                  tree_view->priv->hadjustment,
9079                                  adjustment);
9080
9081   g_object_notify (G_OBJECT (tree_view), "vadjustment");
9082 }
9083
9084 /* Column and header operations */
9085
9086 /**
9087  * pspp_sheet_view_get_headers_visible:
9088  * @tree_view: A #PsppSheetView.
9089  *
9090  * Returns %TRUE if the headers on the @tree_view are visible.
9091  *
9092  * Return value: Whether the headers are visible or not.
9093  **/
9094 gboolean
9095 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9096 {
9097   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9098
9099   return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9100 }
9101
9102 /**
9103  * pspp_sheet_view_set_headers_visible:
9104  * @tree_view: A #PsppSheetView.
9105  * @headers_visible: %TRUE if the headers are visible
9106  *
9107  * Sets the visibility state of the headers.
9108  **/
9109 void
9110 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9111                                    gboolean     headers_visible)
9112 {
9113   gint x, y;
9114   GList *list;
9115   PsppSheetViewColumn *column;
9116
9117   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9118
9119   headers_visible = !! headers_visible;
9120
9121   if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9122     return;
9123
9124   if (headers_visible)
9125     PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9126   else
9127     PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9128
9129   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9130     {
9131       gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9132       if (headers_visible)
9133         {
9134           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));
9135
9136           if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9137             pspp_sheet_view_map_buttons (tree_view);
9138         }
9139       else
9140         {
9141           gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9142
9143           for (list = tree_view->priv->columns; list; list = list->next)
9144             {
9145               column = list->data;
9146               if (column->button)
9147                 gtk_widget_unmap (column->button);
9148             }
9149           gdk_window_hide (tree_view->priv->header_window);
9150         }
9151     }
9152
9153   tree_view->priv->vadjustment->page_size = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
9154   tree_view->priv->vadjustment->page_increment = (GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2;
9155   tree_view->priv->vadjustment->lower = 0;
9156   tree_view->priv->vadjustment->upper = tree_view->priv->height;
9157   gtk_adjustment_changed (tree_view->priv->vadjustment);
9158
9159   gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9160
9161   g_object_notify (G_OBJECT (tree_view), "headers-visible");
9162 }
9163
9164 /**
9165  * pspp_sheet_view_columns_autosize:
9166  * @tree_view: A #PsppSheetView.
9167  *
9168  * Resizes all columns to their optimal width. Only works after the
9169  * treeview has been realized.
9170  **/
9171 void
9172 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9173 {
9174   gboolean dirty = FALSE;
9175   GList *list;
9176   PsppSheetViewColumn *column;
9177
9178   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9179
9180   for (list = tree_view->priv->columns; list; list = list->next)
9181     {
9182       column = list->data;
9183       _pspp_sheet_view_column_cell_set_dirty (column);
9184       dirty = TRUE;
9185     }
9186
9187   if (dirty)
9188     gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9189 }
9190
9191 /**
9192  * pspp_sheet_view_set_headers_clickable:
9193  * @tree_view: A #PsppSheetView.
9194  * @setting: %TRUE if the columns are clickable.
9195  *
9196  * Allow the column title buttons to be clicked.
9197  **/
9198 void
9199 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9200                                      gboolean   setting)
9201 {
9202   GList *list;
9203
9204   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9205
9206   for (list = tree_view->priv->columns; list; list = list->next)
9207     pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9208
9209   g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9210 }
9211
9212
9213 /**
9214  * pspp_sheet_view_get_headers_clickable:
9215  * @tree_view: A #PsppSheetView.
9216  *
9217  * Returns whether all header columns are clickable.
9218  *
9219  * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9220  *
9221  * Since: 2.10
9222  **/
9223 gboolean 
9224 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9225 {
9226   GList *list;
9227   
9228   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9229
9230   for (list = tree_view->priv->columns; list; list = list->next)
9231     if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9232       return FALSE;
9233
9234   return TRUE;
9235 }
9236
9237 /**
9238  * pspp_sheet_view_set_rules_hint
9239  * @tree_view: a #PsppSheetView
9240  * @setting: %TRUE if the tree requires reading across rows
9241  *
9242  * This function tells GTK+ that the user interface for your
9243  * application requires users to read across tree rows and associate
9244  * cells with one another. By default, GTK+ will then render the tree
9245  * with alternating row colors. Do <emphasis>not</emphasis> use it
9246  * just because you prefer the appearance of the ruled tree; that's a
9247  * question for the theme. Some themes will draw tree rows in
9248  * alternating colors even when rules are turned off, and users who
9249  * prefer that appearance all the time can choose those themes. You
9250  * should call this function only as a <emphasis>semantic</emphasis>
9251  * hint to the theme engine that your tree makes alternating colors
9252  * useful from a functional standpoint (since it has lots of columns,
9253  * generally).
9254  *
9255  **/
9256 void
9257 pspp_sheet_view_set_rules_hint (PsppSheetView  *tree_view,
9258                               gboolean      setting)
9259 {
9260   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9261
9262   setting = setting != FALSE;
9263
9264   if (tree_view->priv->has_rules != setting)
9265     {
9266       tree_view->priv->has_rules = setting;
9267       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9268     }
9269
9270   g_object_notify (G_OBJECT (tree_view), "rules-hint");
9271 }
9272
9273 /**
9274  * pspp_sheet_view_get_rules_hint
9275  * @tree_view: a #PsppSheetView
9276  *
9277  * Gets the setting set by pspp_sheet_view_set_rules_hint().
9278  *
9279  * Return value: %TRUE if rules are useful for the user of this tree
9280  **/
9281 gboolean
9282 pspp_sheet_view_get_rules_hint (PsppSheetView  *tree_view)
9283 {
9284   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9285
9286   return tree_view->priv->has_rules;
9287 }
9288
9289 /* Public Column functions
9290  */
9291
9292 /**
9293  * pspp_sheet_view_append_column:
9294  * @tree_view: A #PsppSheetView.
9295  * @column: The #PsppSheetViewColumn to add.
9296  *
9297  * Appends @column to the list of columns.
9298  *
9299  * Return value: The number of columns in @tree_view after appending.
9300  **/
9301 gint
9302 pspp_sheet_view_append_column (PsppSheetView       *tree_view,
9303                              PsppSheetViewColumn *column)
9304 {
9305   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9306   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9307   g_return_val_if_fail (column->tree_view == NULL, -1);
9308
9309   return pspp_sheet_view_insert_column (tree_view, column, -1);
9310 }
9311
9312
9313 /**
9314  * pspp_sheet_view_remove_column:
9315  * @tree_view: A #PsppSheetView.
9316  * @column: The #PsppSheetViewColumn to remove.
9317  *
9318  * Removes @column from @tree_view.
9319  *
9320  * Return value: The number of columns in @tree_view after removing.
9321  **/
9322 gint
9323 pspp_sheet_view_remove_column (PsppSheetView       *tree_view,
9324                              PsppSheetViewColumn *column)
9325 {
9326   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9327   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9328   g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9329
9330   if (tree_view->priv->focus_column == column)
9331     tree_view->priv->focus_column = NULL;
9332
9333   if (tree_view->priv->edited_column == column)
9334     {
9335       pspp_sheet_view_stop_editing (tree_view, TRUE);
9336
9337       /* no need to, but just to be sure ... */
9338       tree_view->priv->edited_column = NULL;
9339     }
9340
9341   _pspp_sheet_view_column_unset_tree_view (column);
9342
9343   tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9344   tree_view->priv->n_columns--;
9345
9346   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9347     {
9348       GList *list;
9349
9350       _pspp_sheet_view_column_unrealize_button (column);
9351       for (list = tree_view->priv->columns; list; list = list->next)
9352         {
9353           PsppSheetViewColumn *tmp_column;
9354
9355           tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9356           if (tmp_column->visible)
9357             _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9358         }
9359
9360       if (tree_view->priv->n_columns == 0 &&
9361           pspp_sheet_view_get_headers_visible (tree_view) && 
9362           tree_view->priv->header_window)
9363         gdk_window_hide (tree_view->priv->header_window);
9364
9365       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9366     }
9367
9368   g_object_unref (column);
9369   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9370
9371   return tree_view->priv->n_columns;
9372 }
9373
9374 /**
9375  * pspp_sheet_view_insert_column:
9376  * @tree_view: A #PsppSheetView.
9377  * @column: The #PsppSheetViewColumn to be inserted.
9378  * @position: The position to insert @column in.
9379  *
9380  * This inserts the @column into the @tree_view at @position.  If @position is
9381  * -1, then the column is inserted at the end.
9382  *
9383  * Return value: The number of columns in @tree_view after insertion.
9384  **/
9385 gint
9386 pspp_sheet_view_insert_column (PsppSheetView       *tree_view,
9387                              PsppSheetViewColumn *column,
9388                              gint               position)
9389 {
9390   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9391   g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9392   g_return_val_if_fail (column->tree_view == NULL, -1);
9393
9394   g_object_ref_sink (column);
9395
9396   if (tree_view->priv->n_columns == 0 &&
9397       gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9398       pspp_sheet_view_get_headers_visible (tree_view))
9399     {
9400       gdk_window_show (tree_view->priv->header_window);
9401     }
9402
9403   tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9404                                             column, position);
9405   tree_view->priv->n_columns++;
9406
9407   _pspp_sheet_view_column_set_tree_view (column, tree_view);
9408
9409   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9410     {
9411       GList *list;
9412
9413       _pspp_sheet_view_column_realize_button (column);
9414
9415       for (list = tree_view->priv->columns; list; list = list->next)
9416         {
9417           column = PSPP_SHEET_VIEW_COLUMN (list->data);
9418           if (column->visible)
9419             _pspp_sheet_view_column_cell_set_dirty (column);
9420         }
9421       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9422     }
9423
9424   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9425
9426   return tree_view->priv->n_columns;
9427 }
9428
9429 /**
9430  * pspp_sheet_view_insert_column_with_attributes:
9431  * @tree_view: A #PsppSheetView
9432  * @position: The position to insert the new column in.
9433  * @title: The title to set the header to.
9434  * @cell: The #GtkCellRenderer.
9435  * @Varargs: A %NULL-terminated list of attributes.
9436  *
9437  * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9438  * @position.  If @position is -1, then the newly created column is inserted at
9439  * the end.  The column is initialized with the attributes given.
9440  *
9441  * Return value: The number of columns in @tree_view after insertion.
9442  **/
9443 gint
9444 pspp_sheet_view_insert_column_with_attributes (PsppSheetView     *tree_view,
9445                                              gint             position,
9446                                              const gchar     *title,
9447                                              GtkCellRenderer *cell,
9448                                              ...)
9449 {
9450   PsppSheetViewColumn *column;
9451   gchar *attribute;
9452   va_list args;
9453   gint column_id;
9454
9455   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9456
9457   column = pspp_sheet_view_column_new ();
9458   pspp_sheet_view_column_set_title (column, title);
9459   pspp_sheet_view_column_pack_start (column, cell, TRUE);
9460
9461   va_start (args, cell);
9462
9463   attribute = va_arg (args, gchar *);
9464
9465   while (attribute != NULL)
9466     {
9467       column_id = va_arg (args, gint);
9468       pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9469       attribute = va_arg (args, gchar *);
9470     }
9471
9472   va_end (args);
9473
9474   pspp_sheet_view_insert_column (tree_view, column, position);
9475
9476   return tree_view->priv->n_columns;
9477 }
9478
9479 /**
9480  * pspp_sheet_view_insert_column_with_data_func:
9481  * @tree_view: a #PsppSheetView
9482  * @position: Position to insert, -1 for append
9483  * @title: column title
9484  * @cell: cell renderer for column
9485  * @func: function to set attributes of cell renderer
9486  * @data: data for @func
9487  * @dnotify: destroy notifier for @data
9488  *
9489  * Convenience function that inserts a new column into the #PsppSheetView
9490  * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9491  * attributes (normally using data from the model). See also
9492  * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9493  *
9494  * Return value: number of columns in the tree view post-insert
9495  **/
9496 gint
9497 pspp_sheet_view_insert_column_with_data_func  (PsppSheetView               *tree_view,
9498                                              gint                       position,
9499                                              const gchar               *title,
9500                                              GtkCellRenderer           *cell,
9501                                              PsppSheetCellDataFunc        func,
9502                                              gpointer                   data,
9503                                              GDestroyNotify             dnotify)
9504 {
9505   PsppSheetViewColumn *column;
9506
9507   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9508
9509   column = pspp_sheet_view_column_new ();
9510   pspp_sheet_view_column_set_title (column, title);
9511   pspp_sheet_view_column_pack_start (column, cell, TRUE);
9512   pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9513
9514   pspp_sheet_view_insert_column (tree_view, column, position);
9515
9516   return tree_view->priv->n_columns;
9517 }
9518
9519 /**
9520  * pspp_sheet_view_get_column:
9521  * @tree_view: A #PsppSheetView.
9522  * @n: The position of the column, counting from 0.
9523  *
9524  * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9525  *
9526  * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9527  * range of columns.
9528  **/
9529 PsppSheetViewColumn *
9530 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9531                           gint         n)
9532 {
9533   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9534
9535   if (n < 0 || n >= tree_view->priv->n_columns)
9536     return NULL;
9537
9538   if (tree_view->priv->columns == NULL)
9539     return NULL;
9540
9541   return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9542 }
9543
9544 /**
9545  * pspp_sheet_view_get_columns:
9546  * @tree_view: A #PsppSheetView
9547  *
9548  * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9549  * The returned list must be freed with g_list_free ().
9550  *
9551  * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9552  **/
9553 GList *
9554 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9555 {
9556   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9557
9558   return g_list_copy (tree_view->priv->columns);
9559 }
9560
9561 /**
9562  * pspp_sheet_view_move_column_after:
9563  * @tree_view: A #PsppSheetView
9564  * @column: The #PsppSheetViewColumn to be moved.
9565  * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9566  *
9567  * Moves @column to be after to @base_column.  If @base_column is %NULL, then
9568  * @column is placed in the first position.
9569  **/
9570 void
9571 pspp_sheet_view_move_column_after (PsppSheetView       *tree_view,
9572                                  PsppSheetViewColumn *column,
9573                                  PsppSheetViewColumn *base_column)
9574 {
9575   GList *column_list_el, *base_el = NULL;
9576
9577   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9578
9579   column_list_el = g_list_find (tree_view->priv->columns, column);
9580   g_return_if_fail (column_list_el != NULL);
9581
9582   if (base_column)
9583     {
9584       base_el = g_list_find (tree_view->priv->columns, base_column);
9585       g_return_if_fail (base_el != NULL);
9586     }
9587
9588   if (column_list_el->prev == base_el)
9589     return;
9590
9591   tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9592   if (base_el == NULL)
9593     {
9594       column_list_el->prev = NULL;
9595       column_list_el->next = tree_view->priv->columns;
9596       if (column_list_el->next)
9597         column_list_el->next->prev = column_list_el;
9598       tree_view->priv->columns = column_list_el;
9599     }
9600   else
9601     {
9602       column_list_el->prev = base_el;
9603       column_list_el->next = base_el->next;
9604       if (column_list_el->next)
9605         column_list_el->next->prev = column_list_el;
9606       base_el->next = column_list_el;
9607     }
9608
9609   if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9610     {
9611       gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9612       pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9613     }
9614
9615   g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9616 }
9617
9618 /**
9619  * pspp_sheet_view_set_column_drag_function:
9620  * @tree_view: A #PsppSheetView.
9621  * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9622  * @user_data: (allow-none): User data to be passed to @func, or %NULL
9623  * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9624  *
9625  * Sets a user function for determining where a column may be dropped when
9626  * dragged.  This function is called on every column pair in turn at the
9627  * beginning of a column drag to determine where a drop can take place.  The
9628  * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9629  * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9630  * @user_data.  If either of the #PsppSheetViewColumn arguments for the drop spot
9631  * are %NULL, then they indicate an edge.  If @func is set to be %NULL, then
9632  * @tree_view reverts to the default behavior of allowing all columns to be
9633  * dropped everywhere.
9634  **/
9635 void
9636 pspp_sheet_view_set_column_drag_function (PsppSheetView               *tree_view,
9637                                         PsppSheetViewColumnDropFunc  func,
9638                                         gpointer                   user_data,
9639                                         GDestroyNotify             destroy)
9640 {
9641   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9642
9643   if (tree_view->priv->column_drop_func_data_destroy)
9644     tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9645
9646   tree_view->priv->column_drop_func = func;
9647   tree_view->priv->column_drop_func_data = user_data;
9648   tree_view->priv->column_drop_func_data_destroy = destroy;
9649 }
9650
9651 /**
9652  * pspp_sheet_view_scroll_to_point:
9653  * @tree_view: a #PsppSheetView
9654  * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9655  * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9656  *
9657  * Scrolls the tree view such that the top-left corner of the visible
9658  * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9659  * in tree coordinates.  The @tree_view must be realized before
9660  * this function is called.  If it isn't, you probably want to be
9661  * using pspp_sheet_view_scroll_to_cell().
9662  *
9663  * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9664  **/
9665 void
9666 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9667                                gint         tree_x,
9668                                gint         tree_y)
9669 {
9670   GtkAdjustment *hadj;
9671   GtkAdjustment *vadj;
9672
9673   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9674   g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9675
9676   hadj = tree_view->priv->hadjustment;
9677   vadj = tree_view->priv->vadjustment;
9678
9679   if (tree_x != -1)
9680     gtk_adjustment_set_value (hadj, CLAMP (tree_x, hadj->lower, hadj->upper - hadj->page_size));
9681   if (tree_y != -1)
9682     gtk_adjustment_set_value (vadj, CLAMP (tree_y, vadj->lower, vadj->upper - vadj->page_size));
9683 }
9684
9685 /**
9686  * pspp_sheet_view_scroll_to_cell:
9687  * @tree_view: A #PsppSheetView.
9688  * @path: (allow-none): The path of the row to move to, or %NULL.
9689  * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9690  * @use_align: whether to use alignment arguments, or %FALSE.
9691  * @row_align: The vertical alignment of the row specified by @path.
9692  * @col_align: The horizontal alignment of the column specified by @column.
9693  *
9694  * Moves the alignments of @tree_view to the position specified by @column and
9695  * @path.  If @column is %NULL, then no horizontal scrolling occurs.  Likewise,
9696  * if @path is %NULL no vertical scrolling occurs.  At a minimum, one of @column
9697  * or @path need to be non-%NULL.  @row_align determines where the row is
9698  * placed, and @col_align determines where @column is placed.  Both are expected
9699  * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9700  * right/bottom alignment, 0.5 means center.
9701  *
9702  * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9703  * tree does the minimum amount of work to scroll the cell onto the screen.
9704  * This means that the cell will be scrolled to the edge closest to its current
9705  * position.  If the cell is currently visible on the screen, nothing is done.
9706  *
9707  * This function only works if the model is set, and @path is a valid row on the
9708  * model.  If the model changes before the @tree_view is realized, the centered
9709  * path will be modified to reflect this change.
9710  **/
9711 void
9712 pspp_sheet_view_scroll_to_cell (PsppSheetView       *tree_view,
9713                               GtkTreePath       *path,
9714                               PsppSheetViewColumn *column,
9715                               gboolean           use_align,
9716                               gfloat             row_align,
9717                               gfloat             col_align)
9718 {
9719   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9720   g_return_if_fail (tree_view->priv->model != NULL);
9721   g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9722   g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9723   g_return_if_fail (path != NULL || column != NULL);
9724
9725 #if 0
9726   g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9727            gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9728 #endif
9729   row_align = CLAMP (row_align, 0.0, 1.0);
9730   col_align = CLAMP (col_align, 0.0, 1.0);
9731
9732
9733   /* Note: Despite the benefits that come from having one code path for the
9734    * scrolling code, we short-circuit validate_visible_area's immplementation as
9735    * it is much slower than just going to the point.
9736    */
9737   if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9738       !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9739       /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9740     {
9741       if (tree_view->priv->scroll_to_path)
9742         gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9743
9744       tree_view->priv->scroll_to_path = NULL;
9745       tree_view->priv->scroll_to_column = NULL;
9746
9747       if (path)
9748         tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
9749       if (column)
9750         tree_view->priv->scroll_to_column = column;
9751       tree_view->priv->scroll_to_use_align = use_align;
9752       tree_view->priv->scroll_to_row_align = row_align;
9753       tree_view->priv->scroll_to_col_align = col_align;
9754
9755       install_presize_handler (tree_view);
9756     }
9757   else
9758     {
9759       GdkRectangle cell_rect;
9760       GdkRectangle vis_rect;
9761       gint dest_x, dest_y;
9762
9763       pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
9764       pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
9765
9766       cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
9767
9768       dest_x = vis_rect.x;
9769       dest_y = vis_rect.y;
9770
9771       if (column)
9772         {
9773           if (use_align)
9774             {
9775               dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
9776             }
9777           else
9778             {
9779               if (cell_rect.x < vis_rect.x)
9780                 dest_x = cell_rect.x;
9781               if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
9782                 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
9783             }
9784         }
9785
9786       if (path)
9787         {
9788           if (use_align)
9789             {
9790               dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
9791               dest_y = MAX (dest_y, 0);
9792             }
9793           else
9794             {
9795               if (cell_rect.y < vis_rect.y)
9796                 dest_y = cell_rect.y;
9797               if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
9798                 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
9799             }
9800         }
9801
9802       pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
9803     }
9804 }
9805
9806 /**
9807  * pspp_sheet_view_row_activated:
9808  * @tree_view: A #PsppSheetView
9809  * @path: The #GtkTreePath to be activated.
9810  * @column: The #PsppSheetViewColumn to be activated.
9811  *
9812  * Activates the cell determined by @path and @column.
9813  **/
9814 void
9815 pspp_sheet_view_row_activated (PsppSheetView       *tree_view,
9816                              GtkTreePath       *path,
9817                              PsppSheetViewColumn *column)
9818 {
9819   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9820
9821   g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
9822 }
9823
9824
9825 /**
9826  * pspp_sheet_view_get_reorderable:
9827  * @tree_view: a #PsppSheetView
9828  *
9829  * Retrieves whether the user can reorder the tree via drag-and-drop. See
9830  * pspp_sheet_view_set_reorderable().
9831  *
9832  * Return value: %TRUE if the tree can be reordered.
9833  **/
9834 gboolean
9835 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
9836 {
9837   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9838
9839   return tree_view->priv->reorderable;
9840 }
9841
9842 /**
9843  * pspp_sheet_view_set_reorderable:
9844  * @tree_view: A #PsppSheetView.
9845  * @reorderable: %TRUE, if the tree can be reordered.
9846  *
9847  * This function is a convenience function to allow you to reorder
9848  * models that support the #GtkDragSourceIface and the
9849  * #GtkDragDestIface.  Both #GtkTreeStore and #GtkListStore support
9850  * these.  If @reorderable is %TRUE, then the user can reorder the
9851  * model by dragging and dropping rows. The developer can listen to
9852  * these changes by connecting to the model's row_inserted and
9853  * row_deleted signals. The reordering is implemented by setting up
9854  * the tree view as a drag source and destination. Therefore, drag and
9855  * drop can not be used in a reorderable view for any other purpose.
9856  *
9857  * This function does not give you any degree of control over the order -- any
9858  * reordering is allowed.  If more control is needed, you should probably
9859  * handle drag and drop manually.
9860  **/
9861 void
9862 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
9863                                gboolean     reorderable)
9864 {
9865   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9866
9867   reorderable = reorderable != FALSE;
9868
9869   if (tree_view->priv->reorderable == reorderable)
9870     return;
9871
9872   if (reorderable)
9873     {
9874       const GtkTargetEntry row_targets[] = {
9875         { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
9876       };
9877
9878       pspp_sheet_view_enable_model_drag_source (tree_view,
9879                                               GDK_BUTTON1_MASK,
9880                                               row_targets,
9881                                               G_N_ELEMENTS (row_targets),
9882                                               GDK_ACTION_MOVE);
9883       pspp_sheet_view_enable_model_drag_dest (tree_view,
9884                                             row_targets,
9885                                             G_N_ELEMENTS (row_targets),
9886                                             GDK_ACTION_MOVE);
9887     }
9888   else
9889     {
9890       pspp_sheet_view_unset_rows_drag_source (tree_view);
9891       pspp_sheet_view_unset_rows_drag_dest (tree_view);
9892     }
9893
9894   tree_view->priv->reorderable = reorderable;
9895
9896   g_object_notify (G_OBJECT (tree_view), "reorderable");
9897 }
9898
9899 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
9900    is pressed, other rows will be unselected.
9901
9902    If CLAMP_NODE is true, then the sheetview will scroll to make the row
9903    visible. */
9904 static void
9905 pspp_sheet_view_real_set_cursor (PsppSheetView     *tree_view,
9906                                GtkTreePath     *path,
9907                                gboolean         clear_and_select,
9908                                gboolean         clamp_node)
9909 {
9910   int node = -1;
9911
9912   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9913     {
9914       GtkTreePath *cursor_path;
9915       cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9916       pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
9917       gtk_tree_path_free (cursor_path);
9918     }
9919
9920   gtk_tree_row_reference_free (tree_view->priv->cursor);
9921   tree_view->priv->cursor = NULL;
9922
9923   _pspp_sheet_view_find_node (tree_view, path, &node);
9924   tree_view->priv->cursor =
9925     gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
9926                                       tree_view->priv->model,
9927                                       path);
9928
9929   if (tree_view->priv->row_count > 0)
9930     {
9931       int new_node = -1;
9932
9933       if (clear_and_select && !tree_view->priv->ctrl_pressed)
9934         {
9935           GtkTreeSelectMode mode = 0;
9936
9937           if (tree_view->priv->ctrl_pressed)
9938             mode |= GTK_TREE_SELECT_MODE_TOGGLE;
9939           if (tree_view->priv->shift_pressed)
9940             mode |= GTK_TREE_SELECT_MODE_EXTEND;
9941
9942           _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
9943                                                     node, path, mode,
9944                                                     FALSE);
9945         }
9946
9947       /* We have to re-find tree and node here again, somebody might have
9948        * cleared the node or the whole tree in the PsppSheetSelection::changed
9949        * callback. If the nodes differ we bail out here.
9950        */
9951       _pspp_sheet_view_find_node (tree_view, path, &new_node);
9952
9953       if (node != new_node)
9954         return;
9955
9956       if (clamp_node)
9957         {
9958           pspp_sheet_view_clamp_node_visible (tree_view, node);
9959           _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
9960         }
9961     }
9962
9963   g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
9964 }
9965
9966 /**
9967  * pspp_sheet_view_get_cursor:
9968  * @tree_view: A #PsppSheetView
9969  * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
9970  * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
9971  *
9972  * Fills in @path and @focus_column with the current path and focus column.  If
9973  * the cursor isn't currently set, then *@path will be %NULL.  If no column
9974  * currently has focus, then *@focus_column will be %NULL.
9975  *
9976  * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
9977  * you are done with it.
9978  **/
9979 void
9980 pspp_sheet_view_get_cursor (PsppSheetView        *tree_view,
9981                           GtkTreePath       **path,
9982                           PsppSheetViewColumn **focus_column)
9983 {
9984   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9985
9986   if (path)
9987     {
9988       if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9989         *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9990       else
9991         *path = NULL;
9992     }
9993
9994   if (focus_column)
9995     {
9996       *focus_column = tree_view->priv->focus_column;
9997     }
9998 }
9999
10000 /**
10001  * pspp_sheet_view_set_cursor:
10002  * @tree_view: A #PsppSheetView
10003  * @path: A #GtkTreePath
10004  * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10005  * @start_editing: %TRUE if the specified cell should start being edited.
10006  *
10007  * Sets the current keyboard focus to be at @path, and selects it.  This is
10008  * useful when you want to focus the user's attention on a particular row.  If
10009  * @focus_column is not %NULL, then focus is given to the column specified by 
10010  * it. Additionally, if @focus_column is specified, and @start_editing is 
10011  * %TRUE, then editing should be started in the specified cell.  
10012  * This function is often followed by @gtk_widget_grab_focus (@tree_view) 
10013  * in order to give keyboard focus to the widget.  Please note that editing 
10014  * can only happen when the widget is realized.
10015  *
10016  * If @path is invalid for @model, the current cursor (if any) will be unset
10017  * and the function will return without failing.
10018  **/
10019 void
10020 pspp_sheet_view_set_cursor (PsppSheetView       *tree_view,
10021                           GtkTreePath       *path,
10022                           PsppSheetViewColumn *focus_column,
10023                           gboolean           start_editing)
10024 {
10025   pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10026                                     NULL, start_editing);
10027 }
10028
10029 /**
10030  * pspp_sheet_view_set_cursor_on_cell:
10031  * @tree_view: A #PsppSheetView
10032  * @path: A #GtkTreePath
10033  * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10034  * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10035  * @start_editing: %TRUE if the specified cell should start being edited.
10036  *
10037  * Sets the current keyboard focus to be at @path, and selects it.  This is
10038  * useful when you want to focus the user's attention on a particular row.  If
10039  * @focus_column is not %NULL, then focus is given to the column specified by
10040  * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10041  * contains 2 or more editable or activatable cells, then focus is given to
10042  * the cell specified by @focus_cell. Additionally, if @focus_column is
10043  * specified, and @start_editing is %TRUE, then editing should be started in
10044  * the specified cell.  This function is often followed by
10045  * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10046  * widget.  Please note that editing can only happen when the widget is
10047  * realized.
10048  *
10049  * If @path is invalid for @model, the current cursor (if any) will be unset
10050  * and the function will return without failing.
10051  *
10052  * Since: 2.2
10053  **/
10054 void
10055 pspp_sheet_view_set_cursor_on_cell (PsppSheetView       *tree_view,
10056                                   GtkTreePath       *path,
10057                                   PsppSheetViewColumn *focus_column,
10058                                   GtkCellRenderer   *focus_cell,
10059                                   gboolean           start_editing)
10060 {
10061   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10062   g_return_if_fail (path != NULL);
10063   g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10064
10065   if (!tree_view->priv->model)
10066     return;
10067
10068   if (focus_cell)
10069     {
10070       g_return_if_fail (focus_column);
10071       g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10072     }
10073
10074   /* cancel the current editing, if it exists */
10075   if (tree_view->priv->edited_column &&
10076       tree_view->priv->edited_column->editable_widget)
10077     pspp_sheet_view_stop_editing (tree_view, TRUE);
10078
10079   pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE);
10080
10081   if (focus_column && focus_column->visible)
10082     {
10083       GList *list;
10084       gboolean column_in_tree = FALSE;
10085
10086       for (list = tree_view->priv->columns; list; list = list->next)
10087         if (list->data == focus_column)
10088           {
10089             column_in_tree = TRUE;
10090             break;
10091           }
10092       g_return_if_fail (column_in_tree);
10093       tree_view->priv->focus_column = focus_column;
10094       if (focus_cell)
10095         pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10096       if (start_editing)
10097         pspp_sheet_view_start_editing (tree_view, path);
10098
10099       pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10100       pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10101
10102     }
10103 }
10104
10105 /**
10106  * pspp_sheet_view_get_bin_window:
10107  * @tree_view: A #PsppSheetView
10108  * 
10109  * Returns the window that @tree_view renders to.  This is used primarily to
10110  * compare to <literal>event->window</literal> to confirm that the event on
10111  * @tree_view is on the right window.
10112  * 
10113  * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10114  **/
10115 GdkWindow *
10116 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10117 {
10118   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10119
10120   return tree_view->priv->bin_window;
10121 }
10122
10123 /**
10124  * pspp_sheet_view_get_path_at_pos:
10125  * @tree_view: A #PsppSheetView.
10126  * @x: The x position to be identified (relative to bin_window).
10127  * @y: The y position to be identified (relative to bin_window).
10128  * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10129  * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10130  * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10131  * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10132  *
10133  * Finds the path at the point (@x, @y), relative to bin_window coordinates
10134  * (please see pspp_sheet_view_get_bin_window()).
10135  * That is, @x and @y are relative to an events coordinates. @x and @y must
10136  * come from an event on the @tree_view only where <literal>event->window ==
10137  * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10138  * things like popup menus. If @path is non-%NULL, then it will be filled
10139  * with the #GtkTreePath at that point.  This path should be freed with
10140  * gtk_tree_path_free().  If @column is non-%NULL, then it will be filled
10141  * with the column at that point.  @cell_x and @cell_y return the coordinates
10142  * relative to the cell background (i.e. the @background_area passed to
10143  * gtk_cell_renderer_render()).  This function is only meaningful if
10144  * @tree_view is realized.  Therefore this function will always return %FALSE
10145  * if @tree_view is not realized or does not have a model.
10146  *
10147  * For converting widget coordinates (eg. the ones you get from
10148  * GtkWidget::query-tooltip), please see
10149  * pspp_sheet_view_convert_widget_to_bin_window_coords().
10150  *
10151  * Return value: %TRUE if a row exists at that coordinate.
10152  **/
10153 gboolean
10154 pspp_sheet_view_get_path_at_pos (PsppSheetView        *tree_view,
10155                                gint                x,
10156                                gint                y,
10157                                GtkTreePath       **path,
10158                                PsppSheetViewColumn **column,
10159                                gint               *cell_x,
10160                                gint               *cell_y)
10161 {
10162   int node;
10163   gint y_offset;
10164
10165   g_return_val_if_fail (tree_view != NULL, FALSE);
10166
10167   if (path)
10168     *path = NULL;
10169   if (column)
10170     *column = NULL;
10171
10172   if (tree_view->priv->bin_window == NULL)
10173     return FALSE;
10174
10175   if (tree_view->priv->row_count == 0)
10176     return FALSE;
10177
10178   if (x > tree_view->priv->hadjustment->upper)
10179     return FALSE;
10180
10181   if (x < 0 || y < 0)
10182     return FALSE;
10183
10184   if (column || cell_x)
10185     {
10186       PsppSheetViewColumn *tmp_column;
10187       PsppSheetViewColumn *last_column = NULL;
10188       GList *list;
10189       gint remaining_x = x;
10190       gboolean found = FALSE;
10191       gboolean rtl;
10192
10193       rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10194       for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10195            list;
10196            list = (rtl ? list->prev : list->next))
10197         {
10198           tmp_column = list->data;
10199
10200           if (tmp_column->visible == FALSE)
10201             continue;
10202
10203           last_column = tmp_column;
10204           if (remaining_x <= tmp_column->width)
10205             {
10206               found = TRUE;
10207
10208               if (column)
10209                 *column = tmp_column;
10210
10211               if (cell_x)
10212                 *cell_x = remaining_x;
10213
10214               break;
10215             }
10216           remaining_x -= tmp_column->width;
10217         }
10218
10219       /* If found is FALSE and there is a last_column, then it the remainder
10220        * space is in that area
10221        */
10222       if (!found)
10223         {
10224           if (last_column)
10225             {
10226               if (column)
10227                 *column = last_column;
10228               
10229               if (cell_x)
10230                 *cell_x = last_column->width + remaining_x;
10231             }
10232           else
10233             {
10234               return FALSE;
10235             }
10236         }
10237     }
10238
10239   y_offset = pspp_sheet_view_find_offset (tree_view,
10240                                           TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10241                                           &node);
10242
10243   if (node < 0)
10244     return FALSE;
10245
10246   if (cell_y)
10247     *cell_y = y_offset;
10248
10249   if (path)
10250     *path = _pspp_sheet_view_find_path (tree_view, node);
10251
10252   return TRUE;
10253 }
10254
10255 /* Computes 'cell_area' from 'background_area', which must be the background
10256    area for a cell.  Set 'subtract_focus_rect' to TRUE to compute the cell area
10257    as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10258    the cell area as passed to _pspp_sheet_view_column_cell_render().
10259
10260    'column' is required to properly adjust 'cell_area->x' and
10261    'cell_area->width'.  It may be set to NULL if these values are not of
10262    interest.  In this case 'cell_area->x' and 'cell_area->width' will be
10263    returned as 0. */
10264 static void
10265 pspp_sheet_view_adjust_cell_area (PsppSheetView        *tree_view,
10266                                   PsppSheetViewColumn  *column,
10267                                   const GdkRectangle   *background_area,
10268                                   gboolean              subtract_focus_rect,
10269                                   GdkRectangle         *cell_area)
10270 {
10271   gint vertical_separator;
10272   gint horizontal_separator;
10273
10274   *cell_area = *background_area;
10275
10276   gtk_widget_style_get (GTK_WIDGET (tree_view),
10277                         "vertical-separator", &vertical_separator,
10278                         "horizontal-separator", &horizontal_separator,
10279                         NULL);
10280   cell_area->x += horizontal_separator / 2;
10281   cell_area->y += vertical_separator / 2;
10282   cell_area->width -= horizontal_separator;
10283   cell_area->height -= vertical_separator;
10284
10285   if (subtract_focus_rect)
10286     {
10287       int focus_line_width;
10288
10289       gtk_widget_style_get (GTK_WIDGET (tree_view),
10290                             "focus-line-width", &focus_line_width,
10291                             NULL);
10292       cell_area->x += focus_line_width;
10293       cell_area->y += focus_line_width;
10294       cell_area->width -= 2 * focus_line_width;
10295       cell_area->height -= 2 * focus_line_width;
10296     }
10297
10298   if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10299     {
10300       gint grid_line_width;
10301       gtk_widget_style_get (GTK_WIDGET (tree_view),
10302                             "grid-line-width", &grid_line_width,
10303                             NULL);
10304
10305       if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10306            || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10307           && column != NULL)
10308         {
10309           PsppSheetViewColumn *first_column, *last_column;
10310           GList *list;
10311
10312           /* Find the last visible column. */
10313           last_column = NULL;
10314           for (list = g_list_last (tree_view->priv->columns);
10315                list;
10316                list = list->prev)
10317             {
10318               PsppSheetViewColumn *c = list->data;
10319               if (c->visible)
10320                 {
10321                   last_column = c;
10322                   break;
10323                 }
10324             }
10325
10326           /* Find the first visible column. */
10327           first_column = NULL;
10328           for (list = g_list_first (tree_view->priv->columns);
10329                list;
10330                list = list->next)
10331             {
10332               PsppSheetViewColumn *c = list->data;
10333               if (c->visible)
10334                 {
10335                   first_column = c;
10336                   break;
10337                 }
10338             }
10339
10340           if (column == first_column)
10341             {
10342               cell_area->width -= grid_line_width / 2;
10343             }
10344           else if (column == last_column)
10345             {
10346               cell_area->x += grid_line_width / 2;
10347               cell_area->width -= grid_line_width / 2;
10348             }
10349           else
10350             {
10351               cell_area->x += grid_line_width / 2;
10352               cell_area->width -= grid_line_width;
10353             }
10354         }
10355
10356       if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10357           || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10358         {
10359           cell_area->y += grid_line_width / 2;
10360           cell_area->height -= grid_line_width;
10361         }
10362     }
10363
10364   if (column == NULL)
10365     {
10366       cell_area->x = 0;
10367       cell_area->width = 0;
10368     }
10369 }
10370
10371 /**
10372  * pspp_sheet_view_get_cell_area:
10373  * @tree_view: a #PsppSheetView
10374  * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10375  * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10376  * @rect: rectangle to fill with cell rect
10377  *
10378  * Fills the bounding rectangle in bin_window coordinates for the cell at the
10379  * row specified by @path and the column specified by @column.  If @path is
10380  * %NULL, or points to a path not currently displayed, the @y and @height fields
10381  * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10382  * fields will be filled with 0.  The sum of all cell rects does not cover the
10383  * entire tree; there are extra pixels in between rows, for example. The
10384  * returned rectangle is equivalent to the @cell_area passed to
10385  * gtk_cell_renderer_render().  This function is only valid if @tree_view is
10386  * realized.
10387  **/
10388 void
10389 pspp_sheet_view_get_cell_area (PsppSheetView        *tree_view,
10390                              GtkTreePath        *path,
10391                              PsppSheetViewColumn  *column,
10392                              GdkRectangle       *rect)
10393 {
10394   GdkRectangle background_area;
10395
10396   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10397   g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10398   g_return_if_fail (rect != NULL);
10399   g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10400   g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10401
10402   pspp_sheet_view_get_background_area (tree_view, path, column,
10403                                        &background_area);
10404   pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10405                                     FALSE, rect);
10406 }
10407
10408 /**
10409  * pspp_sheet_view_get_background_area:
10410  * @tree_view: a #PsppSheetView
10411  * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10412  * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10413  * @rect: rectangle to fill with cell background rect
10414  *
10415  * Fills the bounding rectangle in bin_window coordinates for the cell at the
10416  * row specified by @path and the column specified by @column.  If @path is
10417  * %NULL, or points to a node not found in the tree, the @y and @height fields of
10418  * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10419  * fields will be filled with 0.  The returned rectangle is equivalent to the
10420  * @background_area passed to gtk_cell_renderer_render().  These background
10421  * areas tile to cover the entire bin window.  Contrast with the @cell_area,
10422  * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10423  * itself, excluding surrounding borders.
10424  *
10425  **/
10426 void
10427 pspp_sheet_view_get_background_area (PsppSheetView        *tree_view,
10428                                    GtkTreePath        *path,
10429                                    PsppSheetViewColumn  *column,
10430                                    GdkRectangle       *rect)
10431 {
10432   int node = -1;
10433
10434   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10435   g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10436   g_return_if_fail (rect != NULL);
10437
10438   rect->x = 0;
10439   rect->y = 0;
10440   rect->width = 0;
10441   rect->height = 0;
10442
10443   if (path)
10444     {
10445       /* Get vertical coords */
10446
10447       _pspp_sheet_view_find_node (tree_view, path, &node);
10448       if (node < 0)
10449         return;
10450
10451       rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10452
10453       rect->height = ROW_HEIGHT (tree_view);
10454     }
10455
10456   if (column)
10457     {
10458       gint x2 = 0;
10459
10460       pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10461       rect->width = x2 - rect->x;
10462     }
10463 }
10464
10465 /**
10466  * pspp_sheet_view_get_visible_rect:
10467  * @tree_view: a #PsppSheetView
10468  * @visible_rect: rectangle to fill
10469  *
10470  * Fills @visible_rect with the currently-visible region of the
10471  * buffer, in tree coordinates. Convert to bin_window coordinates with
10472  * pspp_sheet_view_convert_tree_to_bin_window_coords().
10473  * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10474  * scrollable area of the tree.
10475  **/
10476 void
10477 pspp_sheet_view_get_visible_rect (PsppSheetView  *tree_view,
10478                                 GdkRectangle *visible_rect)
10479 {
10480   GtkWidget *widget;
10481
10482   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10483
10484   widget = GTK_WIDGET (tree_view);
10485
10486   if (visible_rect)
10487     {
10488       visible_rect->x = tree_view->priv->hadjustment->value;
10489       visible_rect->y = tree_view->priv->vadjustment->value;
10490       visible_rect->width = widget->allocation.width;
10491       visible_rect->height = widget->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10492     }
10493 }
10494
10495 /**
10496  * pspp_sheet_view_widget_to_tree_coords:
10497  * @tree_view: a #PsppSheetView
10498  * @wx: X coordinate relative to bin_window
10499  * @wy: Y coordinate relative to bin_window
10500  * @tx: return location for tree X coordinate
10501  * @ty: return location for tree Y coordinate
10502  *
10503  * Converts bin_window coordinates to coordinates for the
10504  * tree (the full scrollable area of the tree).
10505  *
10506  * Deprecated: 2.12: Due to historial reasons the name of this function is
10507  * incorrect.  For converting coordinates relative to the widget to
10508  * bin_window coordinates, please see
10509  * pspp_sheet_view_convert_widget_to_bin_window_coords().
10510  *
10511  **/
10512 void
10513 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10514                                       gint         wx,
10515                                       gint         wy,
10516                                       gint        *tx,
10517                                       gint        *ty)
10518 {
10519   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10520
10521   if (tx)
10522     *tx = wx + tree_view->priv->hadjustment->value;
10523   if (ty)
10524     *ty = wy + tree_view->priv->dy;
10525 }
10526
10527 /**
10528  * pspp_sheet_view_tree_to_widget_coords:
10529  * @tree_view: a #PsppSheetView
10530  * @tx: tree X coordinate
10531  * @ty: tree Y coordinate
10532  * @wx: return location for X coordinate relative to bin_window
10533  * @wy: return location for Y coordinate relative to bin_window
10534  *
10535  * Converts tree coordinates (coordinates in full scrollable area of the tree)
10536  * to bin_window coordinates.
10537  *
10538  * Deprecated: 2.12: Due to historial reasons the name of this function is
10539  * incorrect.  For converting bin_window coordinates to coordinates relative
10540  * to bin_window, please see
10541  * pspp_sheet_view_convert_bin_window_to_widget_coords().
10542  *
10543  **/
10544 void
10545 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10546                                      gint         tx,
10547                                      gint         ty,
10548                                      gint        *wx,
10549                                      gint        *wy)
10550 {
10551   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10552
10553   if (wx)
10554     *wx = tx - tree_view->priv->hadjustment->value;
10555   if (wy)
10556     *wy = ty - tree_view->priv->dy;
10557 }
10558
10559
10560 /**
10561  * pspp_sheet_view_convert_widget_to_tree_coords:
10562  * @tree_view: a #PsppSheetView
10563  * @wx: X coordinate relative to the widget
10564  * @wy: Y coordinate relative to the widget
10565  * @tx: return location for tree X coordinate
10566  * @ty: return location for tree Y coordinate
10567  *
10568  * Converts widget coordinates to coordinates for the
10569  * tree (the full scrollable area of the tree).
10570  *
10571  * Since: 2.12
10572  **/
10573 void
10574 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10575                                              gint         wx,
10576                                              gint         wy,
10577                                              gint        *tx,
10578                                              gint        *ty)
10579 {
10580   gint x, y;
10581
10582   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10583
10584   pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10585                                                      wx, wy,
10586                                                      &x, &y);
10587   pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10588                                                    x, y,
10589                                                    tx, ty);
10590 }
10591
10592 /**
10593  * pspp_sheet_view_convert_tree_to_widget_coords:
10594  * @tree_view: a #PsppSheetView
10595  * @tx: X coordinate relative to the tree
10596  * @ty: Y coordinate relative to the tree
10597  * @wx: return location for widget X coordinate
10598  * @wy: return location for widget Y coordinate
10599  *
10600  * Converts tree coordinates (coordinates in full scrollable area of the tree)
10601  * to widget coordinates.
10602  *
10603  * Since: 2.12
10604  **/
10605 void
10606 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10607                                              gint         tx,
10608                                              gint         ty,
10609                                              gint        *wx,
10610                                              gint        *wy)
10611 {
10612   gint x, y;
10613
10614   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10615
10616   pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10617                                                    tx, ty,
10618                                                    &x, &y);
10619   pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10620                                                      x, y,
10621                                                      wx, wy);
10622 }
10623
10624 /**
10625  * pspp_sheet_view_convert_widget_to_bin_window_coords:
10626  * @tree_view: a #PsppSheetView
10627  * @wx: X coordinate relative to the widget
10628  * @wy: Y coordinate relative to the widget
10629  * @bx: return location for bin_window X coordinate
10630  * @by: return location for bin_window Y coordinate
10631  *
10632  * Converts widget coordinates to coordinates for the bin_window
10633  * (see pspp_sheet_view_get_bin_window()).
10634  *
10635  * Since: 2.12
10636  **/
10637 void
10638 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10639                                                    gint         wx,
10640                                                    gint         wy,
10641                                                    gint        *bx,
10642                                                    gint        *by)
10643 {
10644   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10645
10646   if (bx)
10647     *bx = wx + tree_view->priv->hadjustment->value;
10648   if (by)
10649     *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10650 }
10651
10652 /**
10653  * pspp_sheet_view_convert_bin_window_to_widget_coords:
10654  * @tree_view: a #PsppSheetView
10655  * @bx: bin_window X coordinate
10656  * @by: bin_window Y coordinate
10657  * @wx: return location for widget X coordinate
10658  * @wy: return location for widget Y coordinate
10659  *
10660  * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10661  * to widget relative coordinates.
10662  *
10663  * Since: 2.12
10664  **/
10665 void
10666 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10667                                                    gint         bx,
10668                                                    gint         by,
10669                                                    gint        *wx,
10670                                                    gint        *wy)
10671 {
10672   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10673
10674   if (wx)
10675     *wx = bx - tree_view->priv->hadjustment->value;
10676   if (wy)
10677     *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10678 }
10679
10680 /**
10681  * pspp_sheet_view_convert_tree_to_bin_window_coords:
10682  * @tree_view: a #PsppSheetView
10683  * @tx: tree X coordinate
10684  * @ty: tree Y coordinate
10685  * @bx: return location for X coordinate relative to bin_window
10686  * @by: return location for Y coordinate relative to bin_window
10687  *
10688  * Converts tree coordinates (coordinates in full scrollable area of the tree)
10689  * to bin_window coordinates.
10690  *
10691  * Since: 2.12
10692  **/
10693 void
10694 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10695                                                  gint         tx,
10696                                                  gint         ty,
10697                                                  gint        *bx,
10698                                                  gint        *by)
10699 {
10700   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10701
10702   if (bx)
10703     *bx = tx;
10704   if (by)
10705     *by = ty - tree_view->priv->dy;
10706 }
10707
10708 /**
10709  * pspp_sheet_view_convert_bin_window_to_tree_coords:
10710  * @tree_view: a #PsppSheetView
10711  * @bx: X coordinate relative to bin_window
10712  * @by: Y coordinate relative to bin_window
10713  * @tx: return location for tree X coordinate
10714  * @ty: return location for tree Y coordinate
10715  *
10716  * Converts bin_window coordinates to coordinates for the
10717  * tree (the full scrollable area of the tree).
10718  *
10719  * Since: 2.12
10720  **/
10721 void
10722 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10723                                                  gint         bx,
10724                                                  gint         by,
10725                                                  gint        *tx,
10726                                                  gint        *ty)
10727 {
10728   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10729
10730   if (tx)
10731     *tx = bx;
10732   if (ty)
10733     *ty = by + tree_view->priv->dy;
10734 }
10735
10736
10737
10738 /**
10739  * pspp_sheet_view_get_visible_range:
10740  * @tree_view: A #PsppSheetView
10741  * @start_path: (allow-none): Return location for start of region, or %NULL.
10742  * @end_path: (allow-none): Return location for end of region, or %NULL.
10743  *
10744  * Sets @start_path and @end_path to be the first and last visible path.
10745  * Note that there may be invisible paths in between.
10746  *
10747  * The paths should be freed with gtk_tree_path_free() after use.
10748  *
10749  * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
10750  *
10751  * Since: 2.8
10752  **/
10753 gboolean
10754 pspp_sheet_view_get_visible_range (PsppSheetView  *tree_view,
10755                                  GtkTreePath **start_path,
10756                                  GtkTreePath **end_path)
10757 {
10758   int node;
10759   gboolean retval;
10760   
10761   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10762
10763   if (!tree_view->priv->row_count)
10764     return FALSE;
10765
10766   retval = TRUE;
10767
10768   if (start_path)
10769     {
10770       pspp_sheet_view_find_offset (tree_view,
10771                                    TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
10772                                    &node);
10773       if (node >= 0)
10774         *start_path = _pspp_sheet_view_find_path (tree_view, node);
10775       else
10776         retval = FALSE;
10777     }
10778
10779   if (end_path)
10780     {
10781       gint y;
10782
10783       if (tree_view->priv->height < tree_view->priv->vadjustment->page_size)
10784         y = tree_view->priv->height - 1;
10785       else
10786         y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, tree_view->priv->vadjustment->page_size) - 1;
10787
10788       pspp_sheet_view_find_offset (tree_view, y, &node);
10789       if (node >= 0)
10790         *end_path = _pspp_sheet_view_find_path (tree_view, node);
10791       else
10792         retval = FALSE;
10793     }
10794
10795   return retval;
10796 }
10797
10798 static void
10799 unset_reorderable (PsppSheetView *tree_view)
10800 {
10801   if (tree_view->priv->reorderable)
10802     {
10803       tree_view->priv->reorderable = FALSE;
10804       g_object_notify (G_OBJECT (tree_view), "reorderable");
10805     }
10806 }
10807
10808 /**
10809  * pspp_sheet_view_enable_model_drag_source:
10810  * @tree_view: a #PsppSheetView
10811  * @start_button_mask: Mask of allowed buttons to start drag
10812  * @targets: the table of targets that the drag will support
10813  * @n_targets: the number of items in @targets
10814  * @actions: the bitmask of possible actions for a drag from this
10815  *    widget
10816  *
10817  * Turns @tree_view into a drag source for automatic DND. Calling this
10818  * method sets #PsppSheetView:reorderable to %FALSE.
10819  **/
10820 void
10821 pspp_sheet_view_enable_model_drag_source (PsppSheetView              *tree_view,
10822                                         GdkModifierType           start_button_mask,
10823                                         const GtkTargetEntry     *targets,
10824                                         gint                      n_targets,
10825                                         GdkDragAction             actions)
10826 {
10827   TreeViewDragInfo *di;
10828
10829   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10830
10831   gtk_drag_source_set (GTK_WIDGET (tree_view),
10832                        0,
10833                        targets,
10834                        n_targets,
10835                        actions);
10836
10837   di = ensure_info (tree_view);
10838
10839   di->start_button_mask = start_button_mask;
10840   di->source_actions = actions;
10841   di->source_set = TRUE;
10842
10843   unset_reorderable (tree_view);
10844 }
10845
10846 /**
10847  * pspp_sheet_view_enable_model_drag_dest:
10848  * @tree_view: a #PsppSheetView
10849  * @targets: the table of targets that the drag will support
10850  * @n_targets: the number of items in @targets
10851  * @actions: the bitmask of possible actions for a drag from this
10852  *    widget
10853  * 
10854  * Turns @tree_view into a drop destination for automatic DND. Calling
10855  * this method sets #PsppSheetView:reorderable to %FALSE.
10856  **/
10857 void
10858 pspp_sheet_view_enable_model_drag_dest (PsppSheetView              *tree_view,
10859                                       const GtkTargetEntry     *targets,
10860                                       gint                      n_targets,
10861                                       GdkDragAction             actions)
10862 {
10863   TreeViewDragInfo *di;
10864
10865   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10866
10867   gtk_drag_dest_set (GTK_WIDGET (tree_view),
10868                      0,
10869                      targets,
10870                      n_targets,
10871                      actions);
10872
10873   di = ensure_info (tree_view);
10874   di->dest_set = TRUE;
10875
10876   unset_reorderable (tree_view);
10877 }
10878
10879 /**
10880  * pspp_sheet_view_unset_rows_drag_source:
10881  * @tree_view: a #PsppSheetView
10882  *
10883  * Undoes the effect of
10884  * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
10885  * #PsppSheetView:reorderable to %FALSE.
10886  **/
10887 void
10888 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
10889 {
10890   TreeViewDragInfo *di;
10891
10892   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10893
10894   di = get_info (tree_view);
10895
10896   if (di)
10897     {
10898       if (di->source_set)
10899         {
10900           gtk_drag_source_unset (GTK_WIDGET (tree_view));
10901           di->source_set = FALSE;
10902         }
10903
10904       if (!di->dest_set && !di->source_set)
10905         remove_info (tree_view);
10906     }
10907   
10908   unset_reorderable (tree_view);
10909 }
10910
10911 /**
10912  * pspp_sheet_view_unset_rows_drag_dest:
10913  * @tree_view: a #PsppSheetView
10914  *
10915  * Undoes the effect of
10916  * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
10917  * #PsppSheetView:reorderable to %FALSE.
10918  **/
10919 void
10920 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
10921 {
10922   TreeViewDragInfo *di;
10923
10924   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10925
10926   di = get_info (tree_view);
10927
10928   if (di)
10929     {
10930       if (di->dest_set)
10931         {
10932           gtk_drag_dest_unset (GTK_WIDGET (tree_view));
10933           di->dest_set = FALSE;
10934         }
10935
10936       if (!di->dest_set && !di->source_set)
10937         remove_info (tree_view);
10938     }
10939
10940   unset_reorderable (tree_view);
10941 }
10942
10943 /**
10944  * pspp_sheet_view_set_drag_dest_row:
10945  * @tree_view: a #PsppSheetView
10946  * @path: (allow-none): The path of the row to highlight, or %NULL.
10947  * @pos: Specifies whether to drop before, after or into the row
10948  * 
10949  * Sets the row that is highlighted for feedback.
10950  **/
10951 void
10952 pspp_sheet_view_set_drag_dest_row (PsppSheetView            *tree_view,
10953                                  GtkTreePath            *path,
10954                                  PsppSheetViewDropPosition pos)
10955 {
10956   GtkTreePath *current_dest;
10957
10958   /* Note; this function is exported to allow a custom DND
10959    * implementation, so it can't touch TreeViewDragInfo
10960    */
10961
10962   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10963
10964   current_dest = NULL;
10965
10966   if (tree_view->priv->drag_dest_row)
10967     {
10968       current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
10969       gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
10970     }
10971
10972   /* special case a drop on an empty model */
10973   tree_view->priv->empty_view_drop = 0;
10974
10975   if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
10976       && gtk_tree_path_get_depth (path) == 1
10977       && gtk_tree_path_get_indices (path)[0] == 0)
10978     {
10979       gint n_children;
10980
10981       n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
10982                                                    NULL);
10983
10984       if (!n_children)
10985         tree_view->priv->empty_view_drop = 1;
10986     }
10987
10988   tree_view->priv->drag_dest_pos = pos;
10989
10990   if (path)
10991     {
10992       tree_view->priv->drag_dest_row =
10993         gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
10994       pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
10995     }
10996   else
10997     tree_view->priv->drag_dest_row = NULL;
10998
10999   if (current_dest)
11000     {
11001       int node, new_node;
11002
11003       _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11004       _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11005
11006       if (node >= 0)
11007         {
11008           new_node = pspp_sheet_view_node_next (tree_view, node);
11009           if (new_node >= 0)
11010             _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11011
11012           new_node = pspp_sheet_view_node_prev (tree_view, node);
11013           if (new_node >= 0)
11014             _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11015         }
11016       gtk_tree_path_free (current_dest);
11017     }
11018 }
11019
11020 /**
11021  * pspp_sheet_view_get_drag_dest_row:
11022  * @tree_view: a #PsppSheetView
11023  * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11024  * @pos: (allow-none): Return location for the drop position, or %NULL
11025  * 
11026  * Gets information about the row that is highlighted for feedback.
11027  **/
11028 void
11029 pspp_sheet_view_get_drag_dest_row (PsppSheetView              *tree_view,
11030                                  GtkTreePath             **path,
11031                                  PsppSheetViewDropPosition  *pos)
11032 {
11033   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11034
11035   if (path)
11036     {
11037       if (tree_view->priv->drag_dest_row)
11038         *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11039       else
11040         {
11041           if (tree_view->priv->empty_view_drop)
11042             *path = gtk_tree_path_new_from_indices (0, -1);
11043           else
11044             *path = NULL;
11045         }
11046     }
11047
11048   if (pos)
11049     *pos = tree_view->priv->drag_dest_pos;
11050 }
11051
11052 /**
11053  * pspp_sheet_view_get_dest_row_at_pos:
11054  * @tree_view: a #PsppSheetView
11055  * @drag_x: the position to determine the destination row for
11056  * @drag_y: the position to determine the destination row for
11057  * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11058  * @pos: (allow-none): Return location for the drop position, or %NULL
11059  * 
11060  * Determines the destination row for a given position.  @drag_x and
11061  * @drag_y are expected to be in widget coordinates.  This function is only
11062  * meaningful if @tree_view is realized.  Therefore this function will always
11063  * return %FALSE if @tree_view is not realized or does not have a model.
11064  * 
11065  * Return value: whether there is a row at the given position, %TRUE if this
11066  * is indeed the case.
11067  **/
11068 gboolean
11069 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView             *tree_view,
11070                                    gint                     drag_x,
11071                                    gint                     drag_y,
11072                                    GtkTreePath            **path,
11073                                    PsppSheetViewDropPosition *pos)
11074 {
11075   gint cell_y;
11076   gint bin_x, bin_y;
11077   gdouble offset_into_row;
11078   gdouble third;
11079   GdkRectangle cell;
11080   PsppSheetViewColumn *column = NULL;
11081   GtkTreePath *tmp_path = NULL;
11082
11083   /* Note; this function is exported to allow a custom DND
11084    * implementation, so it can't touch TreeViewDragInfo
11085    */
11086
11087   g_return_val_if_fail (tree_view != NULL, FALSE);
11088   g_return_val_if_fail (drag_x >= 0, FALSE);
11089   g_return_val_if_fail (drag_y >= 0, FALSE);
11090
11091   if (path)
11092     *path = NULL;
11093
11094   if (tree_view->priv->bin_window == NULL)
11095     return FALSE;
11096
11097   if (tree_view->priv->row_count == 0)
11098     return FALSE;
11099
11100   /* If in the top third of a row, we drop before that row; if
11101    * in the bottom third, drop after that row; if in the middle,
11102    * and the row has children, drop into the row.
11103    */
11104   pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11105                                                      &bin_x, &bin_y);
11106
11107   if (!pspp_sheet_view_get_path_at_pos (tree_view,
11108                                       bin_x,
11109                                       bin_y,
11110                                       &tmp_path,
11111                                       &column,
11112                                       NULL,
11113                                       &cell_y))
11114     return FALSE;
11115
11116   pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11117                                      &cell);
11118
11119   offset_into_row = cell_y;
11120
11121   if (path)
11122     *path = tmp_path;
11123   else
11124     gtk_tree_path_free (tmp_path);
11125
11126   tmp_path = NULL;
11127
11128   third = cell.height / 3.0;
11129
11130   if (pos)
11131     {
11132       if (offset_into_row < third)
11133         {
11134           *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11135         }
11136       else if (offset_into_row < (cell.height / 2.0))
11137         {
11138           *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11139         }
11140       else if (offset_into_row < third * 2.0)
11141         {
11142           *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11143         }
11144       else
11145         {
11146           *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11147         }
11148     }
11149
11150   return TRUE;
11151 }
11152
11153
11154
11155 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11156 /**
11157  * pspp_sheet_view_create_row_drag_icon:
11158  * @tree_view: a #PsppSheetView
11159  * @path: a #GtkTreePath in @tree_view
11160  *
11161  * Creates a #GdkPixmap representation of the row at @path.  
11162  * This image is used for a drag icon.
11163  *
11164  * Return value: a newly-allocated pixmap of the drag icon.
11165  **/
11166 GdkPixmap *
11167 pspp_sheet_view_create_row_drag_icon (PsppSheetView  *tree_view,
11168                                     GtkTreePath  *path)
11169 {
11170   GtkTreeIter   iter;
11171   int node;
11172   gint cell_offset;
11173   GList *list;
11174   GdkRectangle background_area;
11175   GdkRectangle expose_area;
11176   GtkWidget *widget;
11177   /* start drawing inside the black outline */
11178   gint x = 1, y = 1;
11179   GdkDrawable *drawable;
11180   gint bin_window_width;
11181   gboolean rtl;
11182
11183   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11184   g_return_val_if_fail (path != NULL, NULL);
11185
11186   widget = GTK_WIDGET (tree_view);
11187
11188   if (!gtk_widget_get_realized (widget))
11189     return NULL;
11190
11191   _pspp_sheet_view_find_node (tree_view,
11192                             path,
11193                             &node);
11194
11195   if (node < 0)
11196     return NULL;
11197
11198   if (!gtk_tree_model_get_iter (tree_view->priv->model,
11199                                 &iter,
11200                                 path))
11201     return NULL;
11202   
11203   cell_offset = x;
11204
11205   background_area.y = y;
11206   background_area.height = ROW_HEIGHT (tree_view);
11207
11208   gdk_drawable_get_size (tree_view->priv->bin_window,
11209                          &bin_window_width, NULL);
11210
11211   drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11212                              bin_window_width + 2,
11213                              background_area.height + 2,
11214                              -1);
11215
11216   expose_area.x = 0;
11217   expose_area.y = 0;
11218   expose_area.width = bin_window_width + 2;
11219   expose_area.height = background_area.height + 2;
11220
11221   gdk_draw_rectangle (drawable,
11222                       widget->style->base_gc [gtk_widget_get_state (widget)],
11223                       TRUE,
11224                       0, 0,
11225                       bin_window_width + 2,
11226                       background_area.height + 2);
11227
11228   rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11229
11230   for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11231       list;
11232       list = (rtl ? list->prev : list->next))
11233     {
11234       PsppSheetViewColumn *column = list->data;
11235       GdkRectangle cell_area;
11236       gint vertical_separator;
11237
11238       if (!column->visible)
11239         continue;
11240
11241       pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11242
11243       background_area.x = cell_offset;
11244       background_area.width = column->width;
11245
11246       gtk_widget_style_get (widget,
11247                             "vertical-separator", &vertical_separator,
11248                             NULL);
11249
11250       cell_area = background_area;
11251
11252       cell_area.y += vertical_separator / 2;
11253       cell_area.height -= vertical_separator;
11254
11255       if (pspp_sheet_view_column_cell_is_visible (column))
11256         _pspp_sheet_view_column_cell_render (column,
11257                                              drawable,
11258                                              &background_area,
11259                                              &cell_area,
11260                                              &expose_area,
11261                                              0);
11262       cell_offset += column->width;
11263     }
11264
11265   gdk_draw_rectangle (drawable,
11266                       widget->style->black_gc,
11267                       FALSE,
11268                       0, 0,
11269                       bin_window_width + 1,
11270                       background_area.height + 1);
11271
11272   return drawable;
11273 }
11274
11275
11276 /**
11277  * pspp_sheet_view_set_destroy_count_func:
11278  * @tree_view: A #PsppSheetView
11279  * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11280  * @data: (allow-none): User data to be passed to @func, or %NULL
11281  * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11282  *
11283  * This function should almost never be used.  It is meant for private use by
11284  * ATK for determining the number of visible children that are removed when a row is deleted.
11285  **/
11286 void
11287 pspp_sheet_view_set_destroy_count_func (PsppSheetView             *tree_view,
11288                                       PsppSheetDestroyCountFunc  func,
11289                                       gpointer                 data,
11290                                       GDestroyNotify           destroy)
11291 {
11292   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11293
11294   if (tree_view->priv->destroy_count_destroy)
11295     tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11296
11297   tree_view->priv->destroy_count_func = func;
11298   tree_view->priv->destroy_count_data = data;
11299   tree_view->priv->destroy_count_destroy = destroy;
11300 }
11301
11302
11303 /*
11304  * Interactive search
11305  */
11306
11307 /**
11308  * pspp_sheet_view_set_enable_search:
11309  * @tree_view: A #PsppSheetView
11310  * @enable_search: %TRUE, if the user can search interactively
11311  *
11312  * If @enable_search is set, then the user can type in text to search through
11313  * the tree interactively (this is sometimes called "typeahead find").
11314  * 
11315  * Note that even if this is %FALSE, the user can still initiate a search 
11316  * using the "start-interactive-search" key binding.
11317  */
11318 void
11319 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11320                                  gboolean     enable_search)
11321 {
11322   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11323
11324   enable_search = !!enable_search;
11325   
11326   if (tree_view->priv->enable_search != enable_search)
11327     {
11328        tree_view->priv->enable_search = enable_search;
11329        g_object_notify (G_OBJECT (tree_view), "enable-search");
11330     }
11331 }
11332
11333 /**
11334  * pspp_sheet_view_get_enable_search:
11335  * @tree_view: A #PsppSheetView
11336  *
11337  * Returns whether or not the tree allows to start interactive searching 
11338  * by typing in text.
11339  *
11340  * Return value: whether or not to let the user search interactively
11341  */
11342 gboolean
11343 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11344 {
11345   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11346
11347   return tree_view->priv->enable_search;
11348 }
11349
11350
11351 /**
11352  * pspp_sheet_view_get_search_column:
11353  * @tree_view: A #PsppSheetView
11354  *
11355  * Gets the column searched on by the interactive search code.
11356  *
11357  * Return value: the column the interactive search code searches in.
11358  */
11359 gint
11360 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11361 {
11362   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11363
11364   return (tree_view->priv->search_column);
11365 }
11366
11367 /**
11368  * pspp_sheet_view_set_search_column:
11369  * @tree_view: A #PsppSheetView
11370  * @column: the column of the model to search in, or -1 to disable searching
11371  *
11372  * Sets @column as the column where the interactive search code should
11373  * search in for the current model. 
11374  * 
11375  * If the search column is set, users can use the "start-interactive-search"
11376  * key binding to bring up search popup. The enable-search property controls
11377  * whether simply typing text will also start an interactive search.
11378  *
11379  * Note that @column refers to a column of the current model. The search 
11380  * column is reset to -1 when the model is changed.
11381  */
11382 void
11383 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11384                                  gint         column)
11385 {
11386   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11387   g_return_if_fail (column >= -1);
11388
11389   if (tree_view->priv->search_column == column)
11390     return;
11391
11392   tree_view->priv->search_column = column;
11393   g_object_notify (G_OBJECT (tree_view), "search-column");
11394 }
11395
11396 /**
11397  * pspp_sheet_view_get_search_equal_func:
11398  * @tree_view: A #PsppSheetView
11399  *
11400  * Returns the compare function currently in use.
11401  *
11402  * Return value: the currently used compare function for the search code.
11403  */
11404
11405 PsppSheetViewSearchEqualFunc
11406 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11407 {
11408   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11409
11410   return tree_view->priv->search_equal_func;
11411 }
11412
11413 /**
11414  * pspp_sheet_view_set_search_equal_func:
11415  * @tree_view: A #PsppSheetView
11416  * @search_equal_func: the compare function to use during the search
11417  * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11418  * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11419  *
11420  * Sets the compare function for the interactive search capabilities; note
11421  * that somewhat like strcmp() returning 0 for equality
11422  * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11423  **/
11424 void
11425 pspp_sheet_view_set_search_equal_func (PsppSheetView                *tree_view,
11426                                      PsppSheetViewSearchEqualFunc  search_equal_func,
11427                                      gpointer                    search_user_data,
11428                                      GDestroyNotify              search_destroy)
11429 {
11430   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11431   g_return_if_fail (search_equal_func != NULL);
11432
11433   if (tree_view->priv->search_destroy)
11434     tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11435
11436   tree_view->priv->search_equal_func = search_equal_func;
11437   tree_view->priv->search_user_data = search_user_data;
11438   tree_view->priv->search_destroy = search_destroy;
11439   if (tree_view->priv->search_equal_func == NULL)
11440     tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11441 }
11442
11443 /**
11444  * pspp_sheet_view_get_search_entry:
11445  * @tree_view: A #PsppSheetView
11446  *
11447  * Returns the #GtkEntry which is currently in use as interactive search
11448  * entry for @tree_view.  In case the built-in entry is being used, %NULL
11449  * will be returned.
11450  *
11451  * Return value: the entry currently in use as search entry.
11452  *
11453  * Since: 2.10
11454  */
11455 GtkEntry *
11456 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11457 {
11458   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11459
11460   if (tree_view->priv->search_custom_entry_set)
11461     return GTK_ENTRY (tree_view->priv->search_entry);
11462
11463   return NULL;
11464 }
11465
11466 /**
11467  * pspp_sheet_view_set_search_entry:
11468  * @tree_view: A #PsppSheetView
11469  * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11470  *
11471  * Sets the entry which the interactive search code will use for this
11472  * @tree_view.  This is useful when you want to provide a search entry
11473  * in our interface at all time at a fixed position.  Passing %NULL for
11474  * @entry will make the interactive search code use the built-in popup
11475  * entry again.
11476  *
11477  * Since: 2.10
11478  */
11479 void
11480 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11481                                 GtkEntry    *entry)
11482 {
11483   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11484   g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11485
11486   if (tree_view->priv->search_custom_entry_set)
11487     {
11488       if (tree_view->priv->search_entry_changed_id)
11489         {
11490           g_signal_handler_disconnect (tree_view->priv->search_entry,
11491                                        tree_view->priv->search_entry_changed_id);
11492           tree_view->priv->search_entry_changed_id = 0;
11493         }
11494       g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11495                                             G_CALLBACK (pspp_sheet_view_search_key_press_event),
11496                                             tree_view);
11497
11498       g_object_unref (tree_view->priv->search_entry);
11499     }
11500   else if (tree_view->priv->search_window)
11501     {
11502       gtk_widget_destroy (tree_view->priv->search_window);
11503
11504       tree_view->priv->search_window = NULL;
11505     }
11506
11507   if (entry)
11508     {
11509       tree_view->priv->search_entry = g_object_ref (entry);
11510       tree_view->priv->search_custom_entry_set = TRUE;
11511
11512       if (tree_view->priv->search_entry_changed_id == 0)
11513         {
11514           tree_view->priv->search_entry_changed_id =
11515             g_signal_connect (tree_view->priv->search_entry, "changed",
11516                               G_CALLBACK (pspp_sheet_view_search_init),
11517                               tree_view);
11518         }
11519       
11520         g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11521                           G_CALLBACK (pspp_sheet_view_search_key_press_event),
11522                           tree_view);
11523
11524         pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11525     }
11526   else
11527     {
11528       tree_view->priv->search_entry = NULL;
11529       tree_view->priv->search_custom_entry_set = FALSE;
11530     }
11531 }
11532
11533 /**
11534  * pspp_sheet_view_set_search_position_func:
11535  * @tree_view: A #PsppSheetView
11536  * @func: (allow-none): the function to use to position the search dialog, or %NULL
11537  *    to use the default search position function
11538  * @data: (allow-none): user data to pass to @func, or %NULL
11539  * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11540  *
11541  * Sets the function to use when positioning the search dialog.
11542  *
11543  * Since: 2.10
11544  **/
11545 void
11546 pspp_sheet_view_set_search_position_func (PsppSheetView                   *tree_view,
11547                                         PsppSheetViewSearchPositionFunc  func,
11548                                         gpointer                       user_data,
11549                                         GDestroyNotify                 destroy)
11550 {
11551   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11552
11553   if (tree_view->priv->search_position_destroy)
11554     tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11555
11556   tree_view->priv->search_position_func = func;
11557   tree_view->priv->search_position_user_data = user_data;
11558   tree_view->priv->search_position_destroy = destroy;
11559   if (tree_view->priv->search_position_func == NULL)
11560     tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11561 }
11562
11563 /**
11564  * pspp_sheet_view_get_search_position_func:
11565  * @tree_view: A #PsppSheetView
11566  *
11567  * Returns the positioning function currently in use.
11568  *
11569  * Return value: the currently used function for positioning the search dialog.
11570  *
11571  * Since: 2.10
11572  */
11573 PsppSheetViewSearchPositionFunc
11574 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11575 {
11576   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11577
11578   return tree_view->priv->search_position_func;
11579 }
11580
11581
11582 static void
11583 pspp_sheet_view_search_dialog_hide (GtkWidget   *search_dialog,
11584                                   PsppSheetView *tree_view)
11585 {
11586   if (tree_view->priv->disable_popdown)
11587     return;
11588
11589   if (tree_view->priv->search_entry_changed_id)
11590     {
11591       g_signal_handler_disconnect (tree_view->priv->search_entry,
11592                                    tree_view->priv->search_entry_changed_id);
11593       tree_view->priv->search_entry_changed_id = 0;
11594     }
11595   if (tree_view->priv->typeselect_flush_timeout)
11596     {
11597       g_source_remove (tree_view->priv->typeselect_flush_timeout);
11598       tree_view->priv->typeselect_flush_timeout = 0;
11599     }
11600         
11601   if (gtk_widget_get_visible (search_dialog))
11602     {
11603       /* send focus-in event */
11604       send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11605       gtk_widget_hide (search_dialog);
11606       gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11607       send_focus_change (GTK_WIDGET (tree_view), TRUE);
11608     }
11609 }
11610
11611 static void
11612 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11613                                     GtkWidget   *search_dialog,
11614                                     gpointer     user_data)
11615 {
11616   gint x, y;
11617   gint tree_x, tree_y;
11618   gint tree_width, tree_height;
11619   GdkWindow *tree_window = GTK_WIDGET (tree_view)->window;
11620   GdkScreen *screen = gdk_drawable_get_screen (tree_window);
11621   GtkRequisition requisition;
11622   gint monitor_num;
11623   GdkRectangle monitor;
11624
11625   monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11626   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11627
11628   gtk_widget_realize (search_dialog);
11629
11630   gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11631   gdk_drawable_get_size (tree_window,
11632                          &tree_width,
11633                          &tree_height);
11634   gtk_widget_size_request (search_dialog, &requisition);
11635
11636   if (tree_x + tree_width > gdk_screen_get_width (screen))
11637     x = gdk_screen_get_width (screen) - requisition.width;
11638   else if (tree_x + tree_width - requisition.width < 0)
11639     x = 0;
11640   else
11641     x = tree_x + tree_width - requisition.width;
11642
11643   if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11644     y = gdk_screen_get_height (screen) - requisition.height;
11645   else if (tree_y + tree_height < 0) /* isn't really possible ... */
11646     y = 0;
11647   else
11648     y = tree_y + tree_height;
11649
11650   gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11651 }
11652
11653 static void
11654 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11655                                       GtkMenu  *menu,
11656                                       gpointer  data)
11657 {
11658   PsppSheetView *tree_view = (PsppSheetView *)data;
11659
11660   tree_view->priv->disable_popdown = 1;
11661   g_signal_connect (menu, "hide",
11662                     G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11663 }
11664
11665 /* Because we're visible but offscreen, we just set a flag in the preedit
11666  * callback.
11667  */
11668 static void
11669 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11670                                       PsppSheetView  *tree_view)
11671 {
11672   tree_view->priv->imcontext_changed = 1;
11673   if (tree_view->priv->typeselect_flush_timeout)
11674     {
11675       g_source_remove (tree_view->priv->typeselect_flush_timeout);
11676       tree_view->priv->typeselect_flush_timeout =
11677         gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11678                        (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11679                        tree_view);
11680     }
11681
11682 }
11683
11684 static void
11685 pspp_sheet_view_search_activate (GtkEntry    *entry,
11686                                PsppSheetView *tree_view)
11687 {
11688   GtkTreePath *path;
11689   int node;
11690
11691   pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11692                                     tree_view);
11693
11694   /* If we have a row selected and it's the cursor row, we activate
11695    * the row XXX */
11696   if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11697     {
11698       path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11699       
11700       _pspp_sheet_view_find_node (tree_view, path, &node);
11701       
11702       if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11703         pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11704       
11705       gtk_tree_path_free (path);
11706     }
11707 }
11708
11709 static gboolean
11710 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11711 {
11712   PsppSheetView *tree_view = (PsppSheetView *)data;
11713
11714   tree_view->priv->disable_popdown = 0;
11715
11716   return FALSE;
11717 }
11718
11719 static void
11720 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11721                                      gpointer   data)
11722 {
11723   gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11724 }
11725
11726 static gboolean
11727 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11728                                    GdkEventAny *event,
11729                                    PsppSheetView *tree_view)
11730 {
11731   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11732
11733   pspp_sheet_view_search_dialog_hide (widget, tree_view);
11734
11735   return TRUE;
11736 }
11737
11738 static gboolean
11739 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11740                                          GdkEventButton *event,
11741                                          PsppSheetView *tree_view)
11742 {
11743   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11744
11745   pspp_sheet_view_search_dialog_hide (widget, tree_view);
11746
11747   if (event->window == tree_view->priv->bin_window)
11748     pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
11749
11750   return TRUE;
11751 }
11752
11753 static gboolean
11754 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
11755                                    GdkEventScroll *event,
11756                                    PsppSheetView *tree_view)
11757 {
11758   gboolean retval = FALSE;
11759
11760   if (event->direction == GDK_SCROLL_UP)
11761     {
11762       pspp_sheet_view_search_move (widget, tree_view, TRUE);
11763       retval = TRUE;
11764     }
11765   else if (event->direction == GDK_SCROLL_DOWN)
11766     {
11767       pspp_sheet_view_search_move (widget, tree_view, FALSE);
11768       retval = TRUE;
11769     }
11770
11771   /* renew the flush timeout */
11772   if (retval && tree_view->priv->typeselect_flush_timeout
11773       && !tree_view->priv->search_custom_entry_set)
11774     {
11775       g_source_remove (tree_view->priv->typeselect_flush_timeout);
11776       tree_view->priv->typeselect_flush_timeout =
11777         gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11778                        (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11779                        tree_view);
11780     }
11781
11782   return retval;
11783 }
11784
11785 static gboolean
11786 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
11787                                       GdkEventKey *event,
11788                                       PsppSheetView *tree_view)
11789 {
11790   gboolean retval = FALSE;
11791
11792   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11793   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11794
11795   /* close window and cancel the search */
11796   if (!tree_view->priv->search_custom_entry_set
11797       && (event->keyval == GDK_Escape ||
11798           event->keyval == GDK_Tab ||
11799             event->keyval == GDK_KP_Tab ||
11800             event->keyval == GDK_ISO_Left_Tab))
11801     {
11802       pspp_sheet_view_search_dialog_hide (widget, tree_view);
11803       return TRUE;
11804     }
11805
11806   /* select previous matching iter */
11807   if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
11808     {
11809       if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11810         gtk_widget_error_bell (widget);
11811
11812       retval = TRUE;
11813     }
11814
11815   if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
11816       && (event->keyval == GDK_g || event->keyval == GDK_G))
11817     {
11818       if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11819         gtk_widget_error_bell (widget);
11820
11821       retval = TRUE;
11822     }
11823
11824   /* select next matching iter */
11825   if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
11826     {
11827       if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11828         gtk_widget_error_bell (widget);
11829
11830       retval = TRUE;
11831     }
11832
11833   if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
11834       && (event->keyval == GDK_g || event->keyval == GDK_G))
11835     {
11836       if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11837         gtk_widget_error_bell (widget);
11838
11839       retval = TRUE;
11840     }
11841
11842   /* renew the flush timeout */
11843   if (retval && tree_view->priv->typeselect_flush_timeout
11844       && !tree_view->priv->search_custom_entry_set)
11845     {
11846       g_source_remove (tree_view->priv->typeselect_flush_timeout);
11847       tree_view->priv->typeselect_flush_timeout =
11848         gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11849                        (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11850                        tree_view);
11851     }
11852
11853   return retval;
11854 }
11855
11856 /*  this function returns FALSE if there is a search string but
11857  *  nothing was found, and TRUE otherwise.
11858  */
11859 static gboolean
11860 pspp_sheet_view_search_move (GtkWidget   *window,
11861                            PsppSheetView *tree_view,
11862                            gboolean     up)
11863 {
11864   gboolean ret;
11865   gint len;
11866   gint count = 0;
11867   const gchar *text;
11868   GtkTreeIter iter;
11869   GtkTreeModel *model;
11870   PsppSheetSelection *selection;
11871
11872   text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
11873
11874   g_return_val_if_fail (text != NULL, FALSE);
11875
11876   len = strlen (text);
11877
11878   if (up && tree_view->priv->selected_iter == 1)
11879     return strlen (text) < 1;
11880
11881   len = strlen (text);
11882
11883   if (len < 1)
11884     return TRUE;
11885
11886   model = pspp_sheet_view_get_model (tree_view);
11887   selection = pspp_sheet_view_get_selection (tree_view);
11888
11889   /* search */
11890   pspp_sheet_selection_unselect_all (selection);
11891   if (!gtk_tree_model_get_iter_first (model, &iter))
11892     return TRUE;
11893
11894   ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
11895                                    &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
11896
11897   if (ret)
11898     {
11899       /* found */
11900       tree_view->priv->selected_iter += up?(-1):(1);
11901       return TRUE;
11902     }
11903   else
11904     {
11905       /* return to old iter */
11906       count = 0;
11907       gtk_tree_model_get_iter_first (model, &iter);
11908       pspp_sheet_view_search_iter (model, selection,
11909                                  &iter, text,
11910                                  &count, tree_view->priv->selected_iter);
11911       return FALSE;
11912     }
11913 }
11914
11915 static gboolean
11916 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
11917                                  gint          column,
11918                                  const gchar  *key,
11919                                  GtkTreeIter  *iter,
11920                                  gpointer      search_data)
11921 {
11922   gboolean retval = TRUE;
11923   const gchar *str;
11924   gchar *normalized_string;
11925   gchar *normalized_key;
11926   gchar *case_normalized_string = NULL;
11927   gchar *case_normalized_key = NULL;
11928   GValue value = {0,};
11929   GValue transformed = {0,};
11930
11931   gtk_tree_model_get_value (model, iter, column, &value);
11932
11933   g_value_init (&transformed, G_TYPE_STRING);
11934
11935   if (!g_value_transform (&value, &transformed))
11936     {
11937       g_value_unset (&value);
11938       return TRUE;
11939     }
11940
11941   g_value_unset (&value);
11942
11943   str = g_value_get_string (&transformed);
11944   if (!str)
11945     {
11946       g_value_unset (&transformed);
11947       return TRUE;
11948     }
11949
11950   normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
11951   normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
11952
11953   if (normalized_string && normalized_key)
11954     {
11955       case_normalized_string = g_utf8_casefold (normalized_string, -1);
11956       case_normalized_key = g_utf8_casefold (normalized_key, -1);
11957
11958       if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
11959         retval = FALSE;
11960     }
11961
11962   g_value_unset (&transformed);
11963   g_free (normalized_key);
11964   g_free (normalized_string);
11965   g_free (case_normalized_key);
11966   g_free (case_normalized_string);
11967
11968   return retval;
11969 }
11970
11971 static gboolean
11972 pspp_sheet_view_search_iter (GtkTreeModel     *model,
11973                              PsppSheetSelection *selection,
11974                              GtkTreeIter      *iter,
11975                              const gchar      *text,
11976                              gint             *count,
11977                              gint              n)
11978 {
11979   int node = -1;
11980   GtkTreePath *path;
11981
11982   PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
11983
11984   path = gtk_tree_model_get_path (model, iter);
11985   _pspp_sheet_view_find_node (tree_view, path, &node);
11986
11987   do
11988     {
11989       gboolean done = FALSE;
11990
11991       if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
11992         {
11993           (*count)++;
11994           if (*count == n)
11995             {
11996               pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
11997                                               TRUE, 0.5, 0.0);
11998               pspp_sheet_selection_select_iter (selection, iter);
11999               pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE);
12000
12001               if (path)
12002                 gtk_tree_path_free (path);
12003
12004               return TRUE;
12005             }
12006         }
12007
12008
12009       do
12010         {
12011           node = pspp_sheet_view_node_next (tree_view, node);
12012
12013           if (node >= 0)
12014             {
12015               gboolean has_next;
12016
12017               has_next = gtk_tree_model_iter_next (model, iter);
12018
12019               done = TRUE;
12020               gtk_tree_path_next (path);
12021
12022               /* sanity check */
12023               TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12024             }
12025           else
12026             {
12027               if (path)
12028                 gtk_tree_path_free (path);
12029
12030               /* we've run out of tree, done with this func */
12031               return FALSE;
12032             }
12033         }
12034       while (!done);
12035     }
12036   while (1);
12037
12038   return FALSE;
12039 }
12040
12041 static void
12042 pspp_sheet_view_search_init (GtkWidget   *entry,
12043                            PsppSheetView *tree_view)
12044 {
12045   gint ret;
12046   gint count = 0;
12047   const gchar *text;
12048   GtkTreeIter iter;
12049   GtkTreeModel *model;
12050   PsppSheetSelection *selection;
12051
12052   g_return_if_fail (GTK_IS_ENTRY (entry));
12053   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12054
12055   text = gtk_entry_get_text (GTK_ENTRY (entry));
12056
12057   model = pspp_sheet_view_get_model (tree_view);
12058   selection = pspp_sheet_view_get_selection (tree_view);
12059
12060   /* search */
12061   pspp_sheet_selection_unselect_all (selection);
12062   if (tree_view->priv->typeselect_flush_timeout
12063       && !tree_view->priv->search_custom_entry_set)
12064     {
12065       g_source_remove (tree_view->priv->typeselect_flush_timeout);
12066       tree_view->priv->typeselect_flush_timeout =
12067         gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12068                        (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12069                        tree_view);
12070     }
12071
12072   if (*text == '\0')
12073     return;
12074
12075   if (!gtk_tree_model_get_iter_first (model, &iter))
12076     return;
12077
12078   ret = pspp_sheet_view_search_iter (model, selection,
12079                                    &iter, text,
12080                                    &count, 1);
12081
12082   if (ret)
12083     tree_view->priv->selected_iter = 1;
12084 }
12085
12086 static void
12087 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12088                              PsppSheetView     *tree_view)
12089 {
12090   if (tree_view->priv->edited_column == NULL)
12091     return;
12092
12093   _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12094   tree_view->priv->edited_column = NULL;
12095
12096   if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12097     gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12098
12099   g_signal_handlers_disconnect_by_func (cell_editable,
12100                                         pspp_sheet_view_remove_widget,
12101                                         tree_view);
12102   g_signal_handlers_disconnect_by_func (cell_editable,
12103                                         pspp_sheet_view_editable_button_press_event,
12104                                         tree_view);
12105   g_signal_handlers_disconnect_by_func (cell_editable,
12106                                         pspp_sheet_view_editable_clicked,
12107                                         tree_view);
12108
12109   gtk_container_remove (GTK_CONTAINER (tree_view),
12110                         GTK_WIDGET (cell_editable));  
12111
12112   /* FIXME should only redraw a single node */
12113   gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12114 }
12115
12116 static gboolean
12117 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12118                              GtkTreePath *cursor_path)
12119 {
12120   GtkTreeIter iter;
12121   GdkRectangle background_area;
12122   GdkRectangle cell_area;
12123   GtkCellEditable *editable_widget = NULL;
12124   gchar *path_string;
12125   guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12126   gint retval = FALSE;
12127   int cursor_node;
12128
12129   g_assert (tree_view->priv->focus_column);
12130
12131   if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12132     return FALSE;
12133
12134   _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12135   if (cursor_node < 0)
12136     return FALSE;
12137
12138   path_string = gtk_tree_path_to_string (cursor_path);
12139   gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12140
12141   pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12142                                            tree_view->priv->model,
12143                                            &iter);
12144   pspp_sheet_view_get_background_area (tree_view,
12145                                      cursor_path,
12146                                      tree_view->priv->focus_column,
12147                                      &background_area);
12148   pspp_sheet_view_get_cell_area (tree_view,
12149                                cursor_path,
12150                                tree_view->priv->focus_column,
12151                                &cell_area);
12152
12153   if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12154                                         &editable_widget,
12155                                         NULL,
12156                                         path_string,
12157                                         &background_area,
12158                                         &cell_area,
12159                                         flags))
12160     {
12161       retval = TRUE;
12162       if (editable_widget != NULL)
12163         {
12164           gint left, right;
12165           GdkRectangle area;
12166           GtkCellRenderer *cell;
12167
12168           area = cell_area;
12169           cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12170
12171           _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12172
12173           area.x += left;
12174           area.width -= right + left;
12175
12176           pspp_sheet_view_real_start_editing (tree_view,
12177                                             tree_view->priv->focus_column,
12178                                             cursor_path,
12179                                             editable_widget,
12180                                             &area,
12181                                             NULL,
12182                                             flags);
12183         }
12184
12185     }
12186   g_free (path_string);
12187   return retval;
12188 }
12189
12190 static gboolean
12191 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12192                                              GdkEventButton *event,
12193                                              PsppSheetView *sheet_view)
12194 {
12195   gint node;
12196
12197   node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12198                                              "pspp-sheet-view-node"));
12199   return pspp_sheet_view_row_head_clicked (sheet_view,
12200                                            node,
12201                                            sheet_view->priv->edited_column,
12202                                            event);
12203 }
12204
12205 static void
12206 pspp_sheet_view_editable_clicked (GtkButton *button,
12207                                   PsppSheetView *sheet_view)
12208 {
12209   pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12210                                                sheet_view);
12211 }
12212
12213 static gboolean
12214 is_all_selected (GtkWidget *widget)
12215 {
12216   GtkEntryBuffer *buffer;
12217   gint start_pos, end_pos;
12218
12219   if (!GTK_IS_ENTRY (widget))
12220     return FALSE;
12221
12222   buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12223   return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12224                                              &start_pos, &end_pos)
12225           && start_pos == 0
12226           && end_pos == gtk_entry_buffer_get_length (buffer));
12227 }
12228
12229 static gboolean
12230 is_at_left (GtkWidget *widget)
12231 {
12232   return (GTK_IS_ENTRY (widget)
12233           && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12234 }
12235
12236 static gboolean
12237 is_at_right (GtkWidget *widget)
12238 {
12239   GtkEntryBuffer *buffer;
12240   gint length;
12241
12242   if (!GTK_IS_ENTRY (widget))
12243     return FALSE;
12244
12245   buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12246   length = gtk_entry_buffer_get_length (buffer);
12247   return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12248 }
12249
12250 static gboolean
12251 pspp_sheet_view_event (GtkWidget *widget,
12252                        GdkEventKey *event,
12253                        PsppSheetView *tree_view)
12254 {
12255   PsppSheetViewColumn *column;
12256   GtkTreePath *path;
12257   gboolean handled;
12258   gboolean cancel;
12259   guint keyval;
12260   guint state;
12261   gint row;
12262
12263   /* Intercept only key press events.
12264      It would make sense to use "key-press-event" instead of "event", but
12265      GtkEntry attaches its own signal handler to "key-press-event" that runs
12266      before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12267   */
12268   if (event->type != GDK_KEY_PRESS)
12269     return FALSE;
12270
12271   if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12272     {
12273       /* Pass through most keys that include modifiers. */
12274       if ((event->keyval == GDK_Tab || event->keyval == GDK_ISO_Left_Tab)
12275           && !(event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
12276         {
12277           /* Special case for Shift-Tab. */
12278         }
12279       else
12280         return FALSE;
12281     }
12282
12283   keyval = event->keyval;
12284   state = event->state & ~(GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK);
12285   cancel = FALSE;
12286   switch (event->keyval)
12287     {
12288     case GDK_Left:      case GDK_KP_Left:
12289       if (!is_all_selected (widget) && !is_at_left (widget))
12290         return FALSE;
12291       break;
12292
12293     case GDK_Right:     case GDK_KP_Right:
12294       if (!is_all_selected (widget) && !is_at_right (widget))
12295         return FALSE;
12296       break;
12297
12298     case GDK_Up:        case GDK_KP_Up:
12299     case GDK_Down:      case GDK_KP_Down:
12300       break;
12301
12302     case GDK_Page_Up:   case GDK_KP_Page_Up:
12303     case GDK_Page_Down: case GDK_KP_Page_Down:
12304       break;
12305
12306     case GDK_Escape:
12307       cancel = TRUE;
12308       break;
12309
12310     case GDK_Return:
12311       keyval = GDK_Down;
12312       break;
12313
12314     case GDK_Tab:       case GDK_KP_Tab:
12315     case GDK_ISO_Left_Tab:
12316       keyval = GDK_Tab;
12317       state |= event->state & GDK_SHIFT_MASK;
12318       break;
12319
12320     default:
12321       return FALSE;
12322     }
12323
12324   row = tree_view->priv->edited_row;
12325   column = tree_view->priv->edited_column;
12326   path = gtk_tree_path_new_from_indices (row, -1);
12327
12328   pspp_sheet_view_stop_editing (tree_view, cancel);
12329   gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12330
12331   pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12332   gtk_tree_path_free (path);
12333
12334   handled = gtk_binding_set_activate (edit_bindings, keyval, state,
12335                                       GTK_OBJECT (tree_view));
12336   if (handled)
12337     g_signal_stop_emission_by_name (widget, "event");
12338
12339   pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12340   pspp_sheet_view_start_editing (tree_view, path);
12341   gtk_tree_path_free (path);
12342
12343   return handled;
12344 }
12345
12346 static void
12347 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12348                                           gpointer data)
12349 {
12350   PsppSheetView *sheet_view = data;
12351
12352   g_signal_connect (widget, "event",
12353                     G_CALLBACK (pspp_sheet_view_event),
12354                     sheet_view);
12355
12356   if (GTK_IS_CONTAINER (widget))
12357     gtk_container_foreach (GTK_CONTAINER (widget),
12358                            pspp_sheet_view_override_cell_keypresses,
12359                            data);
12360 }
12361
12362 static void
12363 pspp_sheet_view_real_start_editing (PsppSheetView       *tree_view,
12364                                   PsppSheetViewColumn *column,
12365                                   GtkTreePath       *path,
12366                                   GtkCellEditable   *cell_editable,
12367                                   GdkRectangle      *cell_area,
12368                                   GdkEvent          *event,
12369                                   guint              flags)
12370 {
12371   PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12372   gint pre_val = tree_view->priv->vadjustment->value;
12373   GtkRequisition requisition;
12374   gint row;
12375
12376   g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12377
12378   tree_view->priv->edited_column = column;
12379   _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12380
12381   row = gtk_tree_path_get_indices (path)[0];
12382   tree_view->priv->edited_row = row;
12383   pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE);
12384   cell_area->y += pre_val - (int)tree_view->priv->vadjustment->value;
12385
12386   pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12387   pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12388   tree_view->priv->anchor_column = column;
12389
12390   gtk_widget_size_request (GTK_WIDGET (cell_editable), &requisition);
12391
12392   PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12393
12394   if (requisition.height < cell_area->height)
12395     {
12396       gint diff = cell_area->height - requisition.height;
12397       pspp_sheet_view_put (tree_view,
12398                          GTK_WIDGET (cell_editable),
12399                          cell_area->x, cell_area->y + diff/2,
12400                          cell_area->width, requisition.height);
12401     }
12402   else
12403     {
12404       pspp_sheet_view_put (tree_view,
12405                          GTK_WIDGET (cell_editable),
12406                          cell_area->x, cell_area->y,
12407                          cell_area->width, cell_area->height);
12408     }
12409
12410   gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12411                                    (GdkEvent *)event);
12412
12413   gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12414   g_signal_connect (cell_editable, "remove-widget",
12415                     G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12416   if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12417       GTK_IS_BUTTON (cell_editable))
12418     {
12419       g_signal_connect (cell_editable, "button-press-event",
12420                         G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12421                         tree_view);
12422       g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12423                          GINT_TO_POINTER (row));
12424       g_signal_connect (cell_editable, "clicked",
12425                         G_CALLBACK (pspp_sheet_view_editable_clicked),
12426                         tree_view);
12427     }
12428
12429   pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12430                                             tree_view);
12431 }
12432
12433 void
12434 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12435                               gboolean     cancel_editing)
12436 {
12437   PsppSheetViewColumn *column;
12438   GtkCellRenderer *cell;
12439
12440   if (tree_view->priv->edited_column == NULL)
12441     return;
12442
12443   /*
12444    * This is very evil. We need to do this, because
12445    * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12446    * later on. If pspp_sheet_view_row_changed notices
12447    * tree_view->priv->edited_column != NULL, it'll call
12448    * pspp_sheet_view_stop_editing again. Bad things will happen then.
12449    *
12450    * Please read that again if you intend to modify anything here.
12451    */
12452
12453   column = tree_view->priv->edited_column;
12454   tree_view->priv->edited_column = NULL;
12455
12456   cell = _pspp_sheet_view_column_get_edited_cell (column);
12457   gtk_cell_renderer_stop_editing (cell, cancel_editing);
12458
12459   if (!cancel_editing)
12460     gtk_cell_editable_editing_done (column->editable_widget);
12461
12462   tree_view->priv->edited_column = column;
12463
12464   gtk_cell_editable_remove_widget (column->editable_widget);
12465 }
12466
12467
12468 /**
12469  * pspp_sheet_view_set_hover_selection:
12470  * @tree_view: a #PsppSheetView
12471  * @hover: %TRUE to enable hover selection mode
12472  *
12473  * Enables of disables the hover selection mode of @tree_view.
12474  * Hover selection makes the selected row follow the pointer.
12475  * Currently, this works only for the selection modes 
12476  * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12477  * 
12478  * Since: 2.6
12479  **/
12480 void     
12481 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12482                                    gboolean     hover)
12483 {
12484   hover = hover != FALSE;
12485
12486   if (hover != tree_view->priv->hover_selection)
12487     {
12488       tree_view->priv->hover_selection = hover;
12489
12490       g_object_notify (G_OBJECT (tree_view), "hover-selection");
12491     }
12492 }
12493
12494 /**
12495  * pspp_sheet_view_get_hover_selection:
12496  * @tree_view: a #PsppSheetView
12497  * 
12498  * Returns whether hover selection mode is turned on for @tree_view.
12499  * 
12500  * Return value: %TRUE if @tree_view is in hover selection mode
12501  *
12502  * Since: 2.6 
12503  **/
12504 gboolean 
12505 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12506 {
12507   return tree_view->priv->hover_selection;
12508 }
12509
12510 /**
12511  * pspp_sheet_view_set_rubber_banding:
12512  * @tree_view: a #PsppSheetView
12513  * @enable: %TRUE to enable rubber banding
12514  *
12515  * Enables or disables rubber banding in @tree_view.  If the selection mode is
12516  * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12517  * banding will allow the user to select multiple rows by dragging the mouse.
12518  * 
12519  * Since: 2.10
12520  **/
12521 void
12522 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12523                                   gboolean     enable)
12524 {
12525   enable = enable != FALSE;
12526
12527   if (enable != tree_view->priv->rubber_banding_enable)
12528     {
12529       tree_view->priv->rubber_banding_enable = enable;
12530
12531       g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12532     }
12533 }
12534
12535 /**
12536  * pspp_sheet_view_get_rubber_banding:
12537  * @tree_view: a #PsppSheetView
12538  * 
12539  * Returns whether rubber banding is turned on for @tree_view.  If the
12540  * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12541  * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12542  * select multiple rows by dragging the mouse.
12543  * 
12544  * Return value: %TRUE if rubber banding in @tree_view is enabled.
12545  *
12546  * Since: 2.10
12547  **/
12548 gboolean
12549 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12550 {
12551   return tree_view->priv->rubber_banding_enable;
12552 }
12553
12554 /**
12555  * pspp_sheet_view_is_rubber_banding_active:
12556  * @tree_view: a #PsppSheetView
12557  * 
12558  * Returns whether a rubber banding operation is currently being done
12559  * in @tree_view.
12560  *
12561  * Return value: %TRUE if a rubber banding operation is currently being
12562  * done in @tree_view.
12563  *
12564  * Since: 2.12
12565  **/
12566 gboolean
12567 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12568 {
12569   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12570
12571   if (tree_view->priv->rubber_banding_enable
12572       && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12573     return TRUE;
12574
12575   return FALSE;
12576 }
12577
12578 static void
12579 pspp_sheet_view_grab_notify (GtkWidget *widget,
12580                            gboolean   was_grabbed)
12581 {
12582   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12583
12584   tree_view->priv->in_grab = !was_grabbed;
12585
12586   if (!was_grabbed)
12587     {
12588       tree_view->priv->pressed_button = -1;
12589
12590       if (tree_view->priv->rubber_band_status)
12591         pspp_sheet_view_stop_rubber_band (tree_view);
12592     }
12593 }
12594
12595 static void
12596 pspp_sheet_view_state_changed (GtkWidget      *widget,
12597                              GtkStateType    previous_state)
12598 {
12599   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12600
12601   if (gtk_widget_get_realized (widget))
12602     {
12603       gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
12604       gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
12605     }
12606
12607   gtk_widget_queue_draw (widget);
12608 }
12609
12610 /**
12611  * pspp_sheet_view_get_grid_lines:
12612  * @tree_view: a #PsppSheetView
12613  *
12614  * Returns which grid lines are enabled in @tree_view.
12615  *
12616  * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12617  * are enabled.
12618  *
12619  * Since: 2.10
12620  */
12621 PsppSheetViewGridLines
12622 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12623 {
12624   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12625
12626   return tree_view->priv->grid_lines;
12627 }
12628
12629 /**
12630  * pspp_sheet_view_set_grid_lines:
12631  * @tree_view: a #PsppSheetView
12632  * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12633  * enable.
12634  *
12635  * Sets which grid lines to draw in @tree_view.
12636  *
12637  * Since: 2.10
12638  */
12639 void
12640 pspp_sheet_view_set_grid_lines (PsppSheetView           *tree_view,
12641                               PsppSheetViewGridLines   grid_lines)
12642 {
12643   PsppSheetViewPrivate *priv;
12644   PsppSheetViewGridLines old_grid_lines;
12645
12646   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12647
12648   priv = tree_view->priv;
12649
12650   old_grid_lines = priv->grid_lines;
12651   priv->grid_lines = grid_lines;
12652   
12653   if (old_grid_lines != grid_lines)
12654     {
12655       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12656       
12657       g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12658     }
12659 }
12660
12661 /**
12662  * pspp_sheet_view_get_special_cells:
12663  * @tree_view: a #PsppSheetView
12664  *
12665  * Returns which grid lines are enabled in @tree_view.
12666  *
12667  * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12668  * the sheet view contain special cells.
12669  */
12670 PsppSheetViewSpecialCells
12671 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12672 {
12673   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12674
12675   return tree_view->priv->special_cells;
12676 }
12677
12678 /**
12679  * pspp_sheet_view_set_special_cells:
12680  * @tree_view: a #PsppSheetView
12681  * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12682  * the sheet view contain special cells.
12683  *
12684  * Sets whether rows in the sheet view contain special cells, controlling the
12685  * rendering of row selections.
12686  */
12687 void
12688 pspp_sheet_view_set_special_cells (PsppSheetView           *tree_view,
12689                               PsppSheetViewSpecialCells   special_cells)
12690 {
12691   PsppSheetViewPrivate *priv;
12692
12693   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12694
12695   priv = tree_view->priv;
12696
12697   if (priv->special_cells != special_cells)
12698     {
12699       priv->special_cells = special_cells;
12700       gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12701       g_object_notify (G_OBJECT (tree_view), "special-cells");
12702     }
12703 }
12704
12705 int
12706 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12707 {
12708   /* XXX (re)calculate fixed_height if necessary */
12709   return tree_view->priv->fixed_height;
12710 }
12711
12712 void
12713 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
12714                                   int fixed_height)
12715 {
12716   g_return_if_fail (fixed_height > 0);
12717
12718   if (tree_view->priv->fixed_height != fixed_height)
12719     {
12720       tree_view->priv->fixed_height = fixed_height;
12721       g_object_notify (G_OBJECT (tree_view), "fixed-height");
12722     }
12723   if (!tree_view->priv->fixed_height_set)
12724     {
12725       tree_view->priv->fixed_height_set = TRUE;
12726       g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
12727     }
12728 }
12729
12730 /**
12731  * pspp_sheet_view_set_tooltip_row:
12732  * @tree_view: a #PsppSheetView
12733  * @tooltip: a #GtkTooltip
12734  * @path: a #GtkTreePath
12735  *
12736  * Sets the tip area of @tooltip to be the area covered by the row at @path.
12737  * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12738  * See also gtk_tooltip_set_tip_area().
12739  *
12740  * Since: 2.12
12741  */
12742 void
12743 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
12744                                GtkTooltip  *tooltip,
12745                                GtkTreePath *path)
12746 {
12747   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12748   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12749
12750   pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
12751 }
12752
12753 /**
12754  * pspp_sheet_view_set_tooltip_cell:
12755  * @tree_view: a #PsppSheetView
12756  * @tooltip: a #GtkTooltip
12757  * @path: (allow-none): a #GtkTreePath or %NULL
12758  * @column: (allow-none): a #PsppSheetViewColumn or %NULL
12759  * @cell: (allow-none): a #GtkCellRenderer or %NULL
12760  *
12761  * Sets the tip area of @tooltip to the area @path, @column and @cell have
12762  * in common.  For example if @path is %NULL and @column is set, the tip
12763  * area will be set to the full area covered by @column.  See also
12764  * gtk_tooltip_set_tip_area().
12765  *
12766  * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12767  *
12768  * Since: 2.12
12769  */
12770 void
12771 pspp_sheet_view_set_tooltip_cell (PsppSheetView       *tree_view,
12772                                 GtkTooltip        *tooltip,
12773                                 GtkTreePath       *path,
12774                                 PsppSheetViewColumn *column,
12775                                 GtkCellRenderer   *cell)
12776 {
12777   GdkRectangle rect;
12778
12779   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12780   g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12781   g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
12782   g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
12783
12784   /* Determine x values. */
12785   if (column && cell)
12786     {
12787       GdkRectangle tmp;
12788       gint start, width;
12789
12790       pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
12791       pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
12792
12793       pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12794                                                          tmp.x + start, 0,
12795                                                          &rect.x, NULL);
12796       rect.width = width;
12797     }
12798   else if (column)
12799     {
12800       GdkRectangle tmp;
12801
12802       pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
12803       pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12804                                                          tmp.x, 0,
12805                                                          &rect.x, NULL);
12806       rect.width = tmp.width;
12807     }
12808   else
12809     {
12810       rect.x = 0;
12811       rect.width = GTK_WIDGET (tree_view)->allocation.width;
12812     }
12813
12814   /* Determine y values. */
12815   if (path)
12816     {
12817       GdkRectangle tmp;
12818
12819       pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
12820       pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12821                                                          0, tmp.y,
12822                                                          NULL, &rect.y);
12823       rect.height = tmp.height;
12824     }
12825   else
12826     {
12827       rect.y = 0;
12828       rect.height = tree_view->priv->vadjustment->page_size;
12829     }
12830
12831   gtk_tooltip_set_tip_area (tooltip, &rect);
12832 }
12833
12834 /**
12835  * pspp_sheet_view_get_tooltip_context:
12836  * @tree_view: a #PsppSheetView
12837  * @x: the x coordinate (relative to widget coordinates)
12838  * @y: the y coordinate (relative to widget coordinates)
12839  * @keyboard_tip: whether this is a keyboard tooltip or not
12840  * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
12841  * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
12842  * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
12843  *
12844  * This function is supposed to be used in a #GtkWidget::query-tooltip
12845  * signal handler for #PsppSheetView.  The @x, @y and @keyboard_tip values
12846  * which are received in the signal handler, should be passed to this
12847  * function without modification.
12848  *
12849  * The return value indicates whether there is a tree view row at the given
12850  * coordinates (%TRUE) or not (%FALSE) for mouse tooltips.  For keyboard
12851  * tooltips the row returned will be the cursor row.  When %TRUE, then any of
12852  * @model, @path and @iter which have been provided will be set to point to
12853  * that row and the corresponding model.  @x and @y will always be converted
12854  * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
12855  *
12856  * Return value: whether or not the given tooltip context points to a row.
12857  *
12858  * Since: 2.12
12859  */
12860 gboolean
12861 pspp_sheet_view_get_tooltip_context (PsppSheetView   *tree_view,
12862                                    gint          *x,
12863                                    gint          *y,
12864                                    gboolean       keyboard_tip,
12865                                    GtkTreeModel **model,
12866                                    GtkTreePath  **path,
12867                                    GtkTreeIter   *iter)
12868 {
12869   GtkTreePath *tmppath = NULL;
12870
12871   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12872   g_return_val_if_fail (x != NULL, FALSE);
12873   g_return_val_if_fail (y != NULL, FALSE);
12874
12875   if (keyboard_tip)
12876     {
12877       pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
12878
12879       if (!tmppath)
12880         return FALSE;
12881     }
12882   else
12883     {
12884       pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
12885                                                          x, y);
12886
12887       if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
12888                                           &tmppath, NULL, NULL, NULL))
12889         return FALSE;
12890     }
12891
12892   if (model)
12893     *model = pspp_sheet_view_get_model (tree_view);
12894
12895   if (iter)
12896     gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
12897                              iter, tmppath);
12898
12899   if (path)
12900     *path = tmppath;
12901   else
12902     gtk_tree_path_free (tmppath);
12903
12904   return TRUE;
12905 }
12906
12907 static gboolean
12908 pspp_sheet_view_set_tooltip_query_cb (GtkWidget  *widget,
12909                                     gint        x,
12910                                     gint        y,
12911                                     gboolean    keyboard_tip,
12912                                     GtkTooltip *tooltip,
12913                                     gpointer    data)
12914 {
12915   GValue value = { 0, };
12916   GValue transformed = { 0, };
12917   GtkTreeIter iter;
12918   GtkTreePath *path;
12919   GtkTreeModel *model;
12920   PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12921
12922   if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
12923                                           &x, &y,
12924                                           keyboard_tip,
12925                                           &model, &path, &iter))
12926     return FALSE;
12927
12928   gtk_tree_model_get_value (model, &iter,
12929                             tree_view->priv->tooltip_column, &value);
12930
12931   g_value_init (&transformed, G_TYPE_STRING);
12932
12933   if (!g_value_transform (&value, &transformed))
12934     {
12935       g_value_unset (&value);
12936       gtk_tree_path_free (path);
12937
12938       return FALSE;
12939     }
12940
12941   g_value_unset (&value);
12942
12943   if (!g_value_get_string (&transformed))
12944     {
12945       g_value_unset (&transformed);
12946       gtk_tree_path_free (path);
12947
12948       return FALSE;
12949     }
12950
12951   gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
12952   pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
12953
12954   gtk_tree_path_free (path);
12955   g_value_unset (&transformed);
12956
12957   return TRUE;
12958 }
12959
12960 /**
12961  * pspp_sheet_view_set_tooltip_column:
12962  * @tree_view: a #PsppSheetView
12963  * @column: an integer, which is a valid column number for @tree_view's model
12964  *
12965  * If you only plan to have simple (text-only) tooltips on full rows, you
12966  * can use this function to have #PsppSheetView handle these automatically
12967  * for you. @column should be set to the column in @tree_view's model
12968  * containing the tooltip texts, or -1 to disable this feature.
12969  *
12970  * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
12971  * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
12972  *
12973  * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
12974  * so &amp;, &lt;, etc have to be escaped in the text.
12975  *
12976  * Since: 2.12
12977  */
12978 void
12979 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
12980                                   gint         column)
12981 {
12982   g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12983
12984   if (column == tree_view->priv->tooltip_column)
12985     return;
12986
12987   if (column == -1)
12988     {
12989       g_signal_handlers_disconnect_by_func (tree_view,
12990                                             pspp_sheet_view_set_tooltip_query_cb,
12991                                             NULL);
12992       gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
12993     }
12994   else
12995     {
12996       if (tree_view->priv->tooltip_column == -1)
12997         {
12998           g_signal_connect (tree_view, "query-tooltip",
12999                             G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13000           gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13001         }
13002     }
13003
13004   tree_view->priv->tooltip_column = column;
13005   g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13006 }
13007
13008 /**
13009  * pspp_sheet_view_get_tooltip_column:
13010  * @tree_view: a #PsppSheetView
13011  *
13012  * Returns the column of @tree_view's model which is being used for
13013  * displaying tooltips on @tree_view's rows.
13014  *
13015  * Return value: the index of the tooltip column that is currently being
13016  * used, or -1 if this is disabled.
13017  *
13018  * Since: 2.12
13019  */
13020 gint
13021 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13022 {
13023   g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13024
13025   return tree_view->priv->tooltip_column;
13026 }
13027
13028 gboolean
13029 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13030                                   GValue                *return_accu,
13031                                   const GValue          *handler_return,
13032                                   gpointer               dummy)
13033 {
13034   gboolean continue_emission;
13035   gboolean signal_handled;
13036   
13037   signal_handled = g_value_get_boolean (handler_return);
13038   g_value_set_boolean (return_accu, signal_handled);
13039   continue_emission = !signal_handled;
13040   
13041   return continue_emission;
13042 }
13043
13044 GType
13045 pspp_sheet_view_grid_lines_get_type (void)
13046 {
13047     static GType etype = 0;
13048     if (G_UNLIKELY(etype == 0)) {
13049         static const GEnumValue values[] = {
13050             { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13051             { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13052             { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13053             { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13054             { 0, NULL, NULL }
13055         };
13056         etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13057     }
13058     return etype;
13059 }
13060
13061 GType
13062 pspp_sheet_view_special_cells_get_type (void)
13063 {
13064     static GType etype = 0;
13065     if (G_UNLIKELY(etype == 0)) {
13066         static const GEnumValue values[] = {
13067             { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13068             { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13069             { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13070             { 0, NULL, NULL }
13071         };
13072         etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);
13073     }
13074     return etype;
13075 }