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