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