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