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