1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012 Free Software Foundation, Inc.
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.
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.
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/>. */
18 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
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.
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.
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.
38 #include "ui/gui/pspp-sheet-private.h"
42 #include <gdk/gdkkeysyms.h>
45 #include "ui/gui/psppire-marshal.h"
46 #include "ui/gui/pspp-sheet-selection.h"
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
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
58 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_META_MASK
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
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.
74 #define BACKGROUND_HEIGHT(tree_view) (tree_view->priv->fixed_height)
75 #define CELL_HEIGHT(tree_view, separator) ((BACKGROUND_HEIGHT (tree_view)) - (separator))
77 /* Translate from bin_window coordinates to rbtree (tree coordinates) and
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)
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)
87 #define ROW_HEIGHT(tree_view) \
88 ((tree_view->priv->fixed_height > 0) ? (tree_view->priv->fixed_height) : (tree_view)->priv->expander_size)
91 typedef struct _PsppSheetViewChild PsppSheetViewChild;
92 struct _PsppSheetViewChild
102 typedef struct _TreeViewDragInfo TreeViewDragInfo;
103 struct _TreeViewDragInfo
105 GdkModifierType start_button_mask;
106 GtkTargetList *_unused_source_target_list;
107 GdkDragAction source_actions;
109 GtkTargetList *_unused_dest_target_list;
111 guint source_set : 1;
127 START_INTERACTIVE_SEARCH,
137 PROP_HEADERS_VISIBLE,
138 PROP_HEADERS_CLICKABLE,
143 PROP_HOVER_SELECTION,
145 PROP_ENABLE_GRID_LINES,
149 PROP_FIXED_HEIGHT_SET
153 static void pspp_sheet_view_finalize (GObject *object);
154 static void pspp_sheet_view_set_property (GObject *object,
158 static void pspp_sheet_view_get_property (GObject *object,
163 static void pspp_sheet_view_dispose (GObject *object);
165 /* gtkwidget signals */
166 static void pspp_sheet_view_realize (GtkWidget *widget);
167 static void pspp_sheet_view_unrealize (GtkWidget *widget);
168 static void pspp_sheet_view_map (GtkWidget *widget);
169 static void pspp_sheet_view_size_request (GtkWidget *widget,
170 GtkRequisition *requisition);
171 static void pspp_sheet_view_size_allocate (GtkWidget *widget,
172 GtkAllocation *allocation);
173 static gboolean pspp_sheet_view_expose (GtkWidget *widget,
174 GdkEventExpose *event);
175 static gboolean pspp_sheet_view_key_press (GtkWidget *widget,
177 static gboolean pspp_sheet_view_key_release (GtkWidget *widget,
179 static gboolean pspp_sheet_view_motion (GtkWidget *widget,
180 GdkEventMotion *event);
181 static gboolean pspp_sheet_view_enter_notify (GtkWidget *widget,
182 GdkEventCrossing *event);
183 static gboolean pspp_sheet_view_leave_notify (GtkWidget *widget,
184 GdkEventCrossing *event);
185 static gboolean pspp_sheet_view_button_press (GtkWidget *widget,
186 GdkEventButton *event);
187 static gboolean pspp_sheet_view_button_release (GtkWidget *widget,
188 GdkEventButton *event);
189 static gboolean pspp_sheet_view_grab_broken (GtkWidget *widget,
190 GdkEventGrabBroken *event);
192 static void pspp_sheet_view_set_focus_child (GtkContainer *container,
194 static gint pspp_sheet_view_focus_out (GtkWidget *widget,
195 GdkEventFocus *event);
196 static gint pspp_sheet_view_focus (GtkWidget *widget,
197 GtkDirectionType direction);
198 static void pspp_sheet_view_grab_focus (GtkWidget *widget);
199 static void pspp_sheet_view_style_set (GtkWidget *widget,
200 GtkStyle *previous_style);
201 static void pspp_sheet_view_grab_notify (GtkWidget *widget,
202 gboolean was_grabbed);
203 static void pspp_sheet_view_state_changed (GtkWidget *widget,
204 GtkStateType previous_state);
206 /* container signals */
207 static void pspp_sheet_view_remove (GtkContainer *container,
209 static void pspp_sheet_view_forall (GtkContainer *container,
210 gboolean include_internals,
211 GtkCallback callback,
212 gpointer callback_data);
214 /* Source side drag signals */
215 static void pspp_sheet_view_drag_begin (GtkWidget *widget,
216 GdkDragContext *context);
217 static void pspp_sheet_view_drag_end (GtkWidget *widget,
218 GdkDragContext *context);
219 static void pspp_sheet_view_drag_data_get (GtkWidget *widget,
220 GdkDragContext *context,
221 GtkSelectionData *selection_data,
224 static void pspp_sheet_view_drag_data_delete (GtkWidget *widget,
225 GdkDragContext *context);
227 /* Target side drag signals */
228 static void pspp_sheet_view_drag_leave (GtkWidget *widget,
229 GdkDragContext *context,
231 static gboolean pspp_sheet_view_drag_motion (GtkWidget *widget,
232 GdkDragContext *context,
236 static gboolean pspp_sheet_view_drag_drop (GtkWidget *widget,
237 GdkDragContext *context,
241 static void pspp_sheet_view_drag_data_received (GtkWidget *widget,
242 GdkDragContext *context,
245 GtkSelectionData *selection_data,
249 /* tree_model signals */
250 static void pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
252 GtkAdjustment *vadj);
253 static gboolean pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
254 GtkMovementStep step,
256 static gboolean pspp_sheet_view_real_select_all (PsppSheetView *tree_view);
257 static gboolean pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view);
258 static gboolean pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
259 gboolean start_editing);
260 static gboolean pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view);
261 static void pspp_sheet_view_row_changed (GtkTreeModel *model,
265 static void pspp_sheet_view_row_inserted (GtkTreeModel *model,
269 static void pspp_sheet_view_row_deleted (GtkTreeModel *model,
272 static void pspp_sheet_view_rows_reordered (GtkTreeModel *model,
278 /* Incremental reflow */
279 static gint validate_row (PsppSheetView *tree_view,
283 static void validate_visible_area (PsppSheetView *tree_view);
284 static gboolean validate_rows_handler (PsppSheetView *tree_view);
285 static gboolean presize_handler_callback (gpointer data);
286 static void install_presize_handler (PsppSheetView *tree_view);
287 static void install_scroll_sync_handler (PsppSheetView *tree_view);
288 static void pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
291 static void pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view);
292 static void pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view);
293 static void invalidate_empty_focus (PsppSheetView *tree_view);
295 /* Internal functions */
296 static void pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
299 gboolean add_shifted_binding,
300 GtkMovementStep step,
302 static void pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
304 const GdkRectangle *clip_rect);
305 static gint pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
308 static void pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
309 PsppSheetView *tree_view);
310 static void pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
312 static void pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
313 PsppSheetViewColumn *column,
314 gboolean focus_to_cell);
315 static gboolean pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
316 GdkEventMotion *event);
317 static void pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view);
318 static gboolean pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
320 static void pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
322 static void pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
324 static void pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
326 static void pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
328 static void pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
330 gboolean clear_and_select,
331 gboolean clamp_node);
332 static gboolean pspp_sheet_view_has_special_cell (PsppSheetView *tree_view);
333 static void pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view);
334 static void update_prelight (PsppSheetView *tree_view,
337 static void initialize_fixed_height_mode (PsppSheetView *tree_view);
339 /* interactive search */
340 static void pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view);
341 static void pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
342 PsppSheetView *tree_view);
343 static void pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
344 GtkWidget *search_dialog,
346 static void pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
349 static void pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
350 PsppSheetView *tree_view);
351 static void pspp_sheet_view_search_activate (GtkEntry *entry,
352 PsppSheetView *tree_view);
353 static gboolean pspp_sheet_view_real_search_enable_popdown(gpointer data);
354 static void pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
356 static gboolean pspp_sheet_view_search_delete_event (GtkWidget *widget,
358 PsppSheetView *tree_view);
359 static gboolean pspp_sheet_view_search_button_press_event (GtkWidget *widget,
360 GdkEventButton *event,
361 PsppSheetView *tree_view);
362 static gboolean pspp_sheet_view_search_scroll_event (GtkWidget *entry,
363 GdkEventScroll *event,
364 PsppSheetView *tree_view);
365 static gboolean pspp_sheet_view_search_key_press_event (GtkWidget *entry,
367 PsppSheetView *tree_view);
368 static gboolean pspp_sheet_view_search_move (GtkWidget *window,
369 PsppSheetView *tree_view,
371 static gboolean pspp_sheet_view_search_equal_func (GtkTreeModel *model,
375 gpointer search_data);
376 static gboolean pspp_sheet_view_search_iter (GtkTreeModel *model,
377 PsppSheetSelection *selection,
382 static void pspp_sheet_view_search_init (GtkWidget *entry,
383 PsppSheetView *tree_view);
384 static void pspp_sheet_view_put (PsppSheetView *tree_view,
385 GtkWidget *child_widget,
390 static gboolean pspp_sheet_view_start_editing (PsppSheetView *tree_view,
391 GtkTreePath *cursor_path);
392 static gboolean pspp_sheet_view_editable_button_press_event (GtkWidget *,
395 static void pspp_sheet_view_editable_clicked (GtkButton *, PsppSheetView *);
396 static void pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
397 PsppSheetViewColumn *column,
399 GtkCellEditable *cell_editable,
400 GdkRectangle *cell_area,
403 static gboolean pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
404 gboolean keybinding);
405 static gboolean pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view);
406 static PsppSheetViewColumn *pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
407 PsppSheetViewColumn *column,
410 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
411 PsppSheetViewColumn *column,
412 const GdkRectangle *background_area,
413 gboolean subtract_focus_rect,
414 GdkRectangle *cell_area);
415 static gint pspp_sheet_view_find_offset (PsppSheetView *tree_view,
420 static void pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
424 static void pspp_sheet_view_buildable_init (GtkBuildableIface *iface);
427 static gboolean scroll_row_timeout (gpointer data);
428 static void add_scroll_timeout (PsppSheetView *tree_view);
429 static void remove_scroll_timeout (PsppSheetView *tree_view);
431 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
433 static GtkBindingSet *edit_bindings;
440 G_DEFINE_TYPE_WITH_CODE (PsppSheetView, pspp_sheet_view, GTK_TYPE_CONTAINER,
441 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
442 pspp_sheet_view_buildable_init))
445 pspp_sheet_view_class_init (PsppSheetViewClass *class)
447 GObjectClass *o_class;
448 GtkWidgetClass *widget_class;
449 GtkContainerClass *container_class;
450 GtkBindingSet *binding_set[2];
453 binding_set[0] = gtk_binding_set_by_class (class);
455 binding_set[1] = gtk_binding_set_new ("PsppSheetViewEditing");
456 edit_bindings = binding_set[1];
458 o_class = (GObjectClass *) class;
459 widget_class = (GtkWidgetClass *) class;
460 container_class = (GtkContainerClass *) class;
462 /* GObject signals */
463 o_class->set_property = pspp_sheet_view_set_property;
464 o_class->get_property = pspp_sheet_view_get_property;
465 o_class->finalize = pspp_sheet_view_finalize;
466 o_class->dispose = pspp_sheet_view_dispose;
468 /* GtkWidget signals */
469 widget_class->map = pspp_sheet_view_map;
470 widget_class->realize = pspp_sheet_view_realize;
471 widget_class->unrealize = pspp_sheet_view_unrealize;
472 widget_class->size_request = pspp_sheet_view_size_request;
473 widget_class->size_allocate = pspp_sheet_view_size_allocate;
474 widget_class->button_press_event = pspp_sheet_view_button_press;
475 widget_class->button_release_event = pspp_sheet_view_button_release;
476 widget_class->grab_broken_event = pspp_sheet_view_grab_broken;
477 /*widget_class->configure_event = pspp_sheet_view_configure;*/
478 widget_class->motion_notify_event = pspp_sheet_view_motion;
479 widget_class->expose_event = pspp_sheet_view_expose;
480 widget_class->key_press_event = pspp_sheet_view_key_press;
481 widget_class->key_release_event = pspp_sheet_view_key_release;
482 widget_class->enter_notify_event = pspp_sheet_view_enter_notify;
483 widget_class->leave_notify_event = pspp_sheet_view_leave_notify;
484 widget_class->focus_out_event = pspp_sheet_view_focus_out;
485 widget_class->drag_begin = pspp_sheet_view_drag_begin;
486 widget_class->drag_end = pspp_sheet_view_drag_end;
487 widget_class->drag_data_get = pspp_sheet_view_drag_data_get;
488 widget_class->drag_data_delete = pspp_sheet_view_drag_data_delete;
489 widget_class->drag_leave = pspp_sheet_view_drag_leave;
490 widget_class->drag_motion = pspp_sheet_view_drag_motion;
491 widget_class->drag_drop = pspp_sheet_view_drag_drop;
492 widget_class->drag_data_received = pspp_sheet_view_drag_data_received;
493 widget_class->focus = pspp_sheet_view_focus;
494 widget_class->grab_focus = pspp_sheet_view_grab_focus;
495 widget_class->style_set = pspp_sheet_view_style_set;
496 widget_class->grab_notify = pspp_sheet_view_grab_notify;
497 widget_class->state_changed = pspp_sheet_view_state_changed;
499 /* GtkContainer signals */
500 container_class->remove = pspp_sheet_view_remove;
501 container_class->forall = pspp_sheet_view_forall;
502 container_class->set_focus_child = pspp_sheet_view_set_focus_child;
504 class->set_scroll_adjustments = pspp_sheet_view_set_adjustments;
505 class->move_cursor = pspp_sheet_view_real_move_cursor;
506 class->select_all = pspp_sheet_view_real_select_all;
507 class->unselect_all = pspp_sheet_view_real_unselect_all;
508 class->select_cursor_row = pspp_sheet_view_real_select_cursor_row;
509 class->toggle_cursor_row = pspp_sheet_view_real_toggle_cursor_row;
510 class->start_interactive_search = pspp_sheet_view_start_interactive_search;
514 g_object_class_install_property (o_class,
516 g_param_spec_object ("model",
517 P_("TreeView Model"),
518 P_("The model for the tree view"),
520 GTK_PARAM_READWRITE));
522 g_object_class_install_property (o_class,
524 g_param_spec_object ("hadjustment",
525 P_("Horizontal Adjustment"),
526 P_("Horizontal Adjustment for the widget"),
528 GTK_PARAM_READWRITE));
530 g_object_class_install_property (o_class,
532 g_param_spec_object ("vadjustment",
533 P_("Vertical Adjustment"),
534 P_("Vertical Adjustment for the widget"),
536 GTK_PARAM_READWRITE));
538 g_object_class_install_property (o_class,
539 PROP_HEADERS_VISIBLE,
540 g_param_spec_boolean ("headers-visible",
541 P_("Headers Visible"),
542 P_("Show the column header buttons"),
544 GTK_PARAM_READWRITE));
546 g_object_class_install_property (o_class,
547 PROP_HEADERS_CLICKABLE,
548 g_param_spec_boolean ("headers-clickable",
549 P_("Headers Clickable"),
550 P_("Column headers respond to click events"),
552 GTK_PARAM_READWRITE));
554 g_object_class_install_property (o_class,
556 g_param_spec_boolean ("reorderable",
558 P_("View is reorderable"),
560 GTK_PARAM_READWRITE));
562 g_object_class_install_property (o_class,
564 g_param_spec_boolean ("rules-hint",
566 P_("Set a hint to the theme engine to draw rows in alternating colors"),
568 GTK_PARAM_READWRITE));
570 g_object_class_install_property (o_class,
572 g_param_spec_boolean ("enable-search",
574 P_("View allows user to search through columns interactively"),
576 GTK_PARAM_READWRITE));
578 g_object_class_install_property (o_class,
580 g_param_spec_int ("search-column",
582 P_("Model column to search through during interactive search"),
586 GTK_PARAM_READWRITE));
589 * PsppSheetView:hover-selection:
591 * Enables of disables the hover selection mode of @tree_view.
592 * Hover selection makes the selected row follow the pointer.
593 * Currently, this works only for the selection modes
594 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
596 * This mode is primarily intended for treeviews in popups, e.g.
597 * in #GtkComboBox or #GtkEntryCompletion.
601 g_object_class_install_property (o_class,
602 PROP_HOVER_SELECTION,
603 g_param_spec_boolean ("hover-selection",
604 P_("Hover Selection"),
605 P_("Whether the selection should follow the pointer"),
607 GTK_PARAM_READWRITE));
609 g_object_class_install_property (o_class,
611 g_param_spec_boolean ("rubber-banding",
612 P_("Rubber Banding"),
613 P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
615 GTK_PARAM_READWRITE));
617 g_object_class_install_property (o_class,
618 PROP_ENABLE_GRID_LINES,
619 g_param_spec_enum ("enable-grid-lines",
620 P_("Enable Grid Lines"),
621 P_("Whether grid lines should be drawn in the tree view"),
622 PSPP_TYPE_SHEET_VIEW_GRID_LINES,
623 PSPP_SHEET_VIEW_GRID_LINES_NONE,
624 GTK_PARAM_READWRITE));
626 g_object_class_install_property (o_class,
628 g_param_spec_int ("tooltip-column",
629 P_("Tooltip Column"),
630 P_("The column in the model containing the tooltip texts for the rows"),
634 GTK_PARAM_READWRITE));
636 g_object_class_install_property (o_class,
638 g_param_spec_enum ("special-cells",
640 P_("Whether rows have special cells."),
641 PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS,
642 PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT,
643 GTK_PARAM_READWRITE));
645 g_object_class_install_property (o_class,
647 g_param_spec_int ("fixed-height",
649 P_("Height of a single row. Normally the height of a row is determined automatically. Writing this property sets fixed-height-set to true, preventing this property's value from changing."),
653 GTK_PARAM_READWRITE));
655 g_object_class_install_property (o_class,
656 PROP_FIXED_HEIGHT_SET,
657 g_param_spec_boolean ("fixed-height-set",
658 P_("Fixed Height Set"),
659 P_("Whether fixed-height was set externally."),
661 GTK_PARAM_READWRITE));
663 /* Style properties */
664 #define _TREE_VIEW_EXPANDER_SIZE 12
665 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
666 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
668 gtk_widget_class_install_style_property (widget_class,
669 g_param_spec_int ("expander-size",
671 P_("Size of the expander arrow"),
674 _TREE_VIEW_EXPANDER_SIZE,
675 GTK_PARAM_READABLE));
677 gtk_widget_class_install_style_property (widget_class,
678 g_param_spec_int ("vertical-separator",
679 P_("Vertical Separator Width"),
680 P_("Vertical space between cells. Must be an even number"),
683 _TREE_VIEW_VERTICAL_SEPARATOR,
684 GTK_PARAM_READABLE));
686 gtk_widget_class_install_style_property (widget_class,
687 g_param_spec_int ("horizontal-separator",
688 P_("Horizontal Separator Width"),
689 P_("Horizontal space between cells. Must be an even number"),
692 _TREE_VIEW_HORIZONTAL_SEPARATOR,
693 GTK_PARAM_READABLE));
695 gtk_widget_class_install_style_property (widget_class,
696 g_param_spec_boolean ("allow-rules",
698 P_("Allow drawing of alternating color rows"),
700 GTK_PARAM_READABLE));
702 gtk_widget_class_install_style_property (widget_class,
703 g_param_spec_boxed ("even-row-color",
704 P_("Even Row Color"),
705 P_("Color to use for even rows"),
707 GTK_PARAM_READABLE));
709 gtk_widget_class_install_style_property (widget_class,
710 g_param_spec_boxed ("odd-row-color",
712 P_("Color to use for odd rows"),
714 GTK_PARAM_READABLE));
716 gtk_widget_class_install_style_property (widget_class,
717 g_param_spec_boolean ("row-ending-details",
718 P_("Row Ending details"),
719 P_("Enable extended row background theming"),
721 GTK_PARAM_READABLE));
723 gtk_widget_class_install_style_property (widget_class,
724 g_param_spec_int ("grid-line-width",
725 P_("Grid line width"),
726 P_("Width, in pixels, of the tree view grid lines"),
728 GTK_PARAM_READABLE));
730 gtk_widget_class_install_style_property (widget_class,
731 g_param_spec_int ("tree-line-width",
732 P_("Tree line width"),
733 P_("Width, in pixels, of the tree view lines"),
735 GTK_PARAM_READABLE));
737 gtk_widget_class_install_style_property (widget_class,
738 g_param_spec_string ("tree-line-pattern",
739 P_("Tree line pattern"),
740 P_("Dash pattern used to draw the tree view lines"),
742 GTK_PARAM_READABLE));
746 * PsppSheetView::set-scroll-adjustments
747 * @horizontal: the horizontal #GtkAdjustment
748 * @vertical: the vertical #GtkAdjustment
750 * Set the scroll adjustments for the tree view. Usually scrolled containers
751 * like #GtkScrolledWindow will emit this signal to connect two instances
752 * of #GtkScrollbar to the scroll directions of the #PsppSheetView.
754 widget_class->set_scroll_adjustments_signal =
755 g_signal_new ("set-scroll-adjustments",
756 G_TYPE_FROM_CLASS (o_class),
757 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
758 G_STRUCT_OFFSET (PsppSheetViewClass, set_scroll_adjustments),
760 psppire_marshal_VOID__OBJECT_OBJECT,
763 GTK_TYPE_ADJUSTMENT);
766 * PsppSheetView::row-activated:
767 * @tree_view: the object on which the signal is emitted
768 * @path: the #GtkTreePath for the activated row
769 * @column: the #PsppSheetViewColumn in which the activation occurred
771 * The "row-activated" signal is emitted when the method
772 * pspp_sheet_view_row_activated() is called or the user double clicks
773 * a treeview row. It is also emitted when a non-editable row is
774 * selected and one of the keys: Space, Shift+Space, Return or
777 * For selection handling refer to the <link linkend="TreeWidget">tree
778 * widget conceptual overview</link> as well as #PsppSheetSelection.
780 tree_view_signals[ROW_ACTIVATED] =
781 g_signal_new ("row-activated",
782 G_TYPE_FROM_CLASS (o_class),
783 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
784 G_STRUCT_OFFSET (PsppSheetViewClass, row_activated),
786 psppire_marshal_VOID__BOXED_OBJECT,
789 PSPP_TYPE_SHEET_VIEW_COLUMN);
792 * PsppSheetView::columns-changed:
793 * @tree_view: the object on which the signal is emitted
795 * The number of columns of the treeview has changed.
797 tree_view_signals[COLUMNS_CHANGED] =
798 g_signal_new ("columns-changed",
799 G_TYPE_FROM_CLASS (o_class),
801 G_STRUCT_OFFSET (PsppSheetViewClass, columns_changed),
803 g_cclosure_marshal_VOID__VOID,
807 * PsppSheetView::cursor-changed:
808 * @tree_view: the object on which the signal is emitted
810 * The position of the cursor (focused cell) has changed.
812 tree_view_signals[CURSOR_CHANGED] =
813 g_signal_new ("cursor-changed",
814 G_TYPE_FROM_CLASS (o_class),
816 G_STRUCT_OFFSET (PsppSheetViewClass, cursor_changed),
818 g_cclosure_marshal_VOID__VOID,
821 tree_view_signals[MOVE_CURSOR] =
822 g_signal_new ("move-cursor",
823 G_TYPE_FROM_CLASS (o_class),
824 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
825 G_STRUCT_OFFSET (PsppSheetViewClass, move_cursor),
827 psppire_marshal_BOOLEAN__ENUM_INT,
829 GTK_TYPE_MOVEMENT_STEP,
832 tree_view_signals[SELECT_ALL] =
833 g_signal_new ("select-all",
834 G_TYPE_FROM_CLASS (o_class),
835 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
836 G_STRUCT_OFFSET (PsppSheetViewClass, select_all),
838 psppire_marshal_BOOLEAN__VOID,
841 tree_view_signals[UNSELECT_ALL] =
842 g_signal_new ("unselect-all",
843 G_TYPE_FROM_CLASS (o_class),
844 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
845 G_STRUCT_OFFSET (PsppSheetViewClass, unselect_all),
847 psppire_marshal_BOOLEAN__VOID,
850 tree_view_signals[SELECT_CURSOR_ROW] =
851 g_signal_new ("select-cursor-row",
852 G_TYPE_FROM_CLASS (o_class),
853 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
854 G_STRUCT_OFFSET (PsppSheetViewClass, select_cursor_row),
856 psppire_marshal_BOOLEAN__BOOLEAN,
860 tree_view_signals[TOGGLE_CURSOR_ROW] =
861 g_signal_new ("toggle-cursor-row",
862 G_TYPE_FROM_CLASS (o_class),
863 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
864 G_STRUCT_OFFSET (PsppSheetViewClass, toggle_cursor_row),
866 psppire_marshal_BOOLEAN__VOID,
869 tree_view_signals[START_INTERACTIVE_SEARCH] =
870 g_signal_new ("start-interactive-search",
871 G_TYPE_FROM_CLASS (o_class),
872 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
873 G_STRUCT_OFFSET (PsppSheetViewClass, start_interactive_search),
875 psppire_marshal_BOOLEAN__VOID,
879 for (i = 0; i < 2; i++)
881 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Up, 0, TRUE,
882 GTK_MOVEMENT_DISPLAY_LINES, -1);
883 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Up, 0, TRUE,
884 GTK_MOVEMENT_DISPLAY_LINES, -1);
886 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Down, 0, TRUE,
887 GTK_MOVEMENT_DISPLAY_LINES, 1);
888 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Down, 0, TRUE,
889 GTK_MOVEMENT_DISPLAY_LINES, 1);
891 pspp_sheet_view_add_move_binding (binding_set[i], GDK_p, GDK_CONTROL_MASK, FALSE,
892 GTK_MOVEMENT_DISPLAY_LINES, -1);
894 pspp_sheet_view_add_move_binding (binding_set[i], GDK_n, GDK_CONTROL_MASK, FALSE,
895 GTK_MOVEMENT_DISPLAY_LINES, 1);
897 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Home, 0, TRUE,
898 GTK_MOVEMENT_BUFFER_ENDS, -1);
899 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Home, 0, TRUE,
900 GTK_MOVEMENT_BUFFER_ENDS, -1);
902 pspp_sheet_view_add_move_binding (binding_set[i], GDK_End, 0, TRUE,
903 GTK_MOVEMENT_BUFFER_ENDS, 1);
904 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_End, 0, TRUE,
905 GTK_MOVEMENT_BUFFER_ENDS, 1);
907 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Up, 0, TRUE,
908 GTK_MOVEMENT_PAGES, -1);
909 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Up, 0, TRUE,
910 GTK_MOVEMENT_PAGES, -1);
912 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Down, 0, TRUE,
913 GTK_MOVEMENT_PAGES, 1);
914 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Down, 0, TRUE,
915 GTK_MOVEMENT_PAGES, 1);
918 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, 0, "move-cursor", 2,
919 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
922 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, 0, "move-cursor", 2,
923 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
926 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, 0, "move-cursor", 2,
927 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
930 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, GDK_SHIFT_MASK, "move-cursor", 2,
931 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
934 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, 0, "move-cursor", 2,
935 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
938 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, 0, "move-cursor", 2,
939 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
942 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, GDK_CONTROL_MASK,
944 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
947 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, GDK_CONTROL_MASK,
949 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
952 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, GDK_CONTROL_MASK,
954 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
957 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, GDK_CONTROL_MASK,
959 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
962 gtk_binding_entry_add_signal (binding_set[i], GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
964 gtk_binding_entry_add_signal (binding_set[i], GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
967 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
968 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
970 gtk_binding_entry_add_signal (binding_set[0], GDK_a, GDK_CONTROL_MASK, "select-all", 0);
971 gtk_binding_entry_add_signal (binding_set[0], GDK_slash, GDK_CONTROL_MASK, "select-all", 0);
973 gtk_binding_entry_add_signal (binding_set[0], GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
974 gtk_binding_entry_add_signal (binding_set[0], GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0);
976 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1,
977 G_TYPE_BOOLEAN, TRUE);
978 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1,
979 G_TYPE_BOOLEAN, TRUE);
981 gtk_binding_entry_add_signal (binding_set[0], GDK_space, 0, "select-cursor-row", 1,
982 G_TYPE_BOOLEAN, TRUE);
983 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, 0, "select-cursor-row", 1,
984 G_TYPE_BOOLEAN, TRUE);
985 gtk_binding_entry_add_signal (binding_set[0], GDK_Return, 0, "select-cursor-row", 1,
986 G_TYPE_BOOLEAN, TRUE);
987 gtk_binding_entry_add_signal (binding_set[0], GDK_ISO_Enter, 0, "select-cursor-row", 1,
988 G_TYPE_BOOLEAN, TRUE);
989 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Enter, 0, "select-cursor-row", 1,
990 G_TYPE_BOOLEAN, TRUE);
992 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, 0, "select-cursor-parent", 0);
993 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0);
995 g_type_class_add_private (o_class, sizeof (PsppSheetViewPrivate));
999 pspp_sheet_view_buildable_init (GtkBuildableIface *iface)
1001 iface->add_child = pspp_sheet_view_buildable_add_child;
1005 pspp_sheet_view_init (PsppSheetView *tree_view)
1007 tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, PSPP_TYPE_SHEET_VIEW, PsppSheetViewPrivate);
1009 gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
1010 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE);
1012 tree_view->priv->flags = PSPP_SHEET_VIEW_DRAW_KEYFOCUS
1013 | PSPP_SHEET_VIEW_HEADERS_VISIBLE;
1015 /* We need some padding */
1016 tree_view->priv->selected = range_tower_create ();
1017 tree_view->priv->dy = 0;
1018 tree_view->priv->cursor_offset = 0;
1019 tree_view->priv->n_columns = 0;
1020 tree_view->priv->header_height = 1;
1021 tree_view->priv->x_drag = 0;
1022 tree_view->priv->drag_pos = -1;
1023 tree_view->priv->header_has_focus = FALSE;
1024 tree_view->priv->pressed_button = -1;
1025 tree_view->priv->press_start_x = -1;
1026 tree_view->priv->press_start_y = -1;
1027 tree_view->priv->reorderable = FALSE;
1028 tree_view->priv->presize_handler_timer = 0;
1029 tree_view->priv->scroll_sync_timer = 0;
1030 tree_view->priv->fixed_height = -1;
1031 tree_view->priv->fixed_height_set = FALSE;
1032 pspp_sheet_view_set_adjustments (tree_view, NULL, NULL);
1033 tree_view->priv->selection = _pspp_sheet_selection_new_with_tree_view (tree_view);
1034 tree_view->priv->enable_search = TRUE;
1035 tree_view->priv->search_column = -1;
1036 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
1037 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
1038 tree_view->priv->search_custom_entry_set = FALSE;
1039 tree_view->priv->typeselect_flush_timeout = 0;
1040 tree_view->priv->init_hadjust_value = TRUE;
1041 tree_view->priv->width = 0;
1043 tree_view->priv->hover_selection = FALSE;
1045 tree_view->priv->rubber_banding_enable = FALSE;
1047 tree_view->priv->grid_lines = PSPP_SHEET_VIEW_GRID_LINES_NONE;
1049 tree_view->priv->tooltip_column = -1;
1051 tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT;
1053 tree_view->priv->post_validation_flag = FALSE;
1055 tree_view->priv->last_button_x = -1;
1056 tree_view->priv->last_button_y = -1;
1058 tree_view->priv->event_last_x = -10000;
1059 tree_view->priv->event_last_y = -10000;
1061 tree_view->priv->prelight_node = -1;
1062 tree_view->priv->rubber_band_start_node = -1;
1063 tree_view->priv->rubber_band_end_node = -1;
1065 tree_view->priv->anchor_column = NULL;
1067 tree_view->priv->button_style = NULL;
1069 tree_view->dispose_has_run = FALSE;
1078 pspp_sheet_view_set_property (GObject *object,
1080 const GValue *value,
1083 PsppSheetView *tree_view;
1085 tree_view = PSPP_SHEET_VIEW (object);
1090 pspp_sheet_view_set_model (tree_view, g_value_get_object (value));
1092 case PROP_HADJUSTMENT:
1093 pspp_sheet_view_set_hadjustment (tree_view, g_value_get_object (value));
1095 case PROP_VADJUSTMENT:
1096 pspp_sheet_view_set_vadjustment (tree_view, g_value_get_object (value));
1098 case PROP_HEADERS_VISIBLE:
1099 pspp_sheet_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1101 case PROP_HEADERS_CLICKABLE:
1102 pspp_sheet_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1104 case PROP_REORDERABLE:
1105 pspp_sheet_view_set_reorderable (tree_view, g_value_get_boolean (value));
1107 case PROP_RULES_HINT:
1108 pspp_sheet_view_set_rules_hint (tree_view, g_value_get_boolean (value));
1110 case PROP_ENABLE_SEARCH:
1111 pspp_sheet_view_set_enable_search (tree_view, g_value_get_boolean (value));
1113 case PROP_SEARCH_COLUMN:
1114 pspp_sheet_view_set_search_column (tree_view, g_value_get_int (value));
1116 case PROP_HOVER_SELECTION:
1117 tree_view->priv->hover_selection = g_value_get_boolean (value);
1119 case PROP_RUBBER_BANDING:
1120 tree_view->priv->rubber_banding_enable = g_value_get_boolean (value);
1122 case PROP_ENABLE_GRID_LINES:
1123 pspp_sheet_view_set_grid_lines (tree_view, g_value_get_enum (value));
1125 case PROP_TOOLTIP_COLUMN:
1126 pspp_sheet_view_set_tooltip_column (tree_view, g_value_get_int (value));
1128 case PROP_SPECIAL_CELLS:
1129 pspp_sheet_view_set_special_cells (tree_view, g_value_get_enum (value));
1131 case PROP_FIXED_HEIGHT:
1132 pspp_sheet_view_set_fixed_height (tree_view, g_value_get_int (value));
1134 case PROP_FIXED_HEIGHT_SET:
1135 if (g_value_get_boolean (value))
1137 if (!tree_view->priv->fixed_height_set
1138 && tree_view->priv->fixed_height >= 0)
1140 tree_view->priv->fixed_height_set = true;
1141 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1146 if (tree_view->priv->fixed_height_set)
1148 tree_view->priv->fixed_height_set = false;
1149 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1150 install_presize_handler (tree_view);
1155 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1161 pspp_sheet_view_get_property (GObject *object,
1166 PsppSheetView *tree_view;
1168 tree_view = PSPP_SHEET_VIEW (object);
1173 g_value_set_object (value, tree_view->priv->model);
1175 case PROP_HADJUSTMENT:
1176 g_value_set_object (value, tree_view->priv->hadjustment);
1178 case PROP_VADJUSTMENT:
1179 g_value_set_object (value, tree_view->priv->vadjustment);
1181 case PROP_HEADERS_VISIBLE:
1182 g_value_set_boolean (value, pspp_sheet_view_get_headers_visible (tree_view));
1184 case PROP_HEADERS_CLICKABLE:
1185 g_value_set_boolean (value, pspp_sheet_view_get_headers_clickable (tree_view));
1187 case PROP_REORDERABLE:
1188 g_value_set_boolean (value, tree_view->priv->reorderable);
1190 case PROP_RULES_HINT:
1191 g_value_set_boolean (value, tree_view->priv->has_rules);
1193 case PROP_ENABLE_SEARCH:
1194 g_value_set_boolean (value, tree_view->priv->enable_search);
1196 case PROP_SEARCH_COLUMN:
1197 g_value_set_int (value, tree_view->priv->search_column);
1199 case PROP_HOVER_SELECTION:
1200 g_value_set_boolean (value, tree_view->priv->hover_selection);
1202 case PROP_RUBBER_BANDING:
1203 g_value_set_boolean (value, tree_view->priv->rubber_banding_enable);
1205 case PROP_ENABLE_GRID_LINES:
1206 g_value_set_enum (value, tree_view->priv->grid_lines);
1208 case PROP_TOOLTIP_COLUMN:
1209 g_value_set_int (value, tree_view->priv->tooltip_column);
1211 case PROP_SPECIAL_CELLS:
1212 g_value_set_enum (value, tree_view->priv->special_cells);
1214 case PROP_FIXED_HEIGHT:
1215 g_value_set_int (value, pspp_sheet_view_get_fixed_height (tree_view));
1217 case PROP_FIXED_HEIGHT_SET:
1218 g_value_set_boolean (value, tree_view->priv->fixed_height_set);
1221 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1227 pspp_sheet_view_dispose (GObject *object)
1229 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1231 if (tree_view->dispose_has_run)
1234 tree_view->dispose_has_run = TRUE;
1236 if (tree_view->priv->selection != NULL)
1238 _pspp_sheet_selection_set_tree_view (tree_view->priv->selection, NULL);
1239 g_object_unref (tree_view->priv->selection);
1240 tree_view->priv->selection = NULL;
1243 if (tree_view->priv->hadjustment)
1245 g_object_unref (tree_view->priv->hadjustment);
1246 tree_view->priv->hadjustment = NULL;
1248 if (tree_view->priv->vadjustment)
1250 g_object_unref (tree_view->priv->vadjustment);
1251 tree_view->priv->vadjustment = NULL;
1254 if (tree_view->priv->button_style)
1256 g_object_unref (tree_view->priv->button_style);
1257 tree_view->priv->button_style = NULL;
1261 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->dispose (object);
1267 pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
1268 GtkBuilder *builder,
1272 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), PSPP_SHEET_VIEW_COLUMN (child));
1276 pspp_sheet_view_finalize (GObject *object)
1278 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1280 pspp_sheet_view_stop_editing (tree_view, TRUE);
1282 if (tree_view->priv->selected != NULL)
1284 range_tower_destroy (tree_view->priv->selected);
1285 tree_view->priv->selected = NULL;
1289 tree_view->priv->prelight_node = -1;
1292 if (tree_view->priv->scroll_to_path != NULL)
1294 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
1295 tree_view->priv->scroll_to_path = NULL;
1298 if (tree_view->priv->drag_dest_row != NULL)
1300 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
1301 tree_view->priv->drag_dest_row = NULL;
1304 if (tree_view->priv->top_row != NULL)
1306 gtk_tree_row_reference_free (tree_view->priv->top_row);
1307 tree_view->priv->top_row = NULL;
1310 if (tree_view->priv->column_drop_func_data &&
1311 tree_view->priv->column_drop_func_data_destroy)
1313 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
1314 tree_view->priv->column_drop_func_data = NULL;
1317 if (tree_view->priv->destroy_count_destroy &&
1318 tree_view->priv->destroy_count_data)
1320 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
1321 tree_view->priv->destroy_count_data = NULL;
1324 gtk_tree_row_reference_free (tree_view->priv->cursor);
1325 tree_view->priv->cursor = NULL;
1327 gtk_tree_row_reference_free (tree_view->priv->anchor);
1328 tree_view->priv->anchor = NULL;
1330 /* destroy interactive search dialog */
1331 if (tree_view->priv->search_window)
1333 gtk_widget_destroy (tree_view->priv->search_window);
1334 tree_view->priv->search_window = NULL;
1335 tree_view->priv->search_entry = NULL;
1336 if (tree_view->priv->typeselect_flush_timeout)
1338 g_source_remove (tree_view->priv->typeselect_flush_timeout);
1339 tree_view->priv->typeselect_flush_timeout = 0;
1343 if (tree_view->priv->search_destroy && tree_view->priv->search_user_data)
1345 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
1346 tree_view->priv->search_user_data = NULL;
1349 if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data)
1351 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
1352 tree_view->priv->search_position_user_data = NULL;
1355 pspp_sheet_view_set_model (tree_view, NULL);
1358 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->finalize (object);
1363 /* GtkWidget Methods
1366 /* GtkWidget::map helper */
1368 pspp_sheet_view_map_buttons (PsppSheetView *tree_view)
1372 g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
1374 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
1376 PsppSheetViewColumn *column;
1378 for (list = tree_view->priv->columns; list; list = list->next)
1380 column = list->data;
1381 if (column->button != NULL &&
1382 gtk_widget_get_visible (column->button) &&
1383 !gtk_widget_get_mapped (column->button))
1384 gtk_widget_map (column->button);
1386 for (list = tree_view->priv->columns; list; list = list->next)
1388 column = list->data;
1389 if (column->visible == FALSE || column->window == NULL)
1391 if (column->resizable)
1393 gdk_window_raise (column->window);
1394 gdk_window_show (column->window);
1397 gdk_window_hide (column->window);
1399 gdk_window_show (tree_view->priv->header_window);
1404 pspp_sheet_view_map (GtkWidget *widget)
1406 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1409 gtk_widget_set_mapped (widget, TRUE);
1411 tmp_list = tree_view->priv->children;
1414 PsppSheetViewChild *child = tmp_list->data;
1415 tmp_list = tmp_list->next;
1417 if (gtk_widget_get_visible (child->widget))
1419 if (!gtk_widget_get_mapped (child->widget))
1420 gtk_widget_map (child->widget);
1423 gdk_window_show (tree_view->priv->bin_window);
1425 pspp_sheet_view_map_buttons (tree_view);
1427 gdk_window_show (widget->window);
1431 pspp_sheet_view_realize (GtkWidget *widget)
1434 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1436 GdkWindowAttr attributes;
1437 gint attributes_mask;
1439 gtk_widget_set_realized (widget, TRUE);
1441 /* Make the main, clipping window */
1442 attributes.window_type = GDK_WINDOW_CHILD;
1443 attributes.x = widget->allocation.x;
1444 attributes.y = widget->allocation.y;
1445 attributes.width = widget->allocation.width;
1446 attributes.height = widget->allocation.height;
1447 attributes.wclass = GDK_INPUT_OUTPUT;
1448 attributes.visual = gtk_widget_get_visual (widget);
1449 attributes.colormap = gtk_widget_get_colormap (widget);
1450 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1452 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1454 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1455 &attributes, attributes_mask);
1456 gdk_window_set_user_data (widget->window, widget);
1458 /* Make the window for the tree */
1460 attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
1461 attributes.width = MAX (tree_view->priv->width, widget->allocation.width);
1462 attributes.height = widget->allocation.height;
1463 attributes.event_mask = (GDK_EXPOSURE_MASK |
1465 GDK_POINTER_MOTION_MASK |
1466 GDK_ENTER_NOTIFY_MASK |
1467 GDK_LEAVE_NOTIFY_MASK |
1468 GDK_BUTTON_PRESS_MASK |
1469 GDK_BUTTON_RELEASE_MASK |
1470 gtk_widget_get_events (widget));
1472 tree_view->priv->bin_window = gdk_window_new (widget->window,
1473 &attributes, attributes_mask);
1474 gdk_window_set_user_data (tree_view->priv->bin_window, widget);
1476 /* Make the column header window */
1479 attributes.width = MAX (tree_view->priv->width, widget->allocation.width);
1480 attributes.height = tree_view->priv->header_height;
1481 attributes.event_mask = (GDK_EXPOSURE_MASK |
1483 GDK_BUTTON_PRESS_MASK |
1484 GDK_BUTTON_RELEASE_MASK |
1485 GDK_KEY_PRESS_MASK |
1486 GDK_KEY_RELEASE_MASK |
1487 gtk_widget_get_events (widget));
1489 tree_view->priv->header_window = gdk_window_new (widget->window,
1490 &attributes, attributes_mask);
1491 gdk_window_set_user_data (tree_view->priv->header_window, widget);
1493 /* Add them all up. */
1494 widget->style = gtk_style_attach (widget->style, widget->window);
1495 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
1496 gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
1497 gtk_style_set_background (widget->style, tree_view->priv->header_window, GTK_STATE_NORMAL);
1499 tmp_list = tree_view->priv->children;
1502 PsppSheetViewChild *child = tmp_list->data;
1503 tmp_list = tmp_list->next;
1505 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
1508 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
1509 _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list->data));
1511 /* Need to call those here, since they create GCs */
1512 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
1514 install_presize_handler (tree_view);
1516 for (i = 0; i < 5; ++i)
1518 tree_view->priv->grid_line_gc[i] = gdk_gc_new (widget->window);
1519 gdk_gc_copy (tree_view->priv->grid_line_gc[i], widget->style->text_aa_gc[i]);
1524 pspp_sheet_view_unrealize (GtkWidget *widget)
1527 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1528 PsppSheetViewPrivate *priv = tree_view->priv;
1531 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget);
1533 if (priv->scroll_timeout != 0)
1535 g_source_remove (priv->scroll_timeout);
1536 priv->scroll_timeout = 0;
1539 if (priv->open_dest_timeout != 0)
1541 g_source_remove (priv->open_dest_timeout);
1542 priv->open_dest_timeout = 0;
1545 if (priv->presize_handler_timer != 0)
1547 g_source_remove (priv->presize_handler_timer);
1548 priv->presize_handler_timer = 0;
1551 if (priv->validate_rows_timer != 0)
1553 g_source_remove (priv->validate_rows_timer);
1554 priv->validate_rows_timer = 0;
1557 if (priv->scroll_sync_timer != 0)
1559 g_source_remove (priv->scroll_sync_timer);
1560 priv->scroll_sync_timer = 0;
1563 if (priv->typeselect_flush_timeout)
1565 g_source_remove (priv->typeselect_flush_timeout);
1566 priv->typeselect_flush_timeout = 0;
1569 for (list = priv->columns; list; list = list->next)
1570 _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list->data));
1572 gdk_window_set_user_data (priv->bin_window, NULL);
1573 gdk_window_destroy (priv->bin_window);
1574 priv->bin_window = NULL;
1576 gdk_window_set_user_data (priv->header_window, NULL);
1577 gdk_window_destroy (priv->header_window);
1578 priv->header_window = NULL;
1580 if (priv->drag_window)
1582 gdk_window_set_user_data (priv->drag_window, NULL);
1583 gdk_window_destroy (priv->drag_window);
1584 priv->drag_window = NULL;
1587 if (priv->drag_highlight_window)
1589 gdk_window_set_user_data (priv->drag_highlight_window, NULL);
1590 gdk_window_destroy (priv->drag_highlight_window);
1591 priv->drag_highlight_window = NULL;
1594 for (x = 0 ; x < 5 ; ++x)
1595 g_object_unref (priv->grid_line_gc[x]);
1597 if (tree_view->priv->columns != NULL)
1599 list = tree_view->priv->columns;
1602 PsppSheetViewColumn *column;
1603 column = PSPP_SHEET_VIEW_COLUMN (list->data);
1605 pspp_sheet_view_remove_column (tree_view, column);
1607 tree_view->priv->columns = NULL;
1611 /* GtkWidget::size_request helper */
1613 pspp_sheet_view_size_request_columns (PsppSheetView *tree_view)
1617 tree_view->priv->header_height = 0;
1619 if (tree_view->priv->model)
1621 for (list = tree_view->priv->columns; list; list = list->next)
1623 GtkRequisition requisition;
1624 PsppSheetViewColumn *column = list->data;
1626 pspp_sheet_view_column_size_request (column, &requisition);
1627 column->button_request = requisition.width;
1628 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
1634 /* Called only by ::size_request */
1636 pspp_sheet_view_update_size (PsppSheetView *tree_view)
1639 PsppSheetViewColumn *column;
1642 if (tree_view->priv->model == NULL)
1644 tree_view->priv->width = 0;
1645 tree_view->priv->prev_width = 0;
1646 tree_view->priv->height = 0;
1650 tree_view->priv->prev_width = tree_view->priv->width;
1651 tree_view->priv->width = 0;
1653 /* keep this in sync with size_allocate below */
1654 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
1656 gint real_requested_width = 0;
1657 column = list->data;
1658 if (!column->visible)
1661 if (column->use_resized_width)
1663 real_requested_width = column->resized_width;
1667 real_requested_width = column->fixed_width;
1670 if (column->min_width != -1)
1671 real_requested_width = MAX (real_requested_width, column->min_width);
1672 if (column->max_width != -1)
1673 real_requested_width = MIN (real_requested_width, column->max_width);
1675 tree_view->priv->width += real_requested_width;
1678 tree_view->priv->height = tree_view->priv->fixed_height * tree_view->priv->row_count;
1682 pspp_sheet_view_size_request (GtkWidget *widget,
1683 GtkRequisition *requisition)
1685 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1688 /* we validate some rows initially just to make sure we have some size.
1689 * In practice, with a lot of static lists, this should get a good width.
1691 initialize_fixed_height_mode (tree_view);
1692 pspp_sheet_view_size_request_columns (tree_view);
1693 pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget));
1695 requisition->width = tree_view->priv->width;
1696 requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
1698 tmp_list = tree_view->priv->children;
1702 PsppSheetViewChild *child = tmp_list->data;
1703 GtkRequisition child_requisition;
1705 tmp_list = tmp_list->next;
1707 if (gtk_widget_get_visible (child->widget))
1708 gtk_widget_size_request (child->widget, &child_requisition);
1713 invalidate_column (PsppSheetView *tree_view,
1714 PsppSheetViewColumn *column)
1716 gint column_offset = 0;
1718 GtkWidget *widget = GTK_WIDGET (tree_view);
1721 if (!gtk_widget_get_realized (widget))
1724 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1725 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
1727 list = (rtl ? list->prev : list->next))
1729 PsppSheetViewColumn *tmpcolumn = list->data;
1730 if (tmpcolumn == column)
1732 GdkRectangle invalid_rect;
1734 invalid_rect.x = column_offset;
1736 invalid_rect.width = column->width;
1737 invalid_rect.height = widget->allocation.height;
1739 gdk_window_invalidate_rect (widget->window, &invalid_rect, TRUE);
1743 column_offset += tmpcolumn->width;
1748 invalidate_last_column (PsppSheetView *tree_view)
1753 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1755 for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
1757 last_column = (rtl ? last_column->next : last_column->prev))
1759 if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible)
1761 invalidate_column (tree_view, last_column->data);
1768 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_view,
1769 PsppSheetViewColumn *column)
1771 gint real_requested_width;
1773 if (column->use_resized_width)
1775 real_requested_width = column->resized_width;
1779 real_requested_width = column->fixed_width;
1782 if (column->min_width != -1)
1783 real_requested_width = MAX (real_requested_width, column->min_width);
1784 if (column->max_width != -1)
1785 real_requested_width = MIN (real_requested_width, column->max_width);
1787 return real_requested_width;
1791 span_intersects (int a0, int a_width,
1792 int b0, int b_width)
1794 int a1 = a0 + a_width;
1795 int b1 = b0 + b_width;
1796 return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1);
1799 /* GtkWidget::size_allocate helper */
1801 pspp_sheet_view_size_allocate_columns (GtkWidget *widget,
1802 gboolean *width_changed)
1804 PsppSheetView *tree_view;
1805 GList *list, *first_column, *last_column;
1806 PsppSheetViewColumn *column;
1807 GtkAllocation allocation;
1809 gint extra, extra_per_column;
1810 gint full_requested_width = 0;
1811 gint number_of_expand_columns = 0;
1812 gboolean column_changed = FALSE;
1815 tree_view = PSPP_SHEET_VIEW (widget);
1817 for (last_column = g_list_last (tree_view->priv->columns);
1818 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
1819 last_column = last_column->prev)
1821 if (last_column == NULL)
1824 for (first_column = g_list_first (tree_view->priv->columns);
1825 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
1826 first_column = first_column->next)
1830 allocation.height = tree_view->priv->header_height;
1832 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1834 /* find out how many extra space and expandable columns we have */
1835 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
1837 column = (PsppSheetViewColumn *)list->data;
1839 if (!column->visible)
1842 full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1845 number_of_expand_columns++;
1848 extra = MAX (widget->allocation.width - full_requested_width, 0);
1849 if (number_of_expand_columns > 0)
1850 extra_per_column = extra/number_of_expand_columns;
1852 extra_per_column = 0;
1854 for (list = (rtl ? last_column : first_column);
1855 list != (rtl ? first_column->prev : last_column->next);
1856 list = (rtl ? list->prev : list->next))
1858 gint real_requested_width = 0;
1861 column = list->data;
1862 old_width = column->width;
1864 if (!column->visible)
1867 /* We need to handle the dragged button specially.
1869 if (column == tree_view->priv->drag_column)
1871 GtkAllocation drag_allocation;
1872 gdk_drawable_get_size (tree_view->priv->drag_window,
1873 &(drag_allocation.width),
1874 &(drag_allocation.height));
1875 drag_allocation.x = 0;
1876 drag_allocation.y = 0;
1877 pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column,
1879 width += drag_allocation.width;
1883 real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1885 allocation.x = width;
1886 column->width = real_requested_width;
1890 if (number_of_expand_columns == 1)
1892 /* We add the remander to the last column as
1894 column->width += extra;
1898 column->width += extra_per_column;
1899 extra -= extra_per_column;
1900 number_of_expand_columns --;
1904 if (column->width != old_width)
1905 g_object_notify (G_OBJECT (column), "width");
1907 allocation.width = column->width;
1908 width += column->width;
1910 if (column->width > old_width)
1911 column_changed = TRUE;
1913 pspp_sheet_view_column_size_allocate (column, &allocation);
1915 if (span_intersects (allocation.x, allocation.width,
1916 tree_view->priv->hadjustment->value,
1917 widget->allocation.width)
1918 && gtk_widget_get_realized (widget))
1919 pspp_sheet_view_column_set_need_button (column, TRUE);
1922 gdk_window_move_resize (column->window,
1923 allocation.x + (rtl ? 0 : allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
1925 TREE_VIEW_DRAG_WIDTH, allocation.height);
1928 /* We change the width here. The user might have been resizing columns,
1929 * so the total width of the tree view changes.
1931 tree_view->priv->width = width;
1933 *width_changed = TRUE;
1936 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
1941 pspp_sheet_view_size_allocate (GtkWidget *widget,
1942 GtkAllocation *allocation)
1944 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1946 gboolean width_changed = FALSE;
1947 gint old_width = widget->allocation.width;
1949 if (allocation->width != widget->allocation.width)
1950 width_changed = TRUE;
1952 widget->allocation = *allocation;
1954 tmp_list = tree_view->priv->children;
1958 GtkAllocation allocation;
1960 PsppSheetViewChild *child = tmp_list->data;
1961 tmp_list = tmp_list->next;
1963 /* totally ignore our child's requisition */
1964 allocation.x = child->x;
1965 allocation.y = child->y;
1966 allocation.width = child->width;
1967 allocation.height = child->height;
1968 gtk_widget_size_allocate (child->widget, &allocation);
1971 /* We size-allocate the columns first because the width of the
1972 * tree view (used in updating the adjustments below) might change.
1974 pspp_sheet_view_size_allocate_columns (widget, &width_changed);
1976 tree_view->priv->hadjustment->page_size = allocation->width;
1977 tree_view->priv->hadjustment->page_increment = allocation->width * 0.9;
1978 tree_view->priv->hadjustment->step_increment = allocation->width * 0.1;
1979 tree_view->priv->hadjustment->lower = 0;
1980 tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->page_size, tree_view->priv->width);
1982 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
1984 if (allocation->width < tree_view->priv->width)
1986 if (tree_view->priv->init_hadjust_value)
1988 tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
1989 tree_view->priv->init_hadjust_value = FALSE;
1991 else if (allocation->width != old_width)
1993 tree_view->priv->hadjustment->value = CLAMP (tree_view->priv->hadjustment->value - allocation->width + old_width, 0, tree_view->priv->width - allocation->width);
1996 tree_view->priv->hadjustment->value = CLAMP (tree_view->priv->width - (tree_view->priv->prev_width - tree_view->priv->hadjustment->value), 0, tree_view->priv->width - allocation->width);
2000 tree_view->priv->hadjustment->value = 0;
2001 tree_view->priv->init_hadjust_value = TRUE;
2005 if (tree_view->priv->hadjustment->value + allocation->width > tree_view->priv->width)
2006 tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
2008 gtk_adjustment_changed (tree_view->priv->hadjustment);
2010 tree_view->priv->vadjustment->page_size = allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view);
2011 tree_view->priv->vadjustment->step_increment = tree_view->priv->vadjustment->page_size * 0.1;
2012 tree_view->priv->vadjustment->page_increment = tree_view->priv->vadjustment->page_size * 0.9;
2013 tree_view->priv->vadjustment->lower = 0;
2014 tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->page_size, tree_view->priv->height);
2016 gtk_adjustment_changed (tree_view->priv->vadjustment);
2018 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2019 if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size)
2020 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2021 else if (tree_view->priv->vadjustment->value + tree_view->priv->vadjustment->page_size > tree_view->priv->height)
2022 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2023 tree_view->priv->height - tree_view->priv->vadjustment->page_size);
2024 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2025 pspp_sheet_view_top_row_to_dy (tree_view);
2027 pspp_sheet_view_dy_to_top_row (tree_view);
2029 if (gtk_widget_get_realized (widget))
2031 gdk_window_move_resize (widget->window,
2032 allocation->x, allocation->y,
2033 allocation->width, allocation->height);
2034 gdk_window_move_resize (tree_view->priv->header_window,
2035 - (gint) tree_view->priv->hadjustment->value,
2037 MAX (tree_view->priv->width, allocation->width),
2038 tree_view->priv->header_height);
2039 gdk_window_move_resize (tree_view->priv->bin_window,
2040 - (gint) tree_view->priv->hadjustment->value,
2041 TREE_VIEW_HEADER_HEIGHT (tree_view),
2042 MAX (tree_view->priv->width, allocation->width),
2043 allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2046 if (tree_view->priv->row_count == 0)
2047 invalidate_empty_focus (tree_view);
2049 if (gtk_widget_get_realized (widget))
2051 gboolean has_expand_column = FALSE;
2052 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2054 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2056 has_expand_column = TRUE;
2061 /* This little hack only works if we have an LTR locale, and no column has the */
2064 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2065 ! has_expand_column)
2066 invalidate_last_column (tree_view);
2068 gtk_widget_queue_draw (widget);
2073 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2075 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2077 GtkWidget *widget = GTK_WIDGET (tree_view);
2079 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2080 gtk_widget_grab_focus (widget);
2081 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2085 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2088 return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2092 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2095 range_tower_set1 (tree_view->priv->selected, node, 1);
2099 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2102 range_tower_set0 (tree_view->priv->selected, node, 1);
2106 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2109 return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2113 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2116 return node > 0 ? node - 1 : -1;
2120 all_columns_selected (PsppSheetView *tree_view)
2124 for (list = tree_view->priv->columns; list; list = list->next)
2126 PsppSheetViewColumn *column = list->data;
2127 if (column->selectable && !column->selected)
2135 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2137 PsppSheetViewColumn *column,
2138 GdkEventButton *event)
2140 PsppSheetSelection *selection;
2141 PsppSheetSelectionMode mode;
2143 gboolean update_anchor;
2147 g_return_val_if_fail (tree_view != NULL, FALSE);
2148 g_return_val_if_fail (column != NULL, FALSE);
2150 selection = tree_view->priv->selection;
2151 mode = pspp_sheet_selection_get_mode (selection);
2152 if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2155 if (!column->row_head)
2160 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2161 if (event->type != GDK_BUTTON_PRESS
2162 || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2168 path = gtk_tree_path_new_from_indices (node, -1);
2171 pspp_sheet_selection_unselect_all (selection);
2172 pspp_sheet_selection_select_path (selection, path);
2173 pspp_sheet_selection_select_all_columns (selection);
2174 update_anchor = TRUE;
2177 else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2179 if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2180 || !all_columns_selected (tree_view))
2182 pspp_sheet_selection_unselect_all (selection);
2183 pspp_sheet_selection_select_path (selection, path);
2184 pspp_sheet_selection_select_all_columns (selection);
2185 update_anchor = TRUE;
2189 update_anchor = handled = FALSE;
2191 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2192 && modifiers == GDK_CONTROL_MASK)
2194 if (!all_columns_selected (tree_view))
2196 pspp_sheet_selection_unselect_all (selection);
2197 pspp_sheet_selection_select_all_columns (selection);
2200 if (pspp_sheet_selection_path_is_selected (selection, path))
2201 pspp_sheet_selection_unselect_path (selection, path);
2203 pspp_sheet_selection_select_path (selection, path);
2204 update_anchor = TRUE;
2207 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2208 && modifiers == GDK_SHIFT_MASK)
2210 GtkTreeRowReference *anchor = tree_view->priv->anchor;
2211 GtkTreePath *anchor_path;
2213 if (all_columns_selected (tree_view)
2214 && gtk_tree_row_reference_valid (anchor))
2216 update_anchor = FALSE;
2217 anchor_path = gtk_tree_row_reference_get_path (anchor);
2221 update_anchor = TRUE;
2222 anchor_path = gtk_tree_path_copy (path);
2225 pspp_sheet_selection_unselect_all (selection);
2226 pspp_sheet_selection_select_range (selection, anchor_path, path);
2227 pspp_sheet_selection_select_all_columns (selection);
2229 gtk_tree_path_free (anchor_path);
2234 update_anchor = handled = FALSE;
2238 if (tree_view->priv->anchor)
2239 gtk_tree_row_reference_free (tree_view->priv->anchor);
2240 tree_view->priv->anchor =
2241 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2242 tree_view->priv->model,
2246 gtk_tree_path_free (path);
2251 find_click (PsppSheetView *tree_view,
2254 PsppSheetViewColumn **column,
2255 GdkRectangle *background_area,
2256 GdkRectangle *cell_area)
2263 /* find the node that was clicked */
2264 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2267 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2272 background_area->y = y_offset + y;
2273 background_area->height = ROW_HEIGHT (tree_view);
2274 background_area->x = 0;
2276 /* Let the column have a chance at selecting it. */
2277 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2278 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2279 list; list = (rtl ? list->prev : list->next))
2281 PsppSheetViewColumn *candidate = list->data;
2283 if (!candidate->visible)
2286 background_area->width = candidate->width;
2287 if ((background_area->x > x) ||
2288 (background_area->x + background_area->width <= x))
2290 background_area->x += background_area->width;
2294 /* we found the focus column */
2296 pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2298 *column = candidate;
2306 pspp_sheet_view_button_press (GtkWidget *widget,
2307 GdkEventButton *event)
2309 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2311 PsppSheetViewColumn *column = NULL;
2313 GdkRectangle background_area;
2314 GdkRectangle cell_area;
2317 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2318 pspp_sheet_view_stop_editing (tree_view, FALSE);
2321 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2322 * we're done handling the button press.
2325 if (event->window == tree_view->priv->bin_window)
2330 gint pre_val, aft_val;
2331 PsppSheetViewColumn *column = NULL;
2332 GtkCellRenderer *focus_cell = NULL;
2333 gboolean row_double_click = FALSE;
2336 if (tree_view->priv->row_count == 0)
2338 grab_focus_and_unset_draw_keyfocus (tree_view);
2342 if (!find_click (tree_view, event->x, event->y, &node, &column,
2343 &background_area, &cell_area))
2345 grab_focus_and_unset_draw_keyfocus (tree_view);
2349 tree_view->priv->focus_column = column;
2351 if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2355 pre_val = tree_view->priv->vadjustment->value;
2357 path = _pspp_sheet_view_find_path (tree_view, node);
2359 /* we only handle selection modifications on the first button press
2361 if (event->type == GDK_BUTTON_PRESS)
2363 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2364 tree_view->priv->ctrl_pressed = TRUE;
2365 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2366 tree_view->priv->shift_pressed = TRUE;
2368 focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2370 pspp_sheet_view_column_focus_cell (column, focus_cell);
2372 if (event->state & GDK_CONTROL_MASK)
2374 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE);
2375 pspp_sheet_view_real_toggle_cursor_row (tree_view);
2377 else if (event->state & GDK_SHIFT_MASK)
2379 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE);
2380 pspp_sheet_view_real_select_cursor_row (tree_view, FALSE);
2384 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE);
2387 if (tree_view->priv->anchor_column == NULL ||
2388 !(event->state & GDK_SHIFT_MASK))
2389 tree_view->priv->anchor_column = column;
2390 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2391 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2392 tree_view->priv->anchor_column,
2395 tree_view->priv->ctrl_pressed = FALSE;
2396 tree_view->priv->shift_pressed = FALSE;
2399 /* the treeview may have been scrolled because of _set_cursor,
2403 aft_val = tree_view->priv->vadjustment->value;
2404 dval = pre_val - aft_val;
2406 cell_area.y += dval;
2407 background_area.y += dval;
2409 /* Save press to possibly begin a drag
2411 if (!tree_view->priv->in_grab &&
2412 tree_view->priv->pressed_button < 0)
2414 tree_view->priv->pressed_button = event->button;
2415 tree_view->priv->press_start_x = event->x;
2416 tree_view->priv->press_start_y = event->y;
2417 tree_view->priv->press_start_node = node;
2419 if (tree_view->priv->rubber_banding_enable
2420 && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2421 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2423 tree_view->priv->press_start_y += tree_view->priv->dy;
2424 tree_view->priv->rubber_band_x = event->x;
2425 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2426 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2428 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2429 tree_view->priv->rubber_band_ctrl = TRUE;
2430 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2431 tree_view->priv->rubber_band_shift = TRUE;
2436 /* Test if a double click happened on the same row. */
2437 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2439 int double_click_time, double_click_distance;
2441 g_object_get (gtk_settings_get_for_screen (
2442 gtk_widget_get_screen (widget)),
2443 "gtk-double-click-time", &double_click_time,
2444 "gtk-double-click-distance", &double_click_distance,
2447 /* Same conditions as _gdk_event_button_generate */
2448 if (tree_view->priv->last_button_x != -1 &&
2449 (event->time < tree_view->priv->last_button_time + double_click_time) &&
2450 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2451 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2453 /* We do no longer compare paths of this row and the
2454 * row clicked previously. We use the double click
2455 * distance to decide whether this is a valid click,
2456 * allowing the mouse to slightly move over another row.
2458 row_double_click = TRUE;
2460 tree_view->priv->last_button_time = 0;
2461 tree_view->priv->last_button_x = -1;
2462 tree_view->priv->last_button_y = -1;
2466 tree_view->priv->last_button_time = event->time;
2467 tree_view->priv->last_button_x = event->x;
2468 tree_view->priv->last_button_y = event->y;
2472 if (row_double_click)
2474 gtk_grab_remove (widget);
2475 pspp_sheet_view_row_activated (tree_view, path, column);
2477 if (tree_view->priv->pressed_button == event->button)
2478 tree_view->priv->pressed_button = -1;
2481 gtk_tree_path_free (path);
2483 /* If we activated the row through a double click we don't want to grab
2484 * focus back, as moving focus to another widget is pretty common.
2486 if (!row_double_click)
2487 grab_focus_and_unset_draw_keyfocus (tree_view);
2492 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2494 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2496 column = list->data;
2497 if (event->window == column->window &&
2498 column->resizable &&
2503 if (gdk_pointer_grab (column->window, FALSE,
2504 GDK_POINTER_MOTION_HINT_MASK |
2505 GDK_BUTTON1_MOTION_MASK |
2506 GDK_BUTTON_RELEASE_MASK,
2507 NULL, NULL, event->time))
2510 gtk_grab_add (widget);
2511 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2512 column->resized_width = column->width;
2514 /* block attached dnd signal handler */
2515 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2517 g_signal_handlers_block_matched (widget,
2518 G_SIGNAL_MATCH_DATA,
2522 tree_view->priv->drag_pos = i;
2523 tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2525 if (!gtk_widget_has_focus (widget))
2526 gtk_widget_grab_focus (widget);
2534 /* GtkWidget::button_release_event helper */
2536 pspp_sheet_view_button_release_drag_column (GtkWidget *widget,
2537 GdkEventButton *event)
2539 PsppSheetView *tree_view;
2543 tree_view = PSPP_SHEET_VIEW (widget);
2545 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2546 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2547 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2549 /* Move the button back */
2550 g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2552 g_object_ref (tree_view->priv->drag_column->button);
2553 gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2554 gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2555 gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2556 g_object_unref (tree_view->priv->drag_column->button);
2557 gtk_widget_queue_resize (widget);
2558 if (tree_view->priv->drag_column->resizable)
2560 gdk_window_raise (tree_view->priv->drag_column->window);
2561 gdk_window_show (tree_view->priv->drag_column->window);
2564 gdk_window_hide (tree_view->priv->drag_column->window);
2566 gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2570 if (tree_view->priv->cur_reorder &&
2571 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2572 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2573 tree_view->priv->cur_reorder->right_column);
2577 if (tree_view->priv->cur_reorder &&
2578 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2579 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2580 tree_view->priv->cur_reorder->left_column);
2582 tree_view->priv->drag_column = NULL;
2583 gdk_window_hide (tree_view->priv->drag_window);
2585 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2586 g_slice_free (PsppSheetViewColumnReorder, l->data);
2587 g_list_free (tree_view->priv->column_drag_info);
2588 tree_view->priv->column_drag_info = NULL;
2589 tree_view->priv->cur_reorder = NULL;
2591 if (tree_view->priv->drag_highlight_window)
2592 gdk_window_hide (tree_view->priv->drag_highlight_window);
2594 /* Reset our flags */
2595 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2596 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2601 /* GtkWidget::button_release_event helper */
2603 pspp_sheet_view_button_release_column_resize (GtkWidget *widget,
2604 GdkEventButton *event)
2606 PsppSheetView *tree_view;
2609 tree_view = PSPP_SHEET_VIEW (widget);
2611 tree_view->priv->drag_pos = -1;
2613 /* unblock attached dnd signal handler */
2614 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2616 g_signal_handlers_unblock_matched (widget,
2617 G_SIGNAL_MATCH_DATA,
2621 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2622 gtk_grab_remove (widget);
2623 gdk_display_pointer_ungrab (gdk_drawable_get_display (event->window),
2629 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2630 GdkEventButton *event)
2632 GtkCellEditable *cell_editable;
2637 PsppSheetViewColumn *column;
2638 GdkRectangle background_area;
2639 GdkRectangle cell_area;
2645 if (event->window != tree_view->priv->bin_window)
2648 /* Ignore a released button, if that button wasn't depressed */
2649 if (tree_view->priv->pressed_button != event->button)
2652 if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2656 /* decide if we edit */
2657 path = _pspp_sheet_view_find_path (tree_view, node);
2658 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2659 if (event->button != 1 || modifiers)
2662 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2663 pspp_sheet_view_column_cell_set_cell_data (column,
2664 tree_view->priv->model,
2667 if (!pspp_sheet_view_column_get_quick_edit (column)
2668 && _pspp_sheet_view_column_has_editable_cell (column))
2671 flags = 0; /* FIXME: get the right flags */
2672 path_string = gtk_tree_path_to_string (path);
2674 if (!_pspp_sheet_view_column_cell_event (column,
2682 if (cell_editable == NULL)
2685 pspp_sheet_view_real_set_cursor (tree_view, path,
2687 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2690 _pspp_sheet_view_column_get_neighbor_sizes (
2691 column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2694 area.width -= right + left;
2696 pspp_sheet_view_real_start_editing (tree_view,
2703 g_free (path_string);
2704 gtk_tree_path_free (path);
2709 pspp_sheet_view_button_release (GtkWidget *widget,
2710 GdkEventButton *event)
2712 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2714 pspp_sheet_view_stop_editing (tree_view, FALSE);
2715 if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2716 && pspp_sheet_view_button_release_edit (tree_view, event))
2718 if (tree_view->priv->pressed_button == event->button)
2719 tree_view->priv->pressed_button = -1;
2721 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2725 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2726 return pspp_sheet_view_button_release_drag_column (widget, event);
2728 if (tree_view->priv->rubber_band_status)
2729 pspp_sheet_view_stop_rubber_band (tree_view);
2731 if (tree_view->priv->pressed_button == event->button)
2732 tree_view->priv->pressed_button = -1;
2734 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2735 return pspp_sheet_view_button_release_column_resize (widget, event);
2741 pspp_sheet_view_grab_broken (GtkWidget *widget,
2742 GdkEventGrabBroken *event)
2744 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2746 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2747 pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2749 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2750 pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2755 /* GtkWidget::motion_event function set.
2759 do_prelight (PsppSheetView *tree_view,
2761 /* these are in bin_window coords */
2765 int prev_node = tree_view->priv->prelight_node;
2767 if (prev_node != node)
2769 tree_view->priv->prelight_node = node;
2772 _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2775 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2780 prelight_or_select (PsppSheetView *tree_view,
2782 /* these are in bin_window coords */
2786 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2788 if (tree_view->priv->hover_selection &&
2789 (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2790 !(tree_view->priv->edited_column &&
2791 tree_view->priv->edited_column->editable_widget))
2795 if (!pspp_sheet_view_node_is_selected (tree_view, node))
2799 path = _pspp_sheet_view_find_path (tree_view, node);
2800 pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2801 if (pspp_sheet_view_node_is_selected (tree_view, node))
2803 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2804 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE);
2806 gtk_tree_path_free (path);
2810 else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2811 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2814 do_prelight (tree_view, node, x, y);
2818 ensure_unprelighted (PsppSheetView *tree_view)
2820 do_prelight (tree_view,
2822 -1000, -1000); /* coords not possibly over an arrow */
2824 g_assert (tree_view->priv->prelight_node < 0);
2828 update_prelight (PsppSheetView *tree_view,
2835 if (tree_view->priv->row_count == 0)
2840 ensure_unprelighted (tree_view);
2844 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2848 pspp_sheet_view_find_offset (tree_view, new_y, &node);
2851 prelight_or_select (tree_view, node, x, y);
2857 /* Our motion arrow is either a box (in the case of the original spot)
2858 * or an arrow. It is expander_size wide.
2881 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2883 PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2884 GtkWidget *widget = GTK_WIDGET (tree_view);
2885 GdkBitmap *mask = NULL;
2890 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2891 GdkWindowAttr attributes;
2892 guint attributes_mask;
2895 reorder->left_column == tree_view->priv->drag_column ||
2896 reorder->right_column == tree_view->priv->drag_column)
2897 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2898 else if (reorder->left_column || reorder->right_column)
2900 GdkRectangle visible_rect;
2901 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2902 if (reorder->left_column)
2903 x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2905 x = reorder->right_column->allocation.x;
2907 if (x < visible_rect.x)
2908 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2909 else if (x > visible_rect.x + visible_rect.width)
2910 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2912 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2915 /* We want to draw the rectangle over the initial location. */
2916 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2921 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2923 if (tree_view->priv->drag_highlight_window)
2925 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2927 gdk_window_destroy (tree_view->priv->drag_highlight_window);
2930 attributes.window_type = GDK_WINDOW_CHILD;
2931 attributes.wclass = GDK_INPUT_OUTPUT;
2932 attributes.x = tree_view->priv->drag_column_x;
2934 width = attributes.width = tree_view->priv->drag_column->allocation.width;
2935 height = attributes.height = tree_view->priv->drag_column->allocation.height;
2936 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
2937 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
2938 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
2939 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
2940 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
2941 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
2943 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
2944 gc = gdk_gc_new (mask);
2946 gdk_gc_set_foreground (gc, &col);
2947 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
2949 gdk_gc_set_foreground(gc, &col);
2950 gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
2951 g_object_unref (gc);
2953 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
2955 if (mask) g_object_unref (mask);
2956 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2959 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
2965 width = tree_view->priv->expander_size;
2967 /* Get x, y, width, height of arrow */
2968 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
2969 if (reorder->left_column)
2971 x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
2972 height = reorder->left_column->allocation.height;
2976 x += reorder->right_column->allocation.x - width/2;
2977 height = reorder->right_column->allocation.height;
2979 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
2980 height += tree_view->priv->expander_size;
2982 /* Create the new window */
2983 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
2985 if (tree_view->priv->drag_highlight_window)
2987 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2989 gdk_window_destroy (tree_view->priv->drag_highlight_window);
2992 attributes.window_type = GDK_WINDOW_TEMP;
2993 attributes.wclass = GDK_INPUT_OUTPUT;
2994 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
2995 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
2996 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
2997 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3000 attributes.width = width;
3001 attributes.height = height;
3002 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3003 &attributes, attributes_mask);
3004 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3006 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3007 gc = gdk_gc_new (mask);
3009 gdk_gc_set_foreground (gc, &col);
3010 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3012 /* Draw the 2 arrows as per above */
3014 gdk_gc_set_foreground (gc, &col);
3015 for (i = 0; i < width; i ++)
3017 if (i == (width/2 - 1))
3019 gdk_draw_line (mask, gc, i, j, i, height - j);
3020 if (i < (width/2 - 1))
3025 g_object_unref (gc);
3026 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3028 if (mask) g_object_unref (mask);
3031 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3032 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3034 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3035 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3041 width = tree_view->priv->expander_size;
3043 /* Get x, y, width, height of arrow */
3044 width = width/2; /* remember, the arrow only takes half the available width */
3045 gdk_window_get_origin (widget->window, &x, &y);
3046 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3047 x += widget->allocation.width - width;
3049 if (reorder->left_column)
3050 height = reorder->left_column->allocation.height;
3052 height = reorder->right_column->allocation.height;
3054 y -= tree_view->priv->expander_size;
3055 height += 2*tree_view->priv->expander_size;
3057 /* Create the new window */
3058 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3059 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3061 if (tree_view->priv->drag_highlight_window)
3063 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3065 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3068 attributes.window_type = GDK_WINDOW_TEMP;
3069 attributes.wclass = GDK_INPUT_OUTPUT;
3070 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3071 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3072 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3073 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3076 attributes.width = width;
3077 attributes.height = height;
3078 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3079 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3081 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3082 gc = gdk_gc_new (mask);
3084 gdk_gc_set_foreground (gc, &col);
3085 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3087 /* Draw the 2 arrows as per above */
3089 gdk_gc_set_foreground (gc, &col);
3090 j = tree_view->priv->expander_size;
3091 for (i = 0; i < width; i ++)
3094 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3098 gdk_draw_line (mask, gc, k, j, k, height - j);
3099 gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3100 gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3103 g_object_unref (gc);
3104 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3106 if (mask) g_object_unref (mask);
3109 tree_view->priv->drag_column_window_state = arrow_type;
3110 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3114 g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3115 gdk_window_hide (tree_view->priv->drag_highlight_window);
3119 gdk_window_show (tree_view->priv->drag_highlight_window);
3120 gdk_window_raise (tree_view->priv->drag_highlight_window);
3124 pspp_sheet_view_motion_resize_column (GtkWidget *widget,
3125 GdkEventMotion *event)
3129 PsppSheetViewColumn *column;
3130 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3132 column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3134 if (event->is_hint || event->window != widget->window)
3135 gtk_widget_get_pointer (widget, &x, NULL);
3139 if (tree_view->priv->hadjustment)
3140 x += tree_view->priv->hadjustment->value;
3142 new_width = pspp_sheet_view_new_column_width (tree_view,
3143 tree_view->priv->drag_pos, &x);
3144 if (x != tree_view->priv->x_drag &&
3145 (new_width != column->fixed_width))
3147 column->use_resized_width = TRUE;
3148 column->resized_width = new_width;
3151 column->resized_width -= tree_view->priv->last_extra_space_per_column;
3153 gtk_widget_queue_resize (widget);
3161 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3163 PsppSheetViewColumnReorder *reorder = NULL;
3167 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3168 for (list = tree_view->priv->column_drag_info; list; list = list->next)
3170 reorder = (PsppSheetViewColumnReorder *) list->data;
3171 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3176 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3179 tree_view->priv->cur_reorder = reorder;
3180 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3184 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3186 GdkRectangle visible_rect;
3191 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3192 y += tree_view->priv->dy;
3194 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3196 /* see if we are near the edge. */
3197 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3200 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3205 value = CLAMP (tree_view->priv->vadjustment->value + offset, 0.0,
3206 tree_view->priv->vadjustment->upper - tree_view->priv->vadjustment->page_size);
3207 gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3211 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3213 GdkRectangle visible_rect;
3218 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3220 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3222 /* See if we are near the edge. */
3223 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3226 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3232 value = CLAMP (tree_view->priv->hadjustment->value + offset,
3233 0.0, tree_view->priv->hadjustment->upper - tree_view->priv->hadjustment->page_size);
3234 gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3241 pspp_sheet_view_motion_drag_column (GtkWidget *widget,
3242 GdkEventMotion *event)
3244 PsppSheetView *tree_view = (PsppSheetView *) widget;
3245 PsppSheetViewColumn *column = tree_view->priv->drag_column;
3249 if ((column == NULL) ||
3250 (event->window != tree_view->priv->drag_window))
3253 /* Handle moving the header */
3254 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3255 x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3256 MAX (tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.width) - column->allocation.width);
3257 gdk_window_move (tree_view->priv->drag_window, x, y);
3259 /* autoscroll, if needed */
3260 pspp_sheet_view_horizontal_autoscroll (tree_view);
3261 /* Update the current reorder position and arrow; */
3262 pspp_sheet_view_update_current_reorder (tree_view);
3268 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3270 remove_scroll_timeout (tree_view);
3271 gtk_grab_remove (GTK_WIDGET (tree_view));
3273 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3275 GtkTreePath *tmp_path;
3277 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3279 /* The anchor path should be set to the start path */
3280 tmp_path = _pspp_sheet_view_find_path (tree_view,
3281 tree_view->priv->rubber_band_start_node);
3283 if (tree_view->priv->anchor)
3284 gtk_tree_row_reference_free (tree_view->priv->anchor);
3286 tree_view->priv->anchor =
3287 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3288 tree_view->priv->model,
3291 gtk_tree_path_free (tmp_path);
3293 /* ... and the cursor to the end path */
3294 tmp_path = _pspp_sheet_view_find_path (tree_view,
3295 tree_view->priv->rubber_band_end_node);
3296 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE);
3297 gtk_tree_path_free (tmp_path);
3299 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3302 /* Clear status variables */
3303 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3304 tree_view->priv->rubber_band_shift = 0;
3305 tree_view->priv->rubber_band_ctrl = 0;
3307 tree_view->priv->rubber_band_start_node = -1;
3308 tree_view->priv->rubber_band_end_node = -1;
3312 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3316 gboolean skip_start,
3319 if (start_node == end_node)
3322 /* We skip the first node and jump inside the loop */
3328 /* Small optimization by assuming insensitive nodes are never
3333 if (tree_view->priv->rubber_band_shift)
3334 pspp_sheet_view_node_select (tree_view, start_node);
3335 else if (tree_view->priv->rubber_band_ctrl)
3337 /* Toggle the selection state */
3338 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3339 pspp_sheet_view_node_unselect (tree_view, start_node);
3341 pspp_sheet_view_node_select (tree_view, start_node);
3344 pspp_sheet_view_node_select (tree_view, start_node);
3348 /* Mirror the above */
3349 if (tree_view->priv->rubber_band_shift)
3350 pspp_sheet_view_node_unselect (tree_view, start_node);
3351 else if (tree_view->priv->rubber_band_ctrl)
3353 /* Toggle the selection state */
3354 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3355 pspp_sheet_view_node_unselect (tree_view, start_node);
3357 pspp_sheet_view_node_select (tree_view, start_node);
3360 pspp_sheet_view_node_unselect (tree_view, start_node);
3363 _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3365 if (start_node == end_node)
3370 start_node = pspp_sheet_view_node_next (tree_view, start_node);
3373 /* Ran out of tree */
3376 if (skip_end && start_node == end_node)
3383 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3386 return node * tree_view->priv->fixed_height;
3390 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3394 int fixed_height = tree_view->priv->fixed_height;
3395 if (fixed_height <= 0
3397 || height >= tree_view->priv->row_count * fixed_height)
3404 *new_node = height / fixed_height;
3405 return height % fixed_height;
3410 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3415 pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3416 pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3418 /* Handle the start area first */
3419 if (tree_view->priv->rubber_band_start_node < 0)
3421 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3428 else if (start_node < tree_view->priv->rubber_band_start_node)
3430 /* New node is above the old one; selection became bigger */
3431 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3433 tree_view->priv->rubber_band_start_node,
3438 else if (start_node > tree_view->priv->rubber_band_start_node)
3440 /* New node is below the old one; selection became smaller */
3441 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3442 tree_view->priv->rubber_band_start_node,
3449 tree_view->priv->rubber_band_start_node = start_node;
3451 /* Next, handle the end area */
3452 if (tree_view->priv->rubber_band_end_node < 0)
3454 /* In the event this happens, start_node was also -1; this case is
3458 else if (end_node < 0)
3460 /* Find the last node in the tree */
3461 pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3464 /* Selection reached end of the tree */
3465 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3466 tree_view->priv->rubber_band_end_node,
3472 else if (end_node > tree_view->priv->rubber_band_end_node)
3474 /* New node is below the old one; selection became bigger */
3475 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3476 tree_view->priv->rubber_band_end_node,
3482 else if (end_node < tree_view->priv->rubber_band_end_node)
3484 /* New node is above the old one; selection became smaller */
3485 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3487 tree_view->priv->rubber_band_end_node,
3493 tree_view->priv->rubber_band_end_node = end_node;
3497 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3500 GdkRectangle old_area;
3501 GdkRectangle new_area;
3502 GdkRectangle common;
3503 GdkRegion *invalid_region;
3504 PsppSheetViewColumn *column;
3506 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3507 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3508 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3509 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3511 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3514 y = MAX (y, 0) + tree_view->priv->dy;
3516 new_area.x = MIN (tree_view->priv->press_start_x, x);
3517 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3518 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3519 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3521 invalid_region = gdk_region_rectangle (&old_area);
3522 gdk_region_union_with_rect (invalid_region, &new_area);
3524 gdk_rectangle_intersect (&old_area, &new_area, &common);
3525 if (common.width > 2 && common.height > 2)
3527 GdkRegion *common_region;
3529 /* make sure the border is invalidated */
3535 common_region = gdk_region_rectangle (&common);
3537 gdk_region_subtract (invalid_region, common_region);
3538 gdk_region_destroy (common_region);
3541 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
3543 gdk_region_destroy (invalid_region);
3545 tree_view->priv->rubber_band_x = x;
3546 tree_view->priv->rubber_band_y = y;
3547 pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3549 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3550 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3551 tree_view->priv->anchor_column,
3554 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3556 pspp_sheet_view_update_rubber_band_selection (tree_view);
3560 pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view,
3565 GdkRectangle rubber_rect;
3568 rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3569 rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3570 rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3571 rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3573 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3576 cr = gdk_cairo_create (tree_view->priv->bin_window);
3577 cairo_set_line_width (cr, 1.0);
3579 cairo_set_source_rgba (cr,
3580 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].red / 65535.,
3581 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].green / 65535.,
3582 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].blue / 65535.,
3585 gdk_cairo_rectangle (cr, &rect);
3589 cairo_set_source_rgb (cr,
3590 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].red / 65535.,
3591 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].green / 65535.,
3592 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].blue / 65535.);
3594 cairo_rectangle (cr,
3595 rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3596 rubber_rect.width - 1, rubber_rect.height - 1);
3603 pspp_sheet_view_motion_bin_window (GtkWidget *widget,
3604 GdkEventMotion *event)
3606 PsppSheetView *tree_view;
3610 tree_view = (PsppSheetView *) widget;
3612 if (tree_view->priv->row_count == 0)
3615 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3617 GdkRectangle background_area, cell_area;
3618 PsppSheetViewColumn *column;
3620 if (find_click (tree_view, event->x, event->y, &node, &column,
3621 &background_area, &cell_area)
3622 && tree_view->priv->focus_column == column
3623 && tree_view->priv->press_start_node == node)
3626 gtk_grab_add (GTK_WIDGET (tree_view));
3627 pspp_sheet_view_update_rubber_band (tree_view);
3629 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3631 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3633 pspp_sheet_view_update_rubber_band (tree_view);
3635 add_scroll_timeout (tree_view);
3638 /* only check for an initiated drag when a button is pressed */
3639 if (tree_view->priv->pressed_button >= 0
3640 && !tree_view->priv->rubber_band_status)
3641 pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3643 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3647 pspp_sheet_view_find_offset (tree_view, new_y, &node);
3649 tree_view->priv->event_last_x = event->x;
3650 tree_view->priv->event_last_y = event->y;
3652 prelight_or_select (tree_view, node, event->x, event->y);
3658 pspp_sheet_view_motion (GtkWidget *widget,
3659 GdkEventMotion *event)
3661 PsppSheetView *tree_view;
3663 tree_view = (PsppSheetView *) widget;
3665 /* Resizing a column */
3666 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3667 return pspp_sheet_view_motion_resize_column (widget, event);
3670 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3671 return pspp_sheet_view_motion_drag_column (widget, event);
3673 /* Sanity check it */
3674 if (event->window == tree_view->priv->bin_window)
3675 return pspp_sheet_view_motion_bin_window (widget, event);
3680 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3681 * the tree is empty.
3684 invalidate_empty_focus (PsppSheetView *tree_view)
3688 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3693 gdk_drawable_get_size (tree_view->priv->bin_window, &area.width, &area.height);
3694 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3697 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3701 draw_empty_focus (PsppSheetView *tree_view, GdkRectangle *clip_area)
3703 GtkWidget *widget = GTK_WIDGET (tree_view);
3706 if (!gtk_widget_has_focus (widget))
3709 gdk_drawable_get_size (tree_view->priv->bin_window, &w, &h);
3715 gtk_paint_focus (gtk_widget_get_style (widget),
3716 tree_view->priv->bin_window,
3717 gtk_widget_get_state (widget),
3725 pspp_sheet_view_draw_grid_lines (PsppSheetView *tree_view,
3726 GdkEventExpose *event,
3727 gint n_visible_columns,
3731 GList *list = tree_view->priv->columns;
3736 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3737 && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3740 gdk_drawable_get_size (event->window, NULL, &height);
3742 /* Only draw the lines for visible rows and columns */
3743 for (list = tree_view->priv->columns; list; list = list->next, i++)
3745 PsppSheetViewColumn *column = list->data;
3747 /* We don't want a line for the last column */
3748 if (i == n_visible_columns - 1)
3751 if (! column->visible)
3754 current_x += column->width;
3756 if (current_x - 1 >= event->area.x
3757 && current_x - 1 < event->area.x + event->area.width)
3758 gdk_draw_line (event->window,
3759 tree_view->priv->grid_line_gc[GTK_WIDGET(tree_view)->state],
3760 current_x - 1, min_y,
3761 current_x - 1, max_y - min_y);
3765 /* Warning: Very scary function.
3766 * Modify at your own risk
3768 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3769 * FIXME: It's not...
3772 pspp_sheet_view_bin_expose (GtkWidget *widget,
3773 GdkEventExpose *event)
3775 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3780 int drag_highlight = -1;
3783 gint y_offset, cell_offset;
3785 GdkRectangle background_area;
3786 GdkRectangle cell_area;
3788 gint bin_window_width;
3789 gint bin_window_height;
3790 GtkTreePath *cursor_path;
3791 GtkTreePath *drag_dest_path;
3792 GList *first_column, *last_column;
3793 gint vertical_separator;
3794 gint horizontal_separator;
3795 gint focus_line_width;
3796 gboolean allow_rules;
3797 gboolean has_special_cell;
3799 gint n_visible_columns;
3800 gint grid_line_width;
3801 gboolean row_ending_details;
3802 gboolean draw_vgrid_lines, draw_hgrid_lines;
3805 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3807 gtk_widget_style_get (widget,
3808 "horizontal-separator", &horizontal_separator,
3809 "vertical-separator", &vertical_separator,
3810 "allow-rules", &allow_rules,
3811 "focus-line-width", &focus_line_width,
3812 "row-ending-details", &row_ending_details,
3815 if (tree_view->priv->row_count == 0)
3817 draw_empty_focus (tree_view, &event->area);
3821 /* clip event->area to the visible area */
3822 if (event->area.height < 0)
3825 validate_visible_area (tree_view);
3827 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, event->area.y);
3831 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3832 gdk_drawable_get_size (tree_view->priv->bin_window,
3833 &bin_window_width, &bin_window_height);
3835 if (tree_view->priv->height < bin_window_height)
3837 gtk_paint_flat_box (widget->style,
3844 0, tree_view->priv->height,
3846 bin_window_height - tree_view->priv->height);
3852 /* find the path for the node */
3853 path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3854 gtk_tree_model_get_iter (tree_view->priv->model,
3857 gtk_tree_path_free (path);
3860 drag_dest_path = NULL;
3862 if (tree_view->priv->cursor)
3863 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3866 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3868 if (tree_view->priv->drag_dest_row)
3869 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3872 _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3876 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3877 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3879 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
3880 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3882 if (draw_vgrid_lines || draw_hgrid_lines)
3883 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
3885 n_visible_columns = 0;
3886 for (list = tree_view->priv->columns; list; list = list->next)
3888 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
3890 n_visible_columns ++;
3893 /* Find the last column */
3894 for (last_column = g_list_last (tree_view->priv->columns);
3895 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
3896 last_column = last_column->prev)
3900 for (first_column = g_list_first (tree_view->priv->columns);
3901 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
3902 first_column = first_column->next)
3905 /* Actually process the expose event. To do this, we want to
3906 * start at the first node of the event, and walk the tree in
3907 * order, drawing each successive node.
3914 gboolean is_first = FALSE;
3915 gboolean is_last = FALSE;
3916 gboolean done = FALSE;
3919 max_height = ROW_HEIGHT (tree_view);
3923 background_area.y = y_offset + event->area.y;
3924 background_area.height = max_height;
3925 max_y = background_area.y + max_height;
3929 if (node == tree_view->priv->prelight_node)
3930 flags |= GTK_CELL_RENDERER_PRELIT;
3932 selected = pspp_sheet_view_node_is_selected (tree_view, node);
3936 if (tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
3938 /* we *need* to set cell data on all cells before the call
3939 * to _has_special_cell, else _has_special_cell() does not
3940 * return a correct value.
3942 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
3944 list = (rtl ? list->prev : list->next))
3946 PsppSheetViewColumn *column = list->data;
3947 pspp_sheet_view_column_cell_set_cell_data (column,
3948 tree_view->priv->model,
3952 has_special_cell = pspp_sheet_view_has_special_cell (tree_view);
3955 has_special_cell = tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
3957 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
3959 list = (rtl ? list->prev : list->next))
3961 PsppSheetViewColumn *column = list->data;
3962 const gchar *detail = NULL;
3963 gboolean selected_column;
3966 if (!column->visible)
3969 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
3970 selected_column = column->selected && column->selectable;
3972 selected_column = TRUE;
3974 if (cell_offset > event->area.x + event->area.width ||
3975 cell_offset + column->width < event->area.x)
3977 cell_offset += column->width;
3981 if (selected && selected_column)
3982 flags |= GTK_CELL_RENDERER_SELECTED;
3984 flags &= ~GTK_CELL_RENDERER_SELECTED;
3986 if (column->show_sort_indicator)
3987 flags |= GTK_CELL_RENDERER_SORTED;
3989 flags &= ~GTK_CELL_RENDERER_SORTED;
3992 flags |= GTK_CELL_RENDERER_FOCUSED;
3994 flags &= ~GTK_CELL_RENDERER_FOCUSED;
3996 background_area.x = cell_offset;
3997 background_area.width = column->width;
3999 cell_area = background_area;
4000 cell_area.y += vertical_separator / 2;
4001 cell_area.x += horizontal_separator / 2;
4002 cell_area.height -= vertical_separator;
4003 cell_area.width -= horizontal_separator;
4005 if (draw_vgrid_lines)
4007 if (list == first_column)
4009 cell_area.width -= grid_line_width / 2;
4011 else if (list == last_column)
4013 cell_area.x += grid_line_width / 2;
4014 cell_area.width -= grid_line_width / 2;
4018 cell_area.x += grid_line_width / 2;
4019 cell_area.width -= grid_line_width;
4023 if (draw_hgrid_lines)
4025 cell_area.y += grid_line_width / 2;
4026 cell_area.height -= grid_line_width;
4029 if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4031 cell_offset += column->width;
4035 pspp_sheet_view_column_cell_set_cell_data (column,
4036 tree_view->priv->model,
4039 /* Select the detail for drawing the cell. relevant
4040 * factors are parity, sortedness, and whether to
4043 if (allow_rules && tree_view->priv->has_rules)
4045 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4046 n_visible_columns >= 3)
4049 detail = "cell_odd_ruled_sorted";
4051 detail = "cell_even_ruled_sorted";
4056 detail = "cell_odd_ruled";
4058 detail = "cell_even_ruled";
4063 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4064 n_visible_columns >= 3)
4067 detail = "cell_odd_sorted";
4069 detail = "cell_even_sorted";
4074 detail = "cell_odd";
4076 detail = "cell_even";
4082 if (widget->state == GTK_STATE_INSENSITIVE)
4083 state = GTK_STATE_INSENSITIVE;
4084 else if (flags & GTK_CELL_RENDERER_SELECTED)
4085 state = GTK_STATE_SELECTED;
4087 state = GTK_STATE_NORMAL;
4089 /* Draw background */
4090 if (row_ending_details)
4092 char new_detail[128];
4094 is_first = (rtl ? !list->next : !list->prev);
4095 is_last = (rtl ? !list->prev : !list->next);
4097 /* (I don't like the snprintfs either, but couldn't find a
4100 if (is_first && is_last)
4101 g_snprintf (new_detail, 127, "%s", detail);
4103 g_snprintf (new_detail, 127, "%s_start", detail);
4105 g_snprintf (new_detail, 127, "%s_end", detail);
4107 g_snprintf (new_detail, 128, "%s_middle", detail);
4109 gtk_paint_flat_box (widget->style,
4118 background_area.width,
4119 background_area.height);
4123 gtk_paint_flat_box (widget->style,
4132 background_area.width,
4133 background_area.height);
4136 if (draw_hgrid_lines)
4138 if (background_area.y > 0)
4139 gdk_draw_line (event->window,
4140 tree_view->priv->grid_line_gc[widget->state],
4141 background_area.x, background_area.y,
4142 background_area.x + background_area.width,
4145 if (y_offset + max_height >= event->area.height)
4146 gdk_draw_line (event->window,
4147 tree_view->priv->grid_line_gc[widget->state],
4148 background_area.x, background_area.y + max_height,
4149 background_area.x + background_area.width,
4150 background_area.y + max_height);
4153 _pspp_sheet_view_column_cell_render (column,
4160 if (node == cursor && has_special_cell &&
4161 ((column == tree_view->priv->focus_column &&
4162 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4163 gtk_widget_has_focus (widget)) ||
4164 (column == tree_view->priv->edited_column)))
4166 _pspp_sheet_view_column_cell_draw_focus (column,
4174 cell_offset += column->width;
4177 if (cell_offset < event->area.x)
4179 gtk_paint_flat_box (widget->style,
4188 event->area.x - cell_offset,
4189 background_area.height);
4192 if (node == drag_highlight)
4194 /* Draw indicator for the drop
4196 gint highlight_y = -1;
4200 switch (tree_view->priv->drag_dest_pos)
4202 case PSPP_SHEET_VIEW_DROP_BEFORE:
4203 highlight_y = background_area.y - 1;
4204 if (highlight_y < 0)
4208 case PSPP_SHEET_VIEW_DROP_AFTER:
4209 highlight_y = background_area.y + background_area.height - 1;
4212 case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE:
4213 case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER:
4214 _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node);
4218 gdk_drawable_get_size (tree_view->priv->bin_window,
4221 if (row_ending_details)
4222 gtk_paint_focus (widget->style,
4223 tree_view->priv->bin_window,
4224 gtk_widget_get_state (widget),
4228 ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4229 : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4230 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4231 - focus_line_width / 2,
4232 width, ROW_HEIGHT (tree_view)
4233 - focus_line_width + 1);
4235 gtk_paint_focus (widget->style,
4236 tree_view->priv->bin_window,
4237 gtk_widget_get_state (widget),
4240 "treeview-drop-indicator",
4241 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4242 - focus_line_width / 2,
4243 width, ROW_HEIGHT (tree_view)
4244 - focus_line_width + 1);
4248 if (highlight_y >= 0)
4250 gdk_draw_line (event->window,
4251 widget->style->fg_gc[gtk_widget_get_state (widget)],
4254 rtl ? 0 : bin_window_width,
4259 /* draw the big row-spanning focus rectangle, if needed */
4260 if (!has_special_cell && node == cursor &&
4261 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4262 gtk_widget_has_focus (widget))
4264 gint tmp_y, tmp_height;
4266 GtkStateType focus_rect_state;
4269 flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
4270 (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
4271 (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE :
4274 gdk_drawable_get_size (tree_view->priv->bin_window,
4277 if (draw_hgrid_lines)
4279 tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node) + grid_line_width / 2;
4280 tmp_height = ROW_HEIGHT (tree_view) - grid_line_width;
4284 tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node);
4285 tmp_height = ROW_HEIGHT (tree_view);
4288 if (row_ending_details)
4289 gtk_paint_focus (widget->style,
4290 tree_view->priv->bin_window,
4295 ? (is_last ? "treeview" : "treeview-left" )
4296 : (is_last ? "treeview-right" : "treeview-middle" )),
4300 gtk_paint_focus (widget->style,
4301 tree_view->priv->bin_window,
4310 y_offset += max_height;
4314 node = pspp_sheet_view_node_next (tree_view, node);
4317 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
4321 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
4328 while (y_offset < event->area.height);
4331 pspp_sheet_view_draw_grid_lines (tree_view, event, n_visible_columns,
4334 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4336 GdkRectangle *rectangles;
4339 gdk_region_get_rectangles (event->region,
4343 while (n_rectangles--)
4344 pspp_sheet_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
4346 g_free (rectangles);
4350 gtk_tree_path_free (cursor_path);
4353 gtk_tree_path_free (drag_dest_path);
4359 pspp_sheet_view_expose (GtkWidget *widget,
4360 GdkEventExpose *event)
4362 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4364 if (event->window == tree_view->priv->bin_window)
4369 retval = pspp_sheet_view_bin_expose (widget, event);
4371 /* We can't just chain up to Container::expose as it will try to send the
4372 * event to the headers, so we handle propagating it to our children
4373 * (eg. widgets being edited) ourselves.
4375 tmp_list = tree_view->priv->children;
4378 PsppSheetViewChild *child = tmp_list->data;
4379 tmp_list = tmp_list->next;
4381 gtk_container_propagate_expose (GTK_CONTAINER (tree_view), child->widget, event);
4387 else if (event->window == tree_view->priv->header_window)
4389 gint n_visible_columns;
4392 gtk_paint_flat_box (widget->style,
4402 event->area.height);
4404 for (list = tree_view->priv->columns; list != NULL; list = list->next)
4406 PsppSheetViewColumn *column = list->data;
4408 if (column == tree_view->priv->drag_column || !column->visible)
4411 if (span_intersects (column->allocation.x, column->allocation.width,
4412 event->area.x, event->area.width)
4413 && column->button != NULL)
4414 gtk_container_propagate_expose (GTK_CONTAINER (tree_view),
4415 column->button, event);
4418 n_visible_columns = 0;
4419 for (list = tree_view->priv->columns; list; list = list->next)
4421 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4423 n_visible_columns ++;
4425 pspp_sheet_view_draw_grid_lines (tree_view,
4429 event->area.height);
4431 else if (event->window == tree_view->priv->drag_window)
4433 gtk_container_propagate_expose (GTK_CONTAINER (tree_view),
4434 tree_view->priv->drag_column->button,
4448 /* returns 0x1 when no column has been found -- yes it's hackish */
4449 static PsppSheetViewColumn *
4450 pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
4451 PsppSheetViewColumn *column,
4454 PsppSheetViewColumn *left_column = NULL;
4455 PsppSheetViewColumn *cur_column = NULL;
4458 if (!column->reorderable)
4459 return (PsppSheetViewColumn *)0x1;
4461 switch (drop_position)
4464 /* find first column where we can drop */
4465 tmp_list = tree_view->priv->columns;
4466 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4467 return (PsppSheetViewColumn *)0x1;
4471 g_assert (tmp_list);
4473 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4474 tmp_list = tmp_list->next;
4476 if (left_column && left_column->visible == FALSE)
4479 if (!tree_view->priv->column_drop_func)
4482 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4484 left_column = cur_column;
4491 if (!tree_view->priv->column_drop_func)
4494 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4497 return (PsppSheetViewColumn *)0x1;
4501 /* find first column after column where we can drop */
4502 tmp_list = tree_view->priv->columns;
4504 for (; tmp_list; tmp_list = tmp_list->next)
4505 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4508 if (!tmp_list || !tmp_list->next)
4509 return (PsppSheetViewColumn *)0x1;
4511 tmp_list = tmp_list->next;
4512 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4513 tmp_list = tmp_list->next;
4517 g_assert (tmp_list);
4519 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4520 tmp_list = tmp_list->next;
4522 if (left_column && left_column->visible == FALSE)
4524 left_column = cur_column;
4526 tmp_list = tmp_list->next;
4530 if (!tree_view->priv->column_drop_func)
4533 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4535 left_column = cur_column;
4542 if (!tree_view->priv->column_drop_func)
4545 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4548 return (PsppSheetViewColumn *)0x1;
4552 /* find first column before column where we can drop */
4553 tmp_list = tree_view->priv->columns;
4555 for (; tmp_list; tmp_list = tmp_list->next)
4556 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4559 if (!tmp_list || !tmp_list->prev)
4560 return (PsppSheetViewColumn *)0x1;
4562 tmp_list = tmp_list->prev;
4563 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4564 tmp_list = tmp_list->prev;
4568 g_assert (tmp_list);
4570 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4572 if (left_column && !left_column->visible)
4574 /*if (!tmp_list->prev)
4575 return (PsppSheetViewColumn *)0x1;
4578 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4579 tmp_list = tmp_list->prev->prev;
4582 cur_column = left_column;
4584 tmp_list = tmp_list->prev;
4588 if (!tree_view->priv->column_drop_func)
4591 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4594 cur_column = left_column;
4595 tmp_list = tmp_list->prev;
4598 if (!tree_view->priv->column_drop_func)
4601 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4604 return (PsppSheetViewColumn *)0x1;
4608 /* same as DROP_HOME case, but doing it backwards */
4609 tmp_list = g_list_last (tree_view->priv->columns);
4612 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4613 return (PsppSheetViewColumn *)0x1;
4617 g_assert (tmp_list);
4619 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4621 if (left_column && !left_column->visible)
4623 cur_column = left_column;
4624 tmp_list = tmp_list->prev;
4627 if (!tree_view->priv->column_drop_func)
4630 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4633 cur_column = left_column;
4634 tmp_list = tmp_list->prev;
4637 if (!tree_view->priv->column_drop_func)
4640 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4643 return (PsppSheetViewColumn *)0x1;
4647 return (PsppSheetViewColumn *)0x1;
4651 pspp_sheet_view_key_press (GtkWidget *widget,
4654 PsppSheetView *tree_view = (PsppSheetView *) widget;
4656 if (tree_view->priv->rubber_band_status)
4658 if (event->keyval == GDK_Escape)
4659 pspp_sheet_view_stop_rubber_band (tree_view);
4664 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4666 if (event->keyval == GDK_Escape)
4668 tree_view->priv->cur_reorder = NULL;
4669 pspp_sheet_view_button_release_drag_column (widget, NULL);
4674 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4676 GList *focus_column;
4679 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4681 for (focus_column = tree_view->priv->columns;
4683 focus_column = focus_column->next)
4685 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4687 if (column->button && gtk_widget_has_focus (column->button))
4692 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4693 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4694 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4696 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4698 if (!column->resizable)
4700 gtk_widget_error_bell (widget);
4704 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4705 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4707 gint old_width = column->resized_width;
4709 column->resized_width = MAX (column->resized_width,
4711 column->resized_width -= 2;
4712 if (column->resized_width < 0)
4713 column->resized_width = 0;
4715 if (column->min_width == -1)
4716 column->resized_width = MAX (column->button_request,
4717 column->resized_width);
4719 column->resized_width = MAX (column->min_width,
4720 column->resized_width);
4722 if (column->max_width != -1)
4723 column->resized_width = MIN (column->resized_width,
4726 column->use_resized_width = TRUE;
4728 if (column->resized_width != old_width)
4729 gtk_widget_queue_resize (widget);
4731 gtk_widget_error_bell (widget);
4733 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4734 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4736 gint old_width = column->resized_width;
4738 column->resized_width = MAX (column->resized_width,
4740 column->resized_width += 2;
4742 if (column->max_width != -1)
4743 column->resized_width = MIN (column->resized_width,
4746 column->use_resized_width = TRUE;
4748 if (column->resized_width != old_width)
4749 gtk_widget_queue_resize (widget);
4751 gtk_widget_error_bell (widget);
4758 (event->state & GDK_MOD1_MASK) &&
4759 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4760 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4761 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4762 || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4764 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4766 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4767 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4769 PsppSheetViewColumn *col;
4770 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
4771 if (col != (PsppSheetViewColumn *)0x1)
4772 pspp_sheet_view_move_column_after (tree_view, column, col);
4774 gtk_widget_error_bell (widget);
4776 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4777 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4779 PsppSheetViewColumn *col;
4780 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4781 if (col != (PsppSheetViewColumn *)0x1)
4782 pspp_sheet_view_move_column_after (tree_view, column, col);
4784 gtk_widget_error_bell (widget);
4786 else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4788 PsppSheetViewColumn *col;
4789 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4790 if (col != (PsppSheetViewColumn *)0x1)
4791 pspp_sheet_view_move_column_after (tree_view, column, col);
4793 gtk_widget_error_bell (widget);
4795 else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4797 PsppSheetViewColumn *col;
4798 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4799 if (col != (PsppSheetViewColumn *)0x1)
4800 pspp_sheet_view_move_column_after (tree_view, column, col);
4802 gtk_widget_error_bell (widget);
4809 /* Chain up to the parent class. It handles the keybindings. */
4810 if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4813 if (tree_view->priv->search_entry_avoid_unhandled_binding)
4815 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4819 /* We pass the event to the search_entry. If its text changes, then we start
4820 * the typeahead find capabilities. */
4821 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4822 && tree_view->priv->enable_search
4823 && !tree_view->priv->search_custom_entry_set)
4825 GdkEvent *new_event;
4827 const char *new_text;
4830 gboolean text_modified;
4831 gulong popup_menu_id;
4833 pspp_sheet_view_ensure_interactive_directory (tree_view);
4835 /* Make a copy of the current text */
4836 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4837 new_event = gdk_event_copy ((GdkEvent *) event);
4838 g_object_unref (((GdkEventKey *) new_event)->window);
4839 ((GdkEventKey *) new_event)->window = g_object_ref (tree_view->priv->search_window->window);
4840 gtk_widget_realize (tree_view->priv->search_window);
4842 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
4843 "popup-menu", G_CALLBACK (gtk_true),
4846 /* Move the entry off screen */
4847 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4848 gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4849 gdk_screen_get_width (screen) + 1,
4850 gdk_screen_get_height (screen) + 1);
4851 gtk_widget_show (tree_view->priv->search_window);
4853 /* Send the event to the window. If the preedit_changed signal is emitted
4854 * during this event, we will set priv->imcontext_changed */
4855 tree_view->priv->imcontext_changed = FALSE;
4856 retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4857 gdk_event_free (new_event);
4858 gtk_widget_hide (tree_view->priv->search_window);
4860 g_signal_handler_disconnect (tree_view->priv->search_entry,
4863 /* We check to make sure that the entry tried to handle the text, and that
4864 * the text has changed.
4866 new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
4867 text_modified = strcmp (old_text, new_text) != 0;
4869 if (tree_view->priv->imcontext_changed || /* we're in a preedit */
4870 (retval && text_modified)) /* ...or the text was modified */
4872 if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
4874 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
4879 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
4889 pspp_sheet_view_key_release (GtkWidget *widget,
4892 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4894 if (tree_view->priv->rubber_band_status)
4897 return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
4900 /* FIXME Is this function necessary? Can I get an enter_notify event
4901 * w/o either an expose event or a mouse motion event?
4904 pspp_sheet_view_enter_notify (GtkWidget *widget,
4905 GdkEventCrossing *event)
4907 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4911 /* Sanity check it */
4912 if (event->window != tree_view->priv->bin_window)
4915 if (tree_view->priv->row_count == 0)
4918 if (event->mode == GDK_CROSSING_GRAB ||
4919 event->mode == GDK_CROSSING_GTK_GRAB ||
4920 event->mode == GDK_CROSSING_GTK_UNGRAB ||
4921 event->mode == GDK_CROSSING_STATE_CHANGED)
4924 /* find the node internally */
4925 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
4928 pspp_sheet_view_find_offset (tree_view, new_y, &node);
4930 tree_view->priv->event_last_x = event->x;
4931 tree_view->priv->event_last_y = event->y;
4933 prelight_or_select (tree_view, node, event->x, event->y);
4939 pspp_sheet_view_leave_notify (GtkWidget *widget,
4940 GdkEventCrossing *event)
4942 PsppSheetView *tree_view;
4944 if (event->mode == GDK_CROSSING_GRAB)
4947 tree_view = PSPP_SHEET_VIEW (widget);
4949 if (tree_view->priv->prelight_node >= 0)
4950 _pspp_sheet_view_queue_draw_node (tree_view,
4951 tree_view->priv->prelight_node,
4954 tree_view->priv->event_last_x = -10000;
4955 tree_view->priv->event_last_y = -10000;
4957 prelight_or_select (tree_view,
4959 -1000, -1000); /* coords not possibly over an arrow */
4966 pspp_sheet_view_focus_out (GtkWidget *widget,
4967 GdkEventFocus *event)
4969 PsppSheetView *tree_view;
4971 tree_view = PSPP_SHEET_VIEW (widget);
4973 gtk_widget_queue_draw (widget);
4975 /* destroy interactive search dialog */
4976 if (tree_view->priv->search_window)
4977 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
4983 /* Incremental Reflow
4987 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
4992 y = pspp_sheet_view_node_find_offset (tree_view, node)
4993 - tree_view->priv->vadjustment->value
4994 + TREE_VIEW_HEADER_HEIGHT (tree_view);
4996 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
4998 GTK_WIDGET (tree_view)->allocation.width,
4999 tree_view->priv->fixed_height);
5003 node_is_visible (PsppSheetView *tree_view,
5009 y = pspp_sheet_view_node_find_offset (tree_view, node);
5010 height = ROW_HEIGHT (tree_view);
5012 if (y >= tree_view->priv->vadjustment->value &&
5013 y + height <= (tree_view->priv->vadjustment->value
5014 + tree_view->priv->vadjustment->page_size))
5020 /* Returns the row height. */
5022 validate_row (PsppSheetView *tree_view,
5027 PsppSheetViewColumn *column;
5028 GList *list, *first_column, *last_column;
5030 gint horizontal_separator;
5031 gint vertical_separator;
5032 gint focus_line_width;
5033 gboolean draw_vgrid_lines, draw_hgrid_lines;
5035 gint grid_line_width;
5036 gboolean wide_separators;
5037 gint separator_height;
5039 gtk_widget_style_get (GTK_WIDGET (tree_view),
5040 "focus-padding", &focus_pad,
5041 "focus-line-width", &focus_line_width,
5042 "horizontal-separator", &horizontal_separator,
5043 "vertical-separator", &vertical_separator,
5044 "grid-line-width", &grid_line_width,
5045 "wide-separators", &wide_separators,
5046 "separator-height", &separator_height,
5050 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5051 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5053 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5054 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5056 for (last_column = g_list_last (tree_view->priv->columns);
5057 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5058 last_column = last_column->prev)
5061 for (first_column = g_list_first (tree_view->priv->columns);
5062 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5063 first_column = first_column->next)
5066 for (list = tree_view->priv->columns; list; list = list->next)
5071 column = list->data;
5073 if (! column->visible)
5076 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5077 pspp_sheet_view_column_cell_get_size (column,
5079 &tmp_width, &tmp_height);
5081 tmp_height += vertical_separator;
5082 height = MAX (height, tmp_height);
5084 tmp_width = tmp_width + horizontal_separator;
5086 if (draw_vgrid_lines)
5088 if (list->data == first_column || list->data == last_column)
5089 tmp_width += grid_line_width / 2.0;
5091 tmp_width += grid_line_width;
5094 if (tmp_width > column->requested_width)
5095 column->requested_width = tmp_width;
5098 if (draw_hgrid_lines)
5099 height += grid_line_width;
5101 tree_view->priv->post_validation_flag = TRUE;
5107 validate_visible_area (PsppSheetView *tree_view)
5109 GtkTreePath *path = NULL;
5110 GtkTreePath *above_path = NULL;
5113 gboolean size_changed = FALSE;
5115 gint area_above = 0;
5116 gint area_below = 0;
5118 if (tree_view->priv->row_count == 0)
5121 if (tree_view->priv->scroll_to_path == NULL)
5124 total_height = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5126 if (total_height == 0)
5129 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5132 /* we are going to scroll, and will update dy */
5133 _pspp_sheet_view_find_node (tree_view, path, &node);
5134 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5136 if (tree_view->priv->scroll_to_use_align)
5138 gint height = ROW_HEIGHT (tree_view);
5139 area_above = (total_height - height) *
5140 tree_view->priv->scroll_to_row_align;
5141 area_below = total_height - area_above - height;
5142 area_above = MAX (area_above, 0);
5143 area_below = MAX (area_below, 0);
5148 * 1) row not visible
5152 gint height = ROW_HEIGHT (tree_view);
5154 dy = pspp_sheet_view_node_find_offset (tree_view, node);
5156 if (dy >= tree_view->priv->vadjustment->value &&
5157 dy + height <= (tree_view->priv->vadjustment->value
5158 + tree_view->priv->vadjustment->page_size))
5160 /* row visible: keep the row at the same position */
5161 area_above = dy - tree_view->priv->vadjustment->value;
5162 area_below = (tree_view->priv->vadjustment->value +
5163 tree_view->priv->vadjustment->page_size)
5168 /* row not visible */
5170 && dy + height <= tree_view->priv->vadjustment->page_size)
5172 /* row at the beginning -- fixed */
5174 area_below = tree_view->priv->vadjustment->page_size
5175 - area_above - height;
5177 else if (dy >= (tree_view->priv->vadjustment->upper -
5178 tree_view->priv->vadjustment->page_size))
5180 /* row at the end -- fixed */
5181 area_above = dy - (tree_view->priv->vadjustment->upper -
5182 tree_view->priv->vadjustment->page_size);
5183 area_below = tree_view->priv->vadjustment->page_size -
5184 area_above - height;
5188 area_above = tree_view->priv->vadjustment->page_size - height;
5194 /* row somewhere in the middle, bring it to the top
5198 area_below = total_height - height;
5204 /* the scroll to isn't valid; ignore it.
5207 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5208 tree_view->priv->scroll_to_path = NULL;
5212 above_path = gtk_tree_path_copy (path);
5214 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5215 * backwards is much slower then forward, as there is no iter_prev function.
5216 * We go forwards first in case we run out of tree. Then we go backwards to
5219 while (node >= 0 && area_below > 0)
5221 gboolean done = FALSE;
5224 node = pspp_sheet_view_node_next (tree_view, node);
5227 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5229 gtk_tree_path_next (path);
5232 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5242 area_below -= ROW_HEIGHT (tree_view);
5244 gtk_tree_path_free (path);
5246 /* If we ran out of tree, and have extra area_below left, we need to add it
5249 area_above += area_below;
5251 _pspp_sheet_view_find_node (tree_view, above_path, &node);
5253 /* We walk backwards */
5254 while (area_above > 0)
5256 node = pspp_sheet_view_node_prev (tree_view, node);
5258 /* Always find the new path in the tree. We cannot just assume
5259 * a gtk_tree_path_prev() is enough here, as there might be children
5260 * in between this node and the previous sibling node. If this
5261 * appears to be a performance hotspot in profiles, we can look into
5262 * intrigate logic for keeping path, node and iter in sync like
5263 * we do for forward walks. (Which will be hard because of the lacking
5270 gtk_tree_path_free (above_path);
5271 above_path = _pspp_sheet_view_find_path (tree_view, node);
5273 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5275 area_above -= ROW_HEIGHT (tree_view);
5278 /* set the dy here to scroll to the path,
5279 * and sync the top row accordingly
5281 pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5282 pspp_sheet_view_top_row_to_dy (tree_view);
5284 /* update width/height and queue a resize */
5287 GtkRequisition requisition;
5289 /* We temporarily guess a size, under the assumption that it will be the
5290 * same when we get our next size_allocate. If we don't do this, we'll be
5291 * in an inconsistent state if we call top_row_to_dy. */
5293 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5294 tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width);
5295 tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height);
5296 gtk_adjustment_changed (tree_view->priv->hadjustment);
5297 gtk_adjustment_changed (tree_view->priv->vadjustment);
5298 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5301 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5302 tree_view->priv->scroll_to_path = NULL;
5305 gtk_tree_path_free (above_path);
5307 if (tree_view->priv->scroll_to_column)
5309 tree_view->priv->scroll_to_column = NULL;
5311 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5315 initialize_fixed_height_mode (PsppSheetView *tree_view)
5317 if (!tree_view->priv->row_count)
5320 if (tree_view->priv->fixed_height_set)
5323 if (tree_view->priv->fixed_height < 0)
5330 path = _pspp_sheet_view_find_path (tree_view, node);
5331 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5333 tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5335 gtk_tree_path_free (path);
5337 g_object_notify (G_OBJECT (tree_view), "fixed-height");
5341 /* Our strategy for finding nodes to validate is a little convoluted. We find
5342 * the left-most uninvalidated node. We then try walking right, validating
5343 * nodes. Once we find a valid node, we repeat the previous process of finding
5344 * the first invalid node.
5348 validate_rows_handler (PsppSheetView *tree_view)
5350 initialize_fixed_height_mode (tree_view);
5351 if (tree_view->priv->validate_rows_timer)
5353 g_source_remove (tree_view->priv->validate_rows_timer);
5354 tree_view->priv->validate_rows_timer = 0;
5361 do_presize_handler (PsppSheetView *tree_view)
5363 GtkRequisition requisition;
5365 validate_visible_area (tree_view);
5366 tree_view->priv->presize_handler_timer = 0;
5368 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5371 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5373 tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width);
5374 tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height);
5375 gtk_adjustment_changed (tree_view->priv->hadjustment);
5376 gtk_adjustment_changed (tree_view->priv->vadjustment);
5377 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5383 presize_handler_callback (gpointer data)
5385 do_presize_handler (PSPP_SHEET_VIEW (data));
5391 install_presize_handler (PsppSheetView *tree_view)
5393 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5396 if (! tree_view->priv->presize_handler_timer)
5398 tree_view->priv->presize_handler_timer =
5399 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5401 if (! tree_view->priv->validate_rows_timer)
5403 tree_view->priv->validate_rows_timer =
5404 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5409 scroll_sync_handler (PsppSheetView *tree_view)
5411 if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size)
5412 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5413 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5414 pspp_sheet_view_top_row_to_dy (tree_view);
5416 pspp_sheet_view_dy_to_top_row (tree_view);
5418 tree_view->priv->scroll_sync_timer = 0;
5424 install_scroll_sync_handler (PsppSheetView *tree_view)
5426 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5429 if (!tree_view->priv->scroll_sync_timer)
5431 tree_view->priv->scroll_sync_timer =
5432 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5437 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5441 gtk_tree_row_reference_free (tree_view->priv->top_row);
5445 tree_view->priv->top_row = NULL;
5446 tree_view->priv->top_row_dy = 0;
5450 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5451 tree_view->priv->top_row_dy = offset;
5455 /* Always call this iff dy is in the visible range. If the tree is empty, then
5456 * it's set to be NULL, and top_row_dy is 0;
5459 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5465 if (tree_view->priv->row_count == 0)
5467 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5471 offset = pspp_sheet_view_find_offset (tree_view,
5472 tree_view->priv->dy,
5477 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5481 path = _pspp_sheet_view_find_path (tree_view, node);
5482 pspp_sheet_view_set_top_row (tree_view, path, offset);
5483 gtk_tree_path_free (path);
5489 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5495 /* Avoid recursive calls */
5496 if (tree_view->priv->in_top_row_to_dy)
5499 if (tree_view->priv->top_row)
5500 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5507 _pspp_sheet_view_find_node (tree_view, path, &node);
5510 gtk_tree_path_free (path);
5514 /* keep dy and set new toprow */
5515 gtk_tree_row_reference_free (tree_view->priv->top_row);
5516 tree_view->priv->top_row = NULL;
5517 tree_view->priv->top_row_dy = 0;
5518 /* DO NOT install the idle handler */
5519 pspp_sheet_view_dy_to_top_row (tree_view);
5523 if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5525 /* new top row -- do NOT install the idle handler */
5526 pspp_sheet_view_dy_to_top_row (tree_view);
5530 new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5531 new_dy += tree_view->priv->top_row_dy;
5533 if (new_dy + tree_view->priv->vadjustment->page_size > tree_view->priv->height)
5534 new_dy = tree_view->priv->height - tree_view->priv->vadjustment->page_size;
5536 new_dy = MAX (0, new_dy);
5538 tree_view->priv->in_top_row_to_dy = TRUE;
5539 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5540 tree_view->priv->in_top_row_to_dy = FALSE;
5545 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5547 install_presize_handler (tree_view);
5553 set_source_row (GdkDragContext *context,
5554 GtkTreeModel *model,
5555 GtkTreePath *source_row)
5557 g_object_set_data_full (G_OBJECT (context),
5558 "gtk-tree-view-source-row",
5559 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5560 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5564 get_source_row (GdkDragContext *context)
5566 GtkTreeRowReference *ref =
5567 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5570 return gtk_tree_row_reference_get_path (ref);
5577 GtkTreeRowReference *dest_row;
5578 guint path_down_mode : 1;
5579 guint empty_view_drop : 1;
5580 guint drop_append_mode : 1;
5585 dest_row_free (gpointer data)
5587 DestRow *dr = (DestRow *)data;
5589 gtk_tree_row_reference_free (dr->dest_row);
5590 g_slice_free (DestRow, dr);
5594 set_dest_row (GdkDragContext *context,
5595 GtkTreeModel *model,
5596 GtkTreePath *dest_row,
5597 gboolean path_down_mode,
5598 gboolean empty_view_drop,
5599 gboolean drop_append_mode)
5605 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5610 dr = g_slice_new (DestRow);
5612 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5613 dr->path_down_mode = path_down_mode != FALSE;
5614 dr->empty_view_drop = empty_view_drop != FALSE;
5615 dr->drop_append_mode = drop_append_mode != FALSE;
5617 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5618 dr, (GDestroyNotify) dest_row_free);
5622 get_dest_row (GdkDragContext *context,
5623 gboolean *path_down_mode)
5626 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5630 GtkTreePath *path = NULL;
5633 *path_down_mode = dr->path_down_mode;
5636 path = gtk_tree_row_reference_get_path (dr->dest_row);
5637 else if (dr->empty_view_drop)
5638 path = gtk_tree_path_new_from_indices (0, -1);
5642 if (path && dr->drop_append_mode)
5643 gtk_tree_path_next (path);
5651 /* Get/set whether drag_motion requested the drag data and
5652 * drag_data_received should thus not actually insert the data,
5653 * since the data doesn't result from a drop.
5656 set_status_pending (GdkDragContext *context,
5657 GdkDragAction suggested_action)
5659 g_object_set_data (G_OBJECT (context),
5660 "gtk-tree-view-status-pending",
5661 GINT_TO_POINTER (suggested_action));
5664 static GdkDragAction
5665 get_status_pending (GdkDragContext *context)
5667 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5668 "gtk-tree-view-status-pending"));
5671 static TreeViewDragInfo*
5672 get_info (PsppSheetView *tree_view)
5674 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5678 destroy_info (TreeViewDragInfo *di)
5680 g_slice_free (TreeViewDragInfo, di);
5683 static TreeViewDragInfo*
5684 ensure_info (PsppSheetView *tree_view)
5686 TreeViewDragInfo *di;
5688 di = get_info (tree_view);
5692 di = g_slice_new0 (TreeViewDragInfo);
5694 g_object_set_data_full (G_OBJECT (tree_view),
5695 "gtk-tree-view-drag-info",
5697 (GDestroyNotify) destroy_info);
5704 remove_info (PsppSheetView *tree_view)
5706 g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5711 drag_scan_timeout (gpointer data)
5713 PsppSheetView *tree_view;
5715 GdkModifierType state;
5716 GtkTreePath *path = NULL;
5717 PsppSheetViewColumn *column = NULL;
5718 GdkRectangle visible_rect;
5720 GDK_THREADS_ENTER ();
5722 tree_view = PSPP_SHEET_VIEW (data);
5724 gdk_window_get_pointer (tree_view->priv->bin_window,
5727 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5729 /* See if we are near the edge. */
5730 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5731 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5732 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5733 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5735 pspp_sheet_view_get_path_at_pos (tree_view,
5736 tree_view->priv->bin_window,
5745 pspp_sheet_view_scroll_to_cell (tree_view,
5751 gtk_tree_path_free (path);
5755 GDK_THREADS_LEAVE ();
5762 add_scroll_timeout (PsppSheetView *tree_view)
5764 if (tree_view->priv->scroll_timeout == 0)
5766 tree_view->priv->scroll_timeout =
5767 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5772 remove_scroll_timeout (PsppSheetView *tree_view)
5774 if (tree_view->priv->scroll_timeout != 0)
5776 g_source_remove (tree_view->priv->scroll_timeout);
5777 tree_view->priv->scroll_timeout = 0;
5782 check_model_dnd (GtkTreeModel *model,
5783 GType required_iface,
5784 const gchar *signal)
5786 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5788 g_warning ("You must override the default '%s' handler "
5789 "on PsppSheetView when using models that don't support "
5790 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5791 "is to connect to '%s' and call "
5792 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5793 "the default handler from running. Look at the source code "
5794 "for the default handler in gtktreeview.c to get an idea what "
5795 "your handler should do. (gtktreeview.c is in the GTK source "
5796 "code.) If you're using GTK from a language other than C, "
5797 "there may be a more natural way to override default handlers, e.g. via derivation.",
5798 signal, g_type_name (required_iface), signal);
5806 scroll_row_timeout (gpointer data)
5808 PsppSheetView *tree_view = data;
5810 pspp_sheet_view_horizontal_autoscroll (tree_view);
5811 pspp_sheet_view_vertical_autoscroll (tree_view);
5813 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5814 pspp_sheet_view_update_rubber_band (tree_view);
5819 /* Returns TRUE if event should not be propagated to parent widgets */
5821 set_destination_row (PsppSheetView *tree_view,
5822 GdkDragContext *context,
5823 /* coordinates relative to the widget */
5826 GdkDragAction *suggested_action,
5829 GtkTreePath *path = NULL;
5830 PsppSheetViewDropPosition pos;
5831 PsppSheetViewDropPosition old_pos;
5832 TreeViewDragInfo *di;
5834 GtkTreePath *old_dest_path = NULL;
5835 gboolean can_drop = FALSE;
5837 *suggested_action = 0;
5840 widget = GTK_WIDGET (tree_view);
5842 di = get_info (tree_view);
5844 if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5846 /* someone unset us as a drag dest, note that if
5847 * we return FALSE drag_leave isn't called
5850 pspp_sheet_view_set_drag_dest_row (tree_view,
5852 PSPP_SHEET_VIEW_DROP_BEFORE);
5854 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5856 return FALSE; /* no longer a drop site */
5859 *target = gtk_drag_dest_find_target (widget, context,
5860 gtk_drag_dest_get_target_list (widget));
5861 if (*target == GDK_NONE)
5866 if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
5872 GtkTreeModel *model;
5874 /* the row got dropped on empty space, let's setup a special case
5878 gtk_tree_path_free (path);
5880 model = pspp_sheet_view_get_model (tree_view);
5882 n_children = gtk_tree_model_iter_n_children (model, NULL);
5885 pos = PSPP_SHEET_VIEW_DROP_AFTER;
5886 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
5890 pos = PSPP_SHEET_VIEW_DROP_BEFORE;
5891 path = gtk_tree_path_new_from_indices (0, -1);
5901 /* If we left the current row's "open" zone, unset the timeout for
5904 pspp_sheet_view_get_drag_dest_row (tree_view,
5909 gtk_tree_path_free (old_dest_path);
5911 if (TRUE /* FIXME if the location droppable predicate */)
5919 GtkWidget *source_widget;
5921 *suggested_action = context->suggested_action;
5922 source_widget = gtk_drag_get_source_widget (context);
5924 if (source_widget == widget)
5926 /* Default to MOVE, unless the user has
5927 * pressed ctrl or shift to affect available actions
5929 if ((context->actions & GDK_ACTION_MOVE) != 0)
5930 *suggested_action = GDK_ACTION_MOVE;
5933 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5938 /* can't drop here */
5939 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5941 PSPP_SHEET_VIEW_DROP_BEFORE);
5945 gtk_tree_path_free (path);
5951 get_logical_dest_row (PsppSheetView *tree_view,
5952 gboolean *path_down_mode,
5953 gboolean *drop_append_mode)
5955 /* adjust path to point to the row the drop goes in front of */
5956 GtkTreePath *path = NULL;
5957 PsppSheetViewDropPosition pos;
5959 g_return_val_if_fail (path_down_mode != NULL, NULL);
5960 g_return_val_if_fail (drop_append_mode != NULL, NULL);
5962 *path_down_mode = FALSE;
5963 *drop_append_mode = 0;
5965 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
5970 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
5972 else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
5973 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
5974 *path_down_mode = TRUE;
5978 GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
5980 g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
5982 if (!gtk_tree_model_get_iter (model, &iter, path) ||
5983 !gtk_tree_model_iter_next (model, &iter))
5984 *drop_append_mode = 1;
5987 *drop_append_mode = 0;
5988 gtk_tree_path_next (path);
5996 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
5997 GdkEventMotion *event)
5999 GtkWidget *widget = GTK_WIDGET (tree_view);
6000 GdkDragContext *context;
6001 TreeViewDragInfo *di;
6002 GtkTreePath *path = NULL;
6004 gint cell_x, cell_y;
6005 GtkTreeModel *model;
6006 gboolean retval = FALSE;
6008 di = get_info (tree_view);
6010 if (di == NULL || !di->source_set)
6013 if (tree_view->priv->pressed_button < 0)
6016 if (!gtk_drag_check_threshold (widget,
6017 tree_view->priv->press_start_x,
6018 tree_view->priv->press_start_y,
6019 event->x, event->y))
6022 model = pspp_sheet_view_get_model (tree_view);
6027 button = tree_view->priv->pressed_button;
6028 tree_view->priv->pressed_button = -1;
6030 pspp_sheet_view_get_path_at_pos (tree_view,
6031 tree_view->priv->press_start_x,
6032 tree_view->priv->press_start_y,
6041 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6042 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6046 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6049 /* Now we can begin the drag */
6053 context = gtk_drag_begin (widget,
6054 gtk_drag_source_get_target_list (widget),
6059 set_source_row (context, model, path);
6063 gtk_tree_path_free (path);
6070 pspp_sheet_view_drag_begin (GtkWidget *widget,
6071 GdkDragContext *context)
6073 PsppSheetView *tree_view;
6074 GtkTreePath *path = NULL;
6075 gint cell_x, cell_y;
6077 TreeViewDragInfo *di;
6079 tree_view = PSPP_SHEET_VIEW (widget);
6081 /* if the user uses a custom DND source impl, we don't set the icon here */
6082 di = get_info (tree_view);
6084 if (di == NULL || !di->source_set)
6087 pspp_sheet_view_get_path_at_pos (tree_view,
6088 tree_view->priv->press_start_x,
6089 tree_view->priv->press_start_y,
6095 g_return_if_fail (path != NULL);
6097 row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6100 gtk_drag_set_icon_pixmap (context,
6101 gdk_drawable_get_colormap (row_pix),
6104 /* the + 1 is for the black border in the icon */
6105 tree_view->priv->press_start_x + 1,
6108 g_object_unref (row_pix);
6109 gtk_tree_path_free (path);
6113 pspp_sheet_view_drag_end (GtkWidget *widget,
6114 GdkDragContext *context)
6119 /* Default signal implementations for the drag signals */
6121 pspp_sheet_view_drag_data_get (GtkWidget *widget,
6122 GdkDragContext *context,
6123 GtkSelectionData *selection_data,
6127 PsppSheetView *tree_view;
6128 GtkTreeModel *model;
6129 TreeViewDragInfo *di;
6130 GtkTreePath *source_row;
6132 tree_view = PSPP_SHEET_VIEW (widget);
6134 model = pspp_sheet_view_get_model (tree_view);
6139 di = get_info (PSPP_SHEET_VIEW (widget));
6144 source_row = get_source_row (context);
6146 if (source_row == NULL)
6149 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6150 * any model; for DragSource models there are some other targets
6154 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6155 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6160 /* If drag_data_get does nothing, try providing row data. */
6161 if (selection_data->target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6163 gtk_tree_set_row_drag_data (selection_data,
6169 gtk_tree_path_free (source_row);
6174 pspp_sheet_view_drag_data_delete (GtkWidget *widget,
6175 GdkDragContext *context)
6177 TreeViewDragInfo *di;
6178 GtkTreeModel *model;
6179 PsppSheetView *tree_view;
6180 GtkTreePath *source_row;
6182 tree_view = PSPP_SHEET_VIEW (widget);
6183 model = pspp_sheet_view_get_model (tree_view);
6185 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6188 di = get_info (tree_view);
6193 source_row = get_source_row (context);
6195 if (source_row == NULL)
6198 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6201 gtk_tree_path_free (source_row);
6203 set_source_row (context, NULL, NULL);
6207 pspp_sheet_view_drag_leave (GtkWidget *widget,
6208 GdkDragContext *context,
6211 /* unset any highlight row */
6212 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6214 PSPP_SHEET_VIEW_DROP_BEFORE);
6216 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6221 pspp_sheet_view_drag_motion (GtkWidget *widget,
6222 GdkDragContext *context,
6223 /* coordinates relative to the widget */
6229 GtkTreePath *path = NULL;
6230 PsppSheetViewDropPosition pos;
6231 PsppSheetView *tree_view;
6232 GdkDragAction suggested_action = 0;
6235 tree_view = PSPP_SHEET_VIEW (widget);
6237 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6240 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6242 /* we only know this *after* set_desination_row */
6243 empty = tree_view->priv->empty_view_drop;
6245 if (path == NULL && !empty)
6247 /* Can't drop here. */
6248 gdk_drag_status (context, 0, time);
6252 if (tree_view->priv->open_dest_timeout == 0 &&
6253 (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6254 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6260 add_scroll_timeout (tree_view);
6263 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6265 /* Request data so we can use the source row when
6266 * determining whether to accept the drop
6268 set_status_pending (context, suggested_action);
6269 gtk_drag_get_data (widget, context, target, time);
6273 set_status_pending (context, 0);
6274 gdk_drag_status (context, suggested_action, time);
6279 gtk_tree_path_free (path);
6286 pspp_sheet_view_drag_drop (GtkWidget *widget,
6287 GdkDragContext *context,
6288 /* coordinates relative to the widget */
6293 PsppSheetView *tree_view;
6295 GdkDragAction suggested_action = 0;
6296 GdkAtom target = GDK_NONE;
6297 TreeViewDragInfo *di;
6298 GtkTreeModel *model;
6299 gboolean path_down_mode;
6300 gboolean drop_append_mode;
6302 tree_view = PSPP_SHEET_VIEW (widget);
6304 model = pspp_sheet_view_get_model (tree_view);
6306 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6308 di = get_info (tree_view);
6313 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6316 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6319 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6321 if (target != GDK_NONE && path != NULL)
6323 /* in case a motion had requested drag data, change things so we
6324 * treat drag data receives as a drop.
6326 set_status_pending (context, 0);
6327 set_dest_row (context, model, path,
6328 path_down_mode, tree_view->priv->empty_view_drop,
6333 gtk_tree_path_free (path);
6335 /* Unset this thing */
6336 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6338 PSPP_SHEET_VIEW_DROP_BEFORE);
6340 if (target != GDK_NONE)
6342 gtk_drag_get_data (widget, context, target, time);
6350 pspp_sheet_view_drag_data_received (GtkWidget *widget,
6351 GdkDragContext *context,
6352 /* coordinates relative to the widget */
6355 GtkSelectionData *selection_data,
6360 TreeViewDragInfo *di;
6361 gboolean accepted = FALSE;
6362 GtkTreeModel *model;
6363 PsppSheetView *tree_view;
6364 GtkTreePath *dest_row;
6365 GdkDragAction suggested_action;
6366 gboolean path_down_mode;
6367 gboolean drop_append_mode;
6369 tree_view = PSPP_SHEET_VIEW (widget);
6371 model = pspp_sheet_view_get_model (tree_view);
6373 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6376 di = get_info (tree_view);
6381 suggested_action = get_status_pending (context);
6383 if (suggested_action)
6385 /* We are getting this data due to a request in drag_motion,
6386 * rather than due to a request in drag_drop, so we are just
6387 * supposed to call drag_status, not actually paste in the
6390 path = get_logical_dest_row (tree_view, &path_down_mode,
6394 suggested_action = 0;
6395 else if (path_down_mode)
6396 gtk_tree_path_down (path);
6398 if (suggested_action)
6400 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6406 path_down_mode = FALSE;
6407 gtk_tree_path_up (path);
6409 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6412 suggested_action = 0;
6415 suggested_action = 0;
6419 gdk_drag_status (context, suggested_action, time);
6422 gtk_tree_path_free (path);
6424 /* If you can't drop, remove user drop indicator until the next motion */
6425 if (suggested_action == 0)
6426 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6428 PSPP_SHEET_VIEW_DROP_BEFORE);
6433 dest_row = get_dest_row (context, &path_down_mode);
6435 if (dest_row == NULL)
6438 if (selection_data->length >= 0)
6442 gtk_tree_path_down (dest_row);
6443 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6444 dest_row, selection_data))
6445 gtk_tree_path_up (dest_row);
6449 if (selection_data->length >= 0)
6451 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6457 gtk_drag_finish (context,
6459 (context->action == GDK_ACTION_MOVE),
6462 if (gtk_tree_path_get_depth (dest_row) == 1
6463 && gtk_tree_path_get_indices (dest_row)[0] == 0)
6465 /* special special case drag to "0", scroll to first item */
6466 if (!tree_view->priv->scroll_to_path)
6467 pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6470 gtk_tree_path_free (dest_row);
6473 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6478 /* GtkContainer Methods
6483 pspp_sheet_view_remove (GtkContainer *container,
6486 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6487 PsppSheetViewChild *child = NULL;
6490 tmp_list = tree_view->priv->children;
6493 child = tmp_list->data;
6494 if (child->widget == widget)
6496 gtk_widget_unparent (widget);
6498 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6499 g_list_free_1 (tmp_list);
6500 g_slice_free (PsppSheetViewChild, child);
6504 tmp_list = tmp_list->next;
6507 tmp_list = tree_view->priv->columns;
6511 PsppSheetViewColumn *column;
6512 column = tmp_list->data;
6513 if (column->button == widget)
6515 gtk_widget_unparent (widget);
6518 tmp_list = tmp_list->next;
6523 pspp_sheet_view_forall (GtkContainer *container,
6524 gboolean include_internals,
6525 GtkCallback callback,
6526 gpointer callback_data)
6528 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6529 PsppSheetViewChild *child = NULL;
6530 PsppSheetViewColumn *column;
6533 tmp_list = tree_view->priv->children;
6536 child = tmp_list->data;
6537 tmp_list = tmp_list->next;
6539 (* callback) (child->widget, callback_data);
6541 if (include_internals == FALSE)
6544 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6546 column = tmp_list->data;
6549 (* callback) (column->button, callback_data);
6553 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6554 * cells. If so we draw one big row-spanning focus rectangle.
6557 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6561 if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6562 return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6564 for (list = tree_view->priv->columns; list; list = list->next)
6566 if (!((PsppSheetViewColumn *)list->data)->visible)
6568 if (_pspp_sheet_view_column_count_special_cells (list->data))
6576 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6577 PsppSheetViewColumn *focus_column,
6578 gboolean clamp_column_visible)
6580 g_return_if_fail (focus_column != NULL);
6582 tree_view->priv->focus_column = focus_column;
6583 if (!focus_column->button)
6585 pspp_sheet_view_column_set_need_button (focus_column, TRUE);
6586 g_return_if_fail (focus_column->button != NULL);
6589 if (GTK_CONTAINER (tree_view)->focus_child != focus_column->button)
6590 gtk_widget_grab_focus (focus_column->button);
6592 if (clamp_column_visible)
6593 pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6596 /* Returns TRUE if the focus is within the headers, after the focus operation is
6600 pspp_sheet_view_header_focus (PsppSheetView *tree_view,
6601 GtkDirectionType dir,
6602 gboolean clamp_column_visible)
6604 GtkWidget *focus_child;
6605 PsppSheetViewColumn *focus_column;
6606 GList *last_column, *first_column;
6610 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6613 focus_child = GTK_CONTAINER (tree_view)->focus_child;
6615 first_column = tree_view->priv->columns;
6616 while (first_column)
6618 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6620 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6622 first_column = first_column->next;
6625 /* No headers are visible, or are focusable. We can't focus in or out.
6627 if (first_column == NULL)
6630 last_column = g_list_last (tree_view->priv->columns);
6633 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6635 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6637 last_column = last_column->prev;
6641 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6645 case GTK_DIR_TAB_BACKWARD:
6646 case GTK_DIR_TAB_FORWARD:
6649 if (focus_child == NULL)
6651 if (tree_view->priv->focus_column != NULL &&
6652 pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6653 focus_column = tree_view->priv->focus_column;
6655 focus_column = first_column->data;
6656 pspp_sheet_view_focus_column (tree_view, focus_column,
6657 clamp_column_visible);
6664 if (focus_child == NULL)
6666 if (tree_view->priv->focus_column != NULL)
6667 focus_column = tree_view->priv->focus_column;
6668 else if (dir == GTK_DIR_LEFT)
6669 focus_column = last_column->data;
6671 focus_column = first_column->data;
6672 pspp_sheet_view_focus_column (tree_view, focus_column,
6673 clamp_column_visible);
6677 if (gtk_widget_child_focus (focus_child, dir))
6679 /* The focus moves inside the button. */
6680 /* This is probably a great example of bad UI */
6681 if (clamp_column_visible)
6682 pspp_sheet_view_clamp_column_visible (tree_view,
6683 tree_view->priv->focus_column,
6688 /* We need to move the focus among the row of buttons. */
6689 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6690 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6693 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6694 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6696 gtk_widget_error_bell (GTK_WIDGET (tree_view));
6702 PsppSheetViewColumn *column;
6704 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6705 tmp_list = tmp_list->next;
6707 tmp_list = tmp_list->prev;
6709 if (tmp_list == NULL)
6711 g_warning ("Internal button not found");
6714 column = tmp_list->data;
6715 if (column->visible &&
6716 pspp_sheet_view_column_can_focus (column))
6718 pspp_sheet_view_column_set_need_button (column, TRUE);
6721 pspp_sheet_view_focus_column (tree_view, column,
6722 clamp_column_visible);
6730 g_assert_not_reached ();
6737 /* This function returns in 'path' the first focusable path, if the given path
6738 * is already focusable, it's the returned one.
6742 search_first_focusable_path (PsppSheetView *tree_view,
6744 gboolean search_forward,
6747 /* XXX this function is trivial given that the sheetview doesn't support
6751 if (!path || !*path)
6754 _pspp_sheet_view_find_node (tree_view, *path, &node);
6762 return (*path != NULL);
6766 pspp_sheet_view_focus (GtkWidget *widget,
6767 GtkDirectionType direction)
6769 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6770 GtkContainer *container = GTK_CONTAINER (widget);
6771 GtkWidget *focus_child;
6773 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6776 focus_child = container->focus_child;
6778 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6779 /* Case 1. Headers currently have focus. */
6786 pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6788 case GTK_DIR_TAB_BACKWARD:
6791 case GTK_DIR_TAB_FORWARD:
6793 gtk_widget_grab_focus (widget);
6796 g_assert_not_reached ();
6801 /* Case 2. We don't have focus at all. */
6802 if (!gtk_widget_has_focus (widget))
6804 if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6805 gtk_widget_grab_focus (widget);
6809 /* Case 3. We have focus already. */
6810 if (direction == GTK_DIR_TAB_BACKWARD)
6811 return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6812 else if (direction == GTK_DIR_TAB_FORWARD)
6815 /* Other directions caught by the keybindings */
6816 gtk_widget_grab_focus (widget);
6821 pspp_sheet_view_grab_focus (GtkWidget *widget)
6823 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6825 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6829 pspp_sheet_view_style_set (GtkWidget *widget,
6830 GtkStyle *previous_style)
6832 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6834 PsppSheetViewColumn *column;
6836 if (gtk_widget_get_realized (widget))
6839 PsppSheetViewPrivate *priv = PSPP_SHEET_VIEW (widget)->priv;
6841 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
6842 gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
6843 gtk_style_set_background (widget->style, tree_view->priv->header_window, GTK_STATE_NORMAL);
6844 for (i = 0; i < 5 ; ++i)
6846 g_object_unref (priv->grid_line_gc[i]);
6847 priv->grid_line_gc[i] = gdk_gc_new (widget->window);
6848 gdk_gc_copy (priv->grid_line_gc[i], widget->style->text_aa_gc[i]);
6851 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6854 gtk_widget_style_get (widget,
6855 "expander-size", &tree_view->priv->expander_size,
6857 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6859 for (list = tree_view->priv->columns; list; list = list->next)
6861 column = list->data;
6862 _pspp_sheet_view_column_cell_set_dirty (column);
6865 tree_view->priv->fixed_height = -1;
6867 /* Invalidate cached button style. */
6868 if (tree_view->priv->button_style)
6870 g_object_unref (tree_view->priv->button_style);
6871 tree_view->priv->button_style = NULL;
6874 gtk_widget_queue_resize (widget);
6879 pspp_sheet_view_set_focus_child (GtkContainer *container,
6882 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6885 for (list = tree_view->priv->columns; list; list = list->next)
6887 if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
6889 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
6894 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
6898 pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
6899 GtkAdjustment *hadj,
6900 GtkAdjustment *vadj)
6902 gboolean need_adjust = FALSE;
6904 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
6907 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
6909 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6911 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
6913 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6915 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
6917 g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
6918 pspp_sheet_view_adjustment_changed,
6920 g_object_unref (tree_view->priv->hadjustment);
6923 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
6925 g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
6926 pspp_sheet_view_adjustment_changed,
6928 g_object_unref (tree_view->priv->vadjustment);
6931 if (tree_view->priv->hadjustment != hadj)
6933 tree_view->priv->hadjustment = hadj;
6934 g_object_ref_sink (tree_view->priv->hadjustment);
6936 g_signal_connect (tree_view->priv->hadjustment, "value-changed",
6937 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6942 if (tree_view->priv->vadjustment != vadj)
6944 tree_view->priv->vadjustment = vadj;
6945 g_object_ref_sink (tree_view->priv->vadjustment);
6947 g_signal_connect (tree_view->priv->vadjustment, "value-changed",
6948 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6954 pspp_sheet_view_adjustment_changed (NULL, tree_view);
6959 pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
6960 GtkMovementStep step,
6963 GdkModifierType state;
6965 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
6966 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
6967 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
6968 step == GTK_MOVEMENT_DISPLAY_LINES ||
6969 step == GTK_MOVEMENT_PAGES ||
6970 step == GTK_MOVEMENT_BUFFER_ENDS, FALSE);
6972 if (tree_view->priv->row_count == 0)
6974 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
6977 pspp_sheet_view_stop_editing (tree_view, FALSE);
6978 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
6979 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
6981 if (gtk_get_current_event_state (&state))
6983 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
6984 tree_view->priv->ctrl_pressed = TRUE;
6985 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
6986 tree_view->priv->shift_pressed = TRUE;
6988 /* else we assume not pressed */
6992 case GTK_MOVEMENT_LOGICAL_POSITIONS:
6993 pspp_sheet_view_move_cursor_tab (tree_view, count);
6995 case GTK_MOVEMENT_VISUAL_POSITIONS:
6996 pspp_sheet_view_move_cursor_left_right (tree_view, count);
6998 case GTK_MOVEMENT_DISPLAY_LINES:
6999 pspp_sheet_view_move_cursor_up_down (tree_view, count);
7001 case GTK_MOVEMENT_PAGES:
7002 pspp_sheet_view_move_cursor_page_up_down (tree_view, count);
7004 case GTK_MOVEMENT_BUFFER_ENDS:
7005 pspp_sheet_view_move_cursor_start_end (tree_view, count);
7008 g_assert_not_reached ();
7011 tree_view->priv->ctrl_pressed = FALSE;
7012 tree_view->priv->shift_pressed = FALSE;
7018 pspp_sheet_view_put (PsppSheetView *tree_view,
7019 GtkWidget *child_widget,
7020 /* in bin_window coordinates */
7026 PsppSheetViewChild *child;
7028 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7029 g_return_if_fail (GTK_IS_WIDGET (child_widget));
7031 child = g_slice_new (PsppSheetViewChild);
7033 child->widget = child_widget;
7036 child->width = width;
7037 child->height = height;
7039 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7041 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7042 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7044 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7048 _pspp_sheet_view_child_move_resize (PsppSheetView *tree_view,
7050 /* in tree coordinates */
7056 PsppSheetViewChild *child = NULL;
7058 GdkRectangle allocation;
7060 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7061 g_return_if_fail (GTK_IS_WIDGET (widget));
7063 for (list = tree_view->priv->children; list; list = list->next)
7065 if (((PsppSheetViewChild *)list->data)->widget == widget)
7074 allocation.x = child->x = x;
7075 allocation.y = child->y = y;
7076 allocation.width = child->width = width;
7077 allocation.height = child->height = height;
7079 if (gtk_widget_get_realized (widget))
7080 gtk_widget_size_allocate (widget, &allocation);
7084 /* TreeModel Callbacks
7088 pspp_sheet_view_row_changed (GtkTreeModel *model,
7093 PsppSheetView *tree_view = (PsppSheetView *)data;
7095 gboolean free_path = FALSE;
7096 GtkTreePath *cursor_path;
7098 g_return_if_fail (path != NULL || iter != NULL);
7100 if (tree_view->priv->cursor != NULL)
7101 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7105 if (tree_view->priv->edited_column &&
7106 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7107 pspp_sheet_view_stop_editing (tree_view, TRUE);
7109 if (cursor_path != NULL)
7110 gtk_tree_path_free (cursor_path);
7114 path = gtk_tree_model_get_path (model, iter);
7117 else if (iter == NULL)
7118 gtk_tree_model_get_iter (model, iter, path);
7120 _pspp_sheet_view_find_node (tree_view,
7126 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7127 pspp_sheet_view_node_queue_redraw (tree_view, node);
7131 gtk_tree_path_free (path);
7135 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7140 PsppSheetView *tree_view = (PsppSheetView *) data;
7143 gint height = tree_view->priv->fixed_height;
7144 gboolean free_path = FALSE;
7145 gboolean node_visible = TRUE;
7147 g_return_if_fail (path != NULL || iter != NULL);
7151 path = gtk_tree_model_get_path (model, iter);
7154 else if (iter == NULL)
7155 gtk_tree_model_get_iter (model, iter, path);
7157 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7159 /* Update all row-references */
7160 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7161 indices = gtk_tree_path_get_indices (path);
7162 tmpnode = indices[0];
7164 range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7168 if (node_visible && node_is_visible (tree_view, tmpnode))
7169 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7171 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7174 install_presize_handler (tree_view);
7176 gtk_tree_path_free (path);
7180 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7184 PsppSheetView *tree_view = (PsppSheetView *)data;
7187 g_return_if_fail (path != NULL);
7189 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7191 _pspp_sheet_view_find_node (tree_view, path, &node);
7196 range_tower_delete (tree_view->priv->selected, node, 1);
7198 /* Ensure we don't have a dangling pointer to a dead node */
7199 ensure_unprelighted (tree_view);
7201 /* Cancel editting if we've started */
7202 pspp_sheet_view_stop_editing (tree_view, TRUE);
7204 if (tree_view->priv->destroy_count_func)
7206 gint child_count = 0;
7207 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7210 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7212 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7214 gtk_tree_row_reference_free (tree_view->priv->top_row);
7215 tree_view->priv->top_row = NULL;
7218 install_scroll_sync_handler (tree_view);
7220 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7223 if (helper_data.changed)
7224 g_signal_emit_by_name (tree_view->priv->selection, "changed");
7229 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7230 GtkTreePath *parent,
7235 PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7238 /* XXX need to adjust selection */
7239 len = gtk_tree_model_iter_n_children (model, iter);
7244 gtk_tree_row_reference_reordered (G_OBJECT (data),
7249 if (gtk_tree_path_get_depth (parent) != 0)
7252 if (tree_view->priv->edited_column)
7253 pspp_sheet_view_stop_editing (tree_view, TRUE);
7255 /* we need to be unprelighted */
7256 ensure_unprelighted (tree_view);
7258 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7260 pspp_sheet_view_dy_to_top_row (tree_view);
7264 /* Internal tree functions
7269 pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view,
7270 PsppSheetViewColumn *column,
7274 PsppSheetViewColumn *tmp_column = NULL;
7285 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7288 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7290 list = (rtl ? list->prev : list->next))
7292 tmp_column = list->data;
7294 if (tmp_column == column)
7297 if (tmp_column->visible)
7298 total_width += tmp_column->width;
7301 if (tmp_column != column)
7303 g_warning (G_STRLOC": passed-in column isn't in the tree");
7312 if (column->visible)
7313 *x2 = total_width + column->width;
7315 *x2 = total_width; /* width of 0 */
7319 /* Make sure the node is visible vertically */
7321 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7324 gint node_dy, height;
7325 GtkTreePath *path = NULL;
7327 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7330 /* just return if the node is visible, avoiding a costly expose */
7331 node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7332 height = ROW_HEIGHT (tree_view);
7333 if (node_dy >= tree_view->priv->vadjustment->value
7334 && node_dy + height <= (tree_view->priv->vadjustment->value
7335 + tree_view->priv->vadjustment->page_size))
7338 path = _pspp_sheet_view_find_path (tree_view, node);
7341 /* We process updates because we want to clear old selected items when we scroll.
7342 * if this is removed, we get a "selection streak" at the bottom. */
7343 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7344 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7345 gtk_tree_path_free (path);
7350 pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
7351 PsppSheetViewColumn *column,
7352 gboolean focus_to_cell)
7359 x = column->allocation.x;
7360 width = column->allocation.width;
7362 if (width > tree_view->priv->hadjustment->page_size)
7364 /* The column is larger than the horizontal page size. If the
7365 * column has cells which can be focussed individually, then we make
7366 * sure the cell which gets focus is fully visible (if even the
7367 * focus cell is bigger than the page size, we make sure the
7368 * left-hand side of the cell is visible).
7370 * If the column does not have those so-called special cells, we
7371 * make sure the left-hand side of the column is visible.
7374 if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7376 GtkTreePath *cursor_path;
7377 GdkRectangle background_area, cell_area, focus_area;
7379 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7381 pspp_sheet_view_get_cell_area (tree_view,
7382 cursor_path, column, &cell_area);
7383 pspp_sheet_view_get_background_area (tree_view,
7384 cursor_path, column,
7387 gtk_tree_path_free (cursor_path);
7389 _pspp_sheet_view_column_get_focus_area (column,
7395 width = focus_area.width;
7397 if (width < tree_view->priv->hadjustment->page_size)
7399 if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) < (x + width))
7400 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7401 x + width - tree_view->priv->hadjustment->page_size);
7402 else if (tree_view->priv->hadjustment->value > x)
7403 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7407 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7409 tree_view->priv->hadjustment->lower,
7410 tree_view->priv->hadjustment->upper
7411 - tree_view->priv->hadjustment->page_size));
7415 if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) < (x + width))
7416 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7417 x + width - tree_view->priv->hadjustment->page_size);
7418 else if (tree_view->priv->hadjustment->value > x)
7419 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7424 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7429 path = gtk_tree_path_new ();
7431 gtk_tree_path_append_index (path, node);
7436 _pspp_sheet_view_find_node (PsppSheetView *tree_view,
7440 gint *indices = gtk_tree_path_get_indices (path);
7441 gint depth = gtk_tree_path_get_depth (path);
7444 if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7450 pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
7453 gboolean add_shifted_binding,
7454 GtkMovementStep step,
7458 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7463 if (add_shifted_binding)
7464 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7469 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7472 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7477 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7484 pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view,
7485 PsppSheetViewColumn *column)
7487 PsppSheetViewColumn *left_column;
7488 PsppSheetViewColumn *cur_column = NULL;
7489 PsppSheetViewColumnReorder *reorder;
7494 /* We want to precalculate the motion list such that we know what column slots
7498 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7500 /* First, identify all possible drop spots */
7502 tmp_list = g_list_last (tree_view->priv->columns);
7504 tmp_list = g_list_first (tree_view->priv->columns);
7508 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7509 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7511 if (cur_column->visible == FALSE)
7514 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7515 if (left_column != column && cur_column != column &&
7516 tree_view->priv->column_drop_func &&
7517 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7519 left_column = cur_column;
7522 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7523 reorder->left_column = left_column;
7524 left_column = reorder->right_column = cur_column;
7526 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7529 /* Add the last one */
7530 if (tree_view->priv->column_drop_func == NULL ||
7531 ((left_column != column) &&
7532 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7534 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7535 reorder->left_column = left_column;
7536 reorder->right_column = NULL;
7537 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7540 /* We quickly check to see if it even makes sense to reorder columns. */
7541 /* If there is nothing that can be moved, then we return */
7543 if (tree_view->priv->column_drag_info == NULL)
7546 /* We know there are always 2 slots possbile, as you can always return column. */
7547 /* If that's all there is, return */
7548 if (tree_view->priv->column_drag_info->next == NULL ||
7549 (tree_view->priv->column_drag_info->next->next == NULL &&
7550 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7551 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7553 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7554 g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7555 g_list_free (tree_view->priv->column_drag_info);
7556 tree_view->priv->column_drag_info = NULL;
7559 /* We fill in the ranges for the columns, now that we've isolated them */
7560 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7562 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7564 reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7566 reorder->left_align = left;
7567 if (tmp_list->next != NULL)
7569 g_assert (tmp_list->next->data);
7570 left = reorder->right_align = (reorder->right_column->allocation.x +
7571 reorder->right_column->allocation.width +
7572 ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7578 gdk_drawable_get_size (tree_view->priv->header_window, &width, NULL);
7579 reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7585 _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view,
7586 PsppSheetViewColumn *column)
7588 GdkEvent *send_event;
7589 GtkAllocation allocation;
7590 gint x, y, width, height;
7591 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7592 GdkDisplay *display = gdk_screen_get_display (screen);
7594 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7595 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7596 g_return_if_fail (column->button);
7598 pspp_sheet_view_set_column_drag_info (tree_view, column);
7600 if (tree_view->priv->column_drag_info == NULL)
7603 if (tree_view->priv->drag_window == NULL)
7605 GdkWindowAttr attributes;
7606 guint attributes_mask;
7608 attributes.window_type = GDK_WINDOW_CHILD;
7609 attributes.wclass = GDK_INPUT_OUTPUT;
7610 attributes.x = column->allocation.x;
7612 attributes.width = column->allocation.width;
7613 attributes.height = column->allocation.height;
7614 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7615 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
7616 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7617 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
7619 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7622 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7625 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7626 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7628 gtk_grab_remove (column->button);
7630 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7631 send_event->crossing.send_event = TRUE;
7632 send_event->crossing.window = g_object_ref (GTK_BUTTON (column->button)->event_window);
7633 send_event->crossing.subwindow = NULL;
7634 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7635 send_event->crossing.time = GDK_CURRENT_TIME;
7637 gtk_propagate_event (column->button, send_event);
7638 gdk_event_free (send_event);
7640 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7641 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7642 send_event->button.send_event = TRUE;
7643 send_event->button.time = GDK_CURRENT_TIME;
7644 send_event->button.x = -1;
7645 send_event->button.y = -1;
7646 send_event->button.axes = NULL;
7647 send_event->button.state = 0;
7648 send_event->button.button = 1;
7649 send_event->button.device = gdk_display_get_core_pointer (display);
7650 send_event->button.x_root = 0;
7651 send_event->button.y_root = 0;
7653 gtk_propagate_event (column->button, send_event);
7654 gdk_event_free (send_event);
7656 /* Kids, don't try this at home */
7657 g_object_ref (column->button);
7658 gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7659 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7660 gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7661 g_object_unref (column->button);
7663 tree_view->priv->drag_column_x = column->allocation.x;
7664 allocation = column->allocation;
7666 gtk_widget_size_allocate (column->button, &allocation);
7667 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7669 tree_view->priv->drag_column = column;
7670 gdk_window_show (tree_view->priv->drag_window);
7672 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7673 gdk_drawable_get_size (tree_view->priv->header_window, &width, &height);
7675 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7676 while (gtk_events_pending ())
7677 gtk_main_iteration ();
7679 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7680 gdk_pointer_grab (tree_view->priv->drag_window,
7682 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7683 NULL, NULL, GDK_CURRENT_TIME);
7684 gdk_keyboard_grab (tree_view->priv->drag_window,
7690 _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view,
7692 const GdkRectangle *clip_rect)
7696 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7700 rect.width = MAX (tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.width);
7702 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7703 rect.height = ROW_HEIGHT (tree_view);
7707 GdkRectangle new_rect;
7709 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7711 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7715 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7720 pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
7722 const GdkRectangle *clip_rect)
7726 _pspp_sheet_view_find_node (tree_view, path, &node);
7729 _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7733 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7736 GtkTreePath *cursor_path;
7738 if ((tree_view->priv->row_count == 0) ||
7739 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7743 if (tree_view->priv->cursor)
7744 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7746 if (cursor_path == NULL)
7748 /* There's no cursor. Move the cursor to the first selected row, if any
7749 * are selected, otherwise to the first row in the sheetview.
7751 GList *selected_rows;
7752 GtkTreeModel *model;
7753 PsppSheetSelection *selection;
7755 selection = pspp_sheet_view_get_selection (tree_view);
7756 selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7760 /* XXX we could avoid doing O(n) work to get this result */
7761 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7762 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7763 g_list_free (selected_rows);
7767 cursor_path = gtk_tree_path_new_first ();
7768 search_first_focusable_path (tree_view, &cursor_path,
7772 gtk_tree_row_reference_free (tree_view->priv->cursor);
7773 tree_view->priv->cursor = NULL;
7777 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7778 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7779 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE);
7781 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
7787 /* Now find a column for the cursor. */
7788 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7790 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7791 gtk_tree_path_free (cursor_path);
7793 if (tree_view->priv->focus_column == NULL)
7796 for (list = tree_view->priv->columns; list; list = list->next)
7798 if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7800 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7801 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7802 pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7812 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7815 gint selection_count;
7816 int cursor_node = -1;
7817 int new_cursor_node = -1;
7818 GtkTreePath *cursor_path = NULL;
7819 gboolean grab_focus = TRUE;
7821 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7825 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7826 /* FIXME: we lost the cursor; should we get the first? */
7829 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7830 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7832 if (cursor_node < 0)
7833 /* FIXME: we lost the cursor; should we get the first? */
7836 selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7838 if (selection_count == 0
7839 && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7840 && !tree_view->priv->ctrl_pressed)
7842 /* Don't move the cursor, but just select the current node */
7843 new_cursor_node = cursor_node;
7848 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7850 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7853 gtk_tree_path_free (cursor_path);
7855 if (new_cursor_node)
7857 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7859 search_first_focusable_path (tree_view, &cursor_path,
7864 gtk_tree_path_free (cursor_path);
7868 * If the list has only one item and multi-selection is set then select
7869 * the row (if not yet selected).
7871 if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7872 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7873 new_cursor_node < 0)
7876 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7878 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7880 if (new_cursor_node < 0
7881 && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
7883 new_cursor_node = cursor_node;
7887 new_cursor_node = -1;
7891 if (new_cursor_node >= 0)
7893 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7894 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE);
7895 gtk_tree_path_free (cursor_path);
7899 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7901 if (!tree_view->priv->shift_pressed)
7903 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
7905 GTK_DIR_UP : GTK_DIR_DOWN))
7907 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
7910 gtk_widget_child_focus (toplevel,
7912 GTK_DIR_TAB_BACKWARD :
7913 GTK_DIR_TAB_FORWARD);
7920 gtk_widget_error_bell (GTK_WIDGET (tree_view));
7925 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7927 return new_cursor_node >= 0;
7931 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
7934 int cursor_node = -1;
7935 GtkTreePath *old_cursor_path = NULL;
7936 GtkTreePath *cursor_path = NULL;
7937 int start_cursor_node = -1;
7940 gint vertical_separator;
7942 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7945 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
7946 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7948 /* This is sorta weird. Focus in should give us a cursor */
7951 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
7952 _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
7954 if (cursor_node < 0)
7956 /* FIXME: we lost the cursor. Should we try to get one? */
7957 gtk_tree_path_free (old_cursor_path);
7961 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7962 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
7963 y += tree_view->priv->cursor_offset;
7964 y += count * (int)tree_view->priv->vadjustment->page_increment;
7965 y = CLAMP (y, (gint)tree_view->priv->vadjustment->lower, (gint)tree_view->priv->vadjustment->upper - vertical_separator);
7967 if (y >= tree_view->priv->height)
7968 y = tree_view->priv->height - 1;
7970 tree_view->priv->cursor_offset =
7971 pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
7973 if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
7975 cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7976 tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
7979 y -= tree_view->priv->cursor_offset;
7980 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7982 start_cursor_node = cursor_node;
7984 if (! search_first_focusable_path (tree_view, &cursor_path,
7988 /* It looks like we reached the end of the view without finding
7989 * a focusable row. We will step backwards to find the last
7992 cursor_node = start_cursor_node;
7993 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7995 search_first_focusable_path (tree_view, &cursor_path,
8004 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8006 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
8009 pspp_sheet_view_scroll_to_point (tree_view, -1, y);
8010 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8011 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8013 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
8014 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8016 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8019 gtk_tree_path_free (old_cursor_path);
8020 gtk_tree_path_free (cursor_path);
8024 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8027 int cursor_node = -1;
8028 GtkTreePath *cursor_path = NULL;
8029 PsppSheetViewColumn *column;
8032 gboolean found_column = FALSE;
8035 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8037 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8040 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8041 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8045 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8046 if (cursor_node < 0)
8048 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8050 gtk_tree_path_free (cursor_path);
8053 gtk_tree_path_free (cursor_path);
8055 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8056 if (tree_view->priv->focus_column)
8058 for (; list; list = (rtl ? list->prev : list->next))
8060 if (list->data == tree_view->priv->focus_column)
8067 gboolean left, right;
8069 column = list->data;
8070 if (column->visible == FALSE || column->row_head)
8073 pspp_sheet_view_column_cell_set_cell_data (column,
8074 tree_view->priv->model,
8079 right = list->prev ? TRUE : FALSE;
8080 left = list->next ? TRUE : FALSE;
8084 left = list->prev ? TRUE : FALSE;
8085 right = list->next ? TRUE : FALSE;
8088 if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8090 tree_view->priv->focus_column = column;
8091 found_column = TRUE;
8096 list = rtl ? list->prev : list->next;
8098 list = rtl ? list->next : list->prev;
8103 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8104 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8105 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8109 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8112 pspp_sheet_view_clamp_column_visible (tree_view,
8113 tree_view->priv->focus_column, TRUE);
8117 try_move_cursor_tab (PsppSheetView *tree_view,
8118 gboolean start_at_focus_column,
8121 PsppSheetViewColumn *column;
8123 int cursor_node = -1;
8124 GtkTreePath *cursor_path = NULL;
8128 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8129 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8133 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8134 if (cursor_node < 0)
8136 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8138 gtk_tree_path_free (cursor_path);
8141 gtk_tree_path_free (cursor_path);
8143 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8144 if (start_at_focus_column)
8147 ? g_list_last (tree_view->priv->columns)
8148 : g_list_first (tree_view->priv->columns));
8149 if (tree_view->priv->focus_column)
8151 for (; list; list = (rtl ? list->prev : list->next))
8153 if (list->data == tree_view->priv->focus_column)
8160 list = (rtl ^ (count == 1)
8161 ? g_list_first (tree_view->priv->columns)
8162 : g_list_last (tree_view->priv->columns));
8167 gboolean left, right;
8169 column = list->data;
8170 if (column->visible == FALSE || !column->tabbable)
8173 pspp_sheet_view_column_cell_set_cell_data (column,
8174 tree_view->priv->model,
8179 right = list->prev ? TRUE : FALSE;
8180 left = list->next ? TRUE : FALSE;
8184 left = list->prev ? TRUE : FALSE;
8185 right = list->next ? TRUE : FALSE;
8188 if (column->tabbable
8189 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8191 tree_view->priv->focus_column = column;
8192 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8193 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8194 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8199 list = rtl ? list->prev : list->next;
8201 list = rtl ? list->next : list->prev;
8208 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8211 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8214 if (!try_move_cursor_tab (tree_view, TRUE, count))
8216 /* Shift+Tab goes backward, but Shift isn't supposed to act as Shift does
8217 for other movement commands, that is, it shouldn't cause the selection
8218 to be extended, so we need to act as though it is off. */
8219 tree_view->priv->shift_pressed = FALSE;
8221 if (pspp_sheet_view_move_cursor_up_down (tree_view, count)
8222 && !try_move_cursor_tab (tree_view, FALSE, count))
8223 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8226 pspp_sheet_view_clamp_column_visible (tree_view,
8227 tree_view->priv->focus_column, TRUE);
8231 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8236 GtkTreePath *old_path;
8238 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8241 g_return_if_fail (tree_view->priv->row_count > 0);
8243 pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8247 /* Now go forward to find the first focusable row. */
8248 path = _pspp_sheet_view_find_path (tree_view, 0);
8249 search_first_focusable_path (tree_view, &path,
8250 TRUE, &cursor_node);
8254 /* Now go backwards to find last focusable row. */
8255 path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8256 search_first_focusable_path (tree_view, &path,
8257 FALSE, &cursor_node);
8263 if (gtk_tree_path_compare (old_path, path))
8265 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE);
8266 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8270 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8274 gtk_tree_path_free (old_path);
8275 gtk_tree_path_free (path);
8279 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8281 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8284 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8285 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8288 pspp_sheet_selection_select_all (tree_view->priv->selection);
8294 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8296 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8299 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8300 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8303 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8309 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8310 gboolean start_editing)
8313 int cursor_node = -1;
8314 GtkTreePath *cursor_path = NULL;
8315 GtkTreeSelectMode mode = 0;
8317 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8320 if (tree_view->priv->cursor)
8321 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8323 if (cursor_path == NULL)
8326 _pspp_sheet_view_find_node (tree_view, cursor_path,
8329 if (cursor_node < 0)
8331 gtk_tree_path_free (cursor_path);
8335 if (!tree_view->priv->shift_pressed && start_editing &&
8336 tree_view->priv->focus_column)
8338 if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8340 gtk_tree_path_free (cursor_path);
8345 if (tree_view->priv->ctrl_pressed)
8346 mode |= GTK_TREE_SELECT_MODE_TOGGLE;
8347 if (tree_view->priv->shift_pressed)
8348 mode |= GTK_TREE_SELECT_MODE_EXTEND;
8350 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8356 /* We bail out if the original (tree, node) don't exist anymore after
8357 * handling the selection-changed callback. We do return TRUE because
8358 * the key press has been handled at this point.
8360 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8362 if (cursor_node != new_node)
8365 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8367 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8368 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8370 if (!tree_view->priv->shift_pressed)
8371 pspp_sheet_view_row_activated (tree_view, cursor_path,
8372 tree_view->priv->focus_column);
8374 gtk_tree_path_free (cursor_path);
8380 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8383 int cursor_node = -1;
8384 GtkTreePath *cursor_path = NULL;
8386 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8390 if (tree_view->priv->cursor)
8391 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8393 if (cursor_path == NULL)
8396 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8397 if (cursor_node < 0)
8399 gtk_tree_path_free (cursor_path);
8403 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8406 GTK_TREE_SELECT_MODE_TOGGLE,
8409 /* We bail out if the original (tree, node) don't exist anymore after
8410 * handling the selection-changed callback. We do return TRUE because
8411 * the key press has been handled at this point.
8413 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8415 if (cursor_node != new_node)
8418 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8420 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8421 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8422 gtk_tree_path_free (cursor_path);
8428 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8430 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8431 tree_view->priv->typeselect_flush_timeout = 0;
8436 /* Cut and paste from gtkwindow.c */
8438 send_focus_change (GtkWidget *widget,
8441 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8443 g_object_ref (widget);
8446 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
8448 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
8450 fevent->focus_change.type = GDK_FOCUS_CHANGE;
8451 fevent->focus_change.window = g_object_ref (widget->window);
8452 fevent->focus_change.in = in;
8454 gtk_widget_event (widget, fevent);
8456 g_object_notify (G_OBJECT (widget), "has-focus");
8458 g_object_unref (widget);
8459 gdk_event_free (fevent);
8463 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8465 GtkWidget *frame, *vbox, *toplevel;
8468 if (tree_view->priv->search_custom_entry_set)
8471 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8472 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8474 if (tree_view->priv->search_window != NULL)
8476 if (GTK_WINDOW (toplevel)->group)
8477 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
8478 GTK_WINDOW (tree_view->priv->search_window));
8479 else if (GTK_WINDOW (tree_view->priv->search_window)->group)
8480 gtk_window_group_remove_window (GTK_WINDOW (tree_view->priv->search_window)->group,
8481 GTK_WINDOW (tree_view->priv->search_window));
8482 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8486 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8487 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8489 if (GTK_WINDOW (toplevel)->group)
8490 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
8491 GTK_WINDOW (tree_view->priv->search_window));
8493 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8494 GDK_WINDOW_TYPE_HINT_UTILITY);
8495 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8496 g_signal_connect (tree_view->priv->search_window, "delete-event",
8497 G_CALLBACK (pspp_sheet_view_search_delete_event),
8499 g_signal_connect (tree_view->priv->search_window, "key-press-event",
8500 G_CALLBACK (pspp_sheet_view_search_key_press_event),
8502 g_signal_connect (tree_view->priv->search_window, "button-press-event",
8503 G_CALLBACK (pspp_sheet_view_search_button_press_event),
8505 g_signal_connect (tree_view->priv->search_window, "scroll-event",
8506 G_CALLBACK (pspp_sheet_view_search_scroll_event),
8509 frame = gtk_frame_new (NULL);
8510 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8511 gtk_widget_show (frame);
8512 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8514 vbox = gtk_vbox_new (FALSE, 0);
8515 gtk_widget_show (vbox);
8516 gtk_container_add (GTK_CONTAINER (frame), vbox);
8517 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8520 tree_view->priv->search_entry = gtk_entry_new ();
8521 gtk_widget_show (tree_view->priv->search_entry);
8522 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8523 G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8525 g_signal_connect (tree_view->priv->search_entry,
8526 "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8528 g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8530 G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8532 gtk_container_add (GTK_CONTAINER (vbox),
8533 tree_view->priv->search_entry);
8535 gtk_widget_realize (tree_view->priv->search_entry);
8538 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8539 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8542 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8543 gboolean keybinding)
8545 /* We only start interactive search if we have focus or the columns
8546 * have focus. If one of our children have focus, we don't want to
8550 gboolean found_focus = FALSE;
8551 GtkWidgetClass *entry_parent_class;
8553 if (!tree_view->priv->enable_search && !keybinding)
8556 if (tree_view->priv->search_custom_entry_set)
8559 if (tree_view->priv->search_window != NULL &&
8560 gtk_widget_get_visible (tree_view->priv->search_window))
8563 for (list = tree_view->priv->columns; list; list = list->next)
8565 PsppSheetViewColumn *column;
8567 column = list->data;
8568 if (! column->visible)
8571 if (column->button && gtk_widget_has_focus (column->button))
8578 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8584 if (tree_view->priv->search_column < 0)
8587 pspp_sheet_view_ensure_interactive_directory (tree_view);
8590 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8593 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8594 gtk_widget_show (tree_view->priv->search_window);
8595 if (tree_view->priv->search_entry_changed_id == 0)
8597 tree_view->priv->search_entry_changed_id =
8598 g_signal_connect (tree_view->priv->search_entry, "changed",
8599 G_CALLBACK (pspp_sheet_view_search_init),
8603 tree_view->priv->typeselect_flush_timeout =
8604 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8605 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8608 /* Grab focus will select all the text. We don't want that to happen, so we
8609 * call the parent instance and bypass the selection change. This is probably
8610 * really non-kosher. */
8611 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8612 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8614 /* send focus-in event */
8615 send_focus_change (tree_view->priv->search_entry, TRUE);
8617 /* search first matching iter */
8618 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8624 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8626 return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8629 /* this function returns the new width of the column being resized given
8630 * the column and x position of the cursor; the x cursor position is passed
8631 * in as a pointer and automagicly corrected if it's beyond min/max limits
8634 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8638 PsppSheetViewColumn *column;
8642 /* first translate the x position from widget->window
8643 * to clist->clist_window
8645 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8646 column = g_list_nth (tree_view->priv->columns, i)->data;
8647 width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8649 /* Clamp down the value */
8650 if (column->min_width == -1)
8651 width = MAX (column->button_request, width);
8653 width = MAX (column->min_width, width);
8654 if (column->max_width != -1)
8655 width = MIN (width, column->max_width);
8657 *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8663 /* FIXME this adjust_allocation is a big cut-and-paste from
8664 * GtkCList, needs to be some "official" way to do this
8674 /* The window to which widget->window is relative */
8675 #define ALLOCATION_WINDOW(widget) \
8676 (!gtk_widget_get_has_window (widget) ? \
8677 (widget)->window : \
8678 gdk_window_get_parent ((widget)->window))
8681 adjust_allocation_recurse (GtkWidget *widget,
8684 ScrollData *scroll_data = data;
8686 /* Need to really size allocate instead of just poking
8687 * into widget->allocation if the widget is not realized.
8688 * FIXME someone figure out why this was.
8690 if (!gtk_widget_get_realized (widget))
8692 if (gtk_widget_get_visible (widget))
8694 GdkRectangle tmp_rectangle = widget->allocation;
8695 tmp_rectangle.x += scroll_data->dx;
8696 tmp_rectangle.y += scroll_data->dy;
8698 gtk_widget_size_allocate (widget, &tmp_rectangle);
8703 if (ALLOCATION_WINDOW (widget) == scroll_data->window)
8705 widget->allocation.x += scroll_data->dx;
8706 widget->allocation.y += scroll_data->dy;
8708 if (GTK_IS_CONTAINER (widget))
8709 gtk_container_forall (GTK_CONTAINER (widget),
8710 adjust_allocation_recurse,
8717 adjust_allocation (GtkWidget *widget,
8721 ScrollData scroll_data;
8723 if (gtk_widget_get_realized (widget))
8724 scroll_data.window = ALLOCATION_WINDOW (widget);
8726 scroll_data.window = NULL;
8728 scroll_data.dx = dx;
8729 scroll_data.dy = dy;
8731 adjust_allocation_recurse (widget, &scroll_data);
8735 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8739 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8740 PsppSheetView *tree_view)
8742 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8747 gdk_window_move (tree_view->priv->bin_window,
8748 - tree_view->priv->hadjustment->value,
8749 TREE_VIEW_HEADER_HEIGHT (tree_view));
8750 gdk_window_move (tree_view->priv->header_window,
8751 - tree_view->priv->hadjustment->value,
8753 dy = tree_view->priv->dy - (int) tree_view->priv->vadjustment->value;
8756 update_prelight (tree_view,
8757 tree_view->priv->event_last_x,
8758 tree_view->priv->event_last_y - dy);
8760 if (tree_view->priv->edited_column &&
8761 GTK_IS_WIDGET (tree_view->priv->edited_column->editable_widget))
8765 PsppSheetViewChild *child = NULL;
8767 widget = GTK_WIDGET (tree_view->priv->edited_column->editable_widget);
8768 adjust_allocation (widget, 0, dy);
8770 for (list = tree_view->priv->children; list; list = list->next)
8772 child = (PsppSheetViewChild *)list->data;
8773 if (child->widget == widget)
8781 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8783 if (tree_view->priv->dy != (int) tree_view->priv->vadjustment->value)
8785 /* update our dy and top_row */
8786 tree_view->priv->dy = (int) tree_view->priv->vadjustment->value;
8788 if (!tree_view->priv->in_top_row_to_dy)
8789 pspp_sheet_view_dy_to_top_row (tree_view);
8792 for (list = tree_view->priv->columns; list; list = list->next)
8794 PsppSheetViewColumn *column = list->data;
8795 GtkAllocation *allocation = &column->allocation;
8797 if (span_intersects (allocation->x, allocation->width,
8798 tree_view->priv->hadjustment->value,
8799 GTK_WIDGET (tree_view)->allocation.width))
8801 pspp_sheet_view_column_set_need_button (column, TRUE);
8802 if (!column->button)
8803 pspp_sheet_view_column_update_button (column);
8815 * pspp_sheet_view_new:
8817 * Creates a new #PsppSheetView widget.
8819 * Return value: A newly created #PsppSheetView widget.
8822 pspp_sheet_view_new (void)
8824 return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
8828 * pspp_sheet_view_new_with_model:
8829 * @model: the model.
8831 * Creates a new #PsppSheetView widget with the model initialized to @model.
8833 * Return value: A newly created #PsppSheetView widget.
8836 pspp_sheet_view_new_with_model (GtkTreeModel *model)
8838 return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
8845 * pspp_sheet_view_get_model:
8846 * @tree_view: a #PsppSheetView
8848 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
8851 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
8854 pspp_sheet_view_get_model (PsppSheetView *tree_view)
8856 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8858 return tree_view->priv->model;
8862 * pspp_sheet_view_set_model:
8863 * @tree_view: A #GtkTreeNode.
8864 * @model: (allow-none): The model.
8866 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
8867 * set, it will remove it before setting the new model. If @model is %NULL,
8868 * then it will unset the old model.
8871 pspp_sheet_view_set_model (PsppSheetView *tree_view,
8872 GtkTreeModel *model)
8874 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8875 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
8877 if (model == tree_view->priv->model)
8880 if (tree_view->priv->scroll_to_path)
8882 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8883 tree_view->priv->scroll_to_path = NULL;
8886 if (tree_view->priv->model)
8888 GList *tmplist = tree_view->priv->columns;
8890 if (tree_view->priv->selected)
8891 range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
8892 pspp_sheet_view_stop_editing (tree_view, TRUE);
8894 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8895 pspp_sheet_view_row_changed,
8897 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8898 pspp_sheet_view_row_inserted,
8900 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8901 pspp_sheet_view_row_deleted,
8903 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8904 pspp_sheet_view_rows_reordered,
8907 for (; tmplist; tmplist = tmplist->next)
8908 _pspp_sheet_view_column_unset_model (tmplist->data,
8909 tree_view->priv->model);
8911 tree_view->priv->prelight_node = -1;
8913 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
8914 tree_view->priv->drag_dest_row = NULL;
8915 gtk_tree_row_reference_free (tree_view->priv->cursor);
8916 tree_view->priv->cursor = NULL;
8917 gtk_tree_row_reference_free (tree_view->priv->anchor);
8918 tree_view->priv->anchor = NULL;
8919 gtk_tree_row_reference_free (tree_view->priv->top_row);
8920 tree_view->priv->top_row = NULL;
8921 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8922 tree_view->priv->scroll_to_path = NULL;
8924 tree_view->priv->scroll_to_column = NULL;
8926 g_object_unref (tree_view->priv->model);
8928 tree_view->priv->search_column = -1;
8929 tree_view->priv->fixed_height = -1;
8930 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
8931 tree_view->priv->last_button_x = -1;
8932 tree_view->priv->last_button_y = -1;
8935 tree_view->priv->model = model;
8937 if (tree_view->priv->model)
8941 if (tree_view->priv->search_column == -1)
8943 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
8945 GType type = gtk_tree_model_get_column_type (model, i);
8947 if (g_value_type_transformable (type, G_TYPE_STRING))
8949 tree_view->priv->search_column = i;
8955 g_object_ref (tree_view->priv->model);
8956 g_signal_connect (tree_view->priv->model,
8958 G_CALLBACK (pspp_sheet_view_row_changed),
8960 g_signal_connect (tree_view->priv->model,
8962 G_CALLBACK (pspp_sheet_view_row_inserted),
8964 g_signal_connect (tree_view->priv->model,
8966 G_CALLBACK (pspp_sheet_view_row_deleted),
8968 g_signal_connect (tree_view->priv->model,
8970 G_CALLBACK (pspp_sheet_view_rows_reordered),
8973 tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
8975 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
8976 install_presize_handler (tree_view);
8979 g_object_notify (G_OBJECT (tree_view), "model");
8981 if (tree_view->priv->selection)
8982 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
8984 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8985 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8989 * pspp_sheet_view_get_selection:
8990 * @tree_view: A #PsppSheetView.
8992 * Gets the #PsppSheetSelection associated with @tree_view.
8994 * Return value: A #PsppSheetSelection object.
8996 PsppSheetSelection *
8997 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
8999 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9001 return tree_view->priv->selection;
9005 * pspp_sheet_view_get_hadjustment:
9006 * @tree_view: A #PsppSheetView
9008 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
9010 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9014 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
9016 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9018 if (tree_view->priv->hadjustment == NULL)
9019 pspp_sheet_view_set_hadjustment (tree_view, NULL);
9021 return tree_view->priv->hadjustment;
9025 * pspp_sheet_view_set_hadjustment:
9026 * @tree_view: A #PsppSheetView
9027 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9029 * Sets the #GtkAdjustment for the current horizontal aspect.
9032 pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view,
9033 GtkAdjustment *adjustment)
9035 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9037 pspp_sheet_view_set_adjustments (tree_view,
9039 tree_view->priv->vadjustment);
9041 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9045 * pspp_sheet_view_get_vadjustment:
9046 * @tree_view: A #PsppSheetView
9048 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9050 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9054 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9056 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9058 if (tree_view->priv->vadjustment == NULL)
9059 pspp_sheet_view_set_vadjustment (tree_view, NULL);
9061 return tree_view->priv->vadjustment;
9065 * pspp_sheet_view_set_vadjustment:
9066 * @tree_view: A #PsppSheetView
9067 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9069 * Sets the #GtkAdjustment for the current vertical aspect.
9072 pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view,
9073 GtkAdjustment *adjustment)
9075 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9077 pspp_sheet_view_set_adjustments (tree_view,
9078 tree_view->priv->hadjustment,
9081 g_object_notify (G_OBJECT (tree_view), "vadjustment");
9084 /* Column and header operations */
9087 * pspp_sheet_view_get_headers_visible:
9088 * @tree_view: A #PsppSheetView.
9090 * Returns %TRUE if the headers on the @tree_view are visible.
9092 * Return value: Whether the headers are visible or not.
9095 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9097 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9099 return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9103 * pspp_sheet_view_set_headers_visible:
9104 * @tree_view: A #PsppSheetView.
9105 * @headers_visible: %TRUE if the headers are visible
9107 * Sets the visibility state of the headers.
9110 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9111 gboolean headers_visible)
9115 PsppSheetViewColumn *column;
9117 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9119 headers_visible = !! headers_visible;
9121 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9124 if (headers_visible)
9125 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9127 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9129 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9131 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9132 if (headers_visible)
9134 gdk_window_move_resize (tree_view->priv->bin_window, x, y + TREE_VIEW_HEADER_HEIGHT (tree_view), tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view));
9136 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9137 pspp_sheet_view_map_buttons (tree_view);
9141 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9143 for (list = tree_view->priv->columns; list; list = list->next)
9145 column = list->data;
9147 gtk_widget_unmap (column->button);
9149 gdk_window_hide (tree_view->priv->header_window);
9153 tree_view->priv->vadjustment->page_size = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
9154 tree_view->priv->vadjustment->page_increment = (GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2;
9155 tree_view->priv->vadjustment->lower = 0;
9156 tree_view->priv->vadjustment->upper = tree_view->priv->height;
9157 gtk_adjustment_changed (tree_view->priv->vadjustment);
9159 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9161 g_object_notify (G_OBJECT (tree_view), "headers-visible");
9165 * pspp_sheet_view_columns_autosize:
9166 * @tree_view: A #PsppSheetView.
9168 * Resizes all columns to their optimal width. Only works after the
9169 * treeview has been realized.
9172 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9174 gboolean dirty = FALSE;
9176 PsppSheetViewColumn *column;
9178 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9180 for (list = tree_view->priv->columns; list; list = list->next)
9182 column = list->data;
9183 _pspp_sheet_view_column_cell_set_dirty (column);
9188 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9192 * pspp_sheet_view_set_headers_clickable:
9193 * @tree_view: A #PsppSheetView.
9194 * @setting: %TRUE if the columns are clickable.
9196 * Allow the column title buttons to be clicked.
9199 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9204 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9206 for (list = tree_view->priv->columns; list; list = list->next)
9207 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9209 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9214 * pspp_sheet_view_get_headers_clickable:
9215 * @tree_view: A #PsppSheetView.
9217 * Returns whether all header columns are clickable.
9219 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9224 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9228 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9230 for (list = tree_view->priv->columns; list; list = list->next)
9231 if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9238 * pspp_sheet_view_set_rules_hint
9239 * @tree_view: a #PsppSheetView
9240 * @setting: %TRUE if the tree requires reading across rows
9242 * This function tells GTK+ that the user interface for your
9243 * application requires users to read across tree rows and associate
9244 * cells with one another. By default, GTK+ will then render the tree
9245 * with alternating row colors. Do <emphasis>not</emphasis> use it
9246 * just because you prefer the appearance of the ruled tree; that's a
9247 * question for the theme. Some themes will draw tree rows in
9248 * alternating colors even when rules are turned off, and users who
9249 * prefer that appearance all the time can choose those themes. You
9250 * should call this function only as a <emphasis>semantic</emphasis>
9251 * hint to the theme engine that your tree makes alternating colors
9252 * useful from a functional standpoint (since it has lots of columns,
9257 pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view,
9260 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9262 setting = setting != FALSE;
9264 if (tree_view->priv->has_rules != setting)
9266 tree_view->priv->has_rules = setting;
9267 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9270 g_object_notify (G_OBJECT (tree_view), "rules-hint");
9274 * pspp_sheet_view_get_rules_hint
9275 * @tree_view: a #PsppSheetView
9277 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9279 * Return value: %TRUE if rules are useful for the user of this tree
9282 pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view)
9284 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9286 return tree_view->priv->has_rules;
9289 /* Public Column functions
9293 * pspp_sheet_view_append_column:
9294 * @tree_view: A #PsppSheetView.
9295 * @column: The #PsppSheetViewColumn to add.
9297 * Appends @column to the list of columns.
9299 * Return value: The number of columns in @tree_view after appending.
9302 pspp_sheet_view_append_column (PsppSheetView *tree_view,
9303 PsppSheetViewColumn *column)
9305 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9306 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9307 g_return_val_if_fail (column->tree_view == NULL, -1);
9309 return pspp_sheet_view_insert_column (tree_view, column, -1);
9314 * pspp_sheet_view_remove_column:
9315 * @tree_view: A #PsppSheetView.
9316 * @column: The #PsppSheetViewColumn to remove.
9318 * Removes @column from @tree_view.
9320 * Return value: The number of columns in @tree_view after removing.
9323 pspp_sheet_view_remove_column (PsppSheetView *tree_view,
9324 PsppSheetViewColumn *column)
9326 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9327 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9328 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9330 if (tree_view->priv->focus_column == column)
9331 tree_view->priv->focus_column = NULL;
9333 if (tree_view->priv->edited_column == column)
9335 pspp_sheet_view_stop_editing (tree_view, TRUE);
9337 /* no need to, but just to be sure ... */
9338 tree_view->priv->edited_column = NULL;
9341 _pspp_sheet_view_column_unset_tree_view (column);
9343 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9344 tree_view->priv->n_columns--;
9346 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9350 _pspp_sheet_view_column_unrealize_button (column);
9351 for (list = tree_view->priv->columns; list; list = list->next)
9353 PsppSheetViewColumn *tmp_column;
9355 tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9356 if (tmp_column->visible)
9357 _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9360 if (tree_view->priv->n_columns == 0 &&
9361 pspp_sheet_view_get_headers_visible (tree_view) &&
9362 tree_view->priv->header_window)
9363 gdk_window_hide (tree_view->priv->header_window);
9365 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9368 g_object_unref (column);
9369 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9371 return tree_view->priv->n_columns;
9375 * pspp_sheet_view_insert_column:
9376 * @tree_view: A #PsppSheetView.
9377 * @column: The #PsppSheetViewColumn to be inserted.
9378 * @position: The position to insert @column in.
9380 * This inserts the @column into the @tree_view at @position. If @position is
9381 * -1, then the column is inserted at the end.
9383 * Return value: The number of columns in @tree_view after insertion.
9386 pspp_sheet_view_insert_column (PsppSheetView *tree_view,
9387 PsppSheetViewColumn *column,
9390 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9391 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9392 g_return_val_if_fail (column->tree_view == NULL, -1);
9394 g_object_ref_sink (column);
9396 if (tree_view->priv->n_columns == 0 &&
9397 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9398 pspp_sheet_view_get_headers_visible (tree_view))
9400 gdk_window_show (tree_view->priv->header_window);
9403 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9405 tree_view->priv->n_columns++;
9407 _pspp_sheet_view_column_set_tree_view (column, tree_view);
9409 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9413 _pspp_sheet_view_column_realize_button (column);
9415 for (list = tree_view->priv->columns; list; list = list->next)
9417 column = PSPP_SHEET_VIEW_COLUMN (list->data);
9418 if (column->visible)
9419 _pspp_sheet_view_column_cell_set_dirty (column);
9421 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9424 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9426 return tree_view->priv->n_columns;
9430 * pspp_sheet_view_insert_column_with_attributes:
9431 * @tree_view: A #PsppSheetView
9432 * @position: The position to insert the new column in.
9433 * @title: The title to set the header to.
9434 * @cell: The #GtkCellRenderer.
9435 * @Varargs: A %NULL-terminated list of attributes.
9437 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9438 * @position. If @position is -1, then the newly created column is inserted at
9439 * the end. The column is initialized with the attributes given.
9441 * Return value: The number of columns in @tree_view after insertion.
9444 pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view,
9447 GtkCellRenderer *cell,
9450 PsppSheetViewColumn *column;
9455 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9457 column = pspp_sheet_view_column_new ();
9458 pspp_sheet_view_column_set_title (column, title);
9459 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9461 va_start (args, cell);
9463 attribute = va_arg (args, gchar *);
9465 while (attribute != NULL)
9467 column_id = va_arg (args, gint);
9468 pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9469 attribute = va_arg (args, gchar *);
9474 pspp_sheet_view_insert_column (tree_view, column, position);
9476 return tree_view->priv->n_columns;
9480 * pspp_sheet_view_insert_column_with_data_func:
9481 * @tree_view: a #PsppSheetView
9482 * @position: Position to insert, -1 for append
9483 * @title: column title
9484 * @cell: cell renderer for column
9485 * @func: function to set attributes of cell renderer
9486 * @data: data for @func
9487 * @dnotify: destroy notifier for @data
9489 * Convenience function that inserts a new column into the #PsppSheetView
9490 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9491 * attributes (normally using data from the model). See also
9492 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9494 * Return value: number of columns in the tree view post-insert
9497 pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view,
9500 GtkCellRenderer *cell,
9501 PsppSheetCellDataFunc func,
9503 GDestroyNotify dnotify)
9505 PsppSheetViewColumn *column;
9507 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9509 column = pspp_sheet_view_column_new ();
9510 pspp_sheet_view_column_set_title (column, title);
9511 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9512 pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9514 pspp_sheet_view_insert_column (tree_view, column, position);
9516 return tree_view->priv->n_columns;
9520 * pspp_sheet_view_get_column:
9521 * @tree_view: A #PsppSheetView.
9522 * @n: The position of the column, counting from 0.
9524 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9526 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9529 PsppSheetViewColumn *
9530 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9533 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9535 if (n < 0 || n >= tree_view->priv->n_columns)
9538 if (tree_view->priv->columns == NULL)
9541 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9545 * pspp_sheet_view_get_columns:
9546 * @tree_view: A #PsppSheetView
9548 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9549 * The returned list must be freed with g_list_free ().
9551 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9554 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9556 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9558 return g_list_copy (tree_view->priv->columns);
9562 * pspp_sheet_view_move_column_after:
9563 * @tree_view: A #PsppSheetView
9564 * @column: The #PsppSheetViewColumn to be moved.
9565 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9567 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9568 * @column is placed in the first position.
9571 pspp_sheet_view_move_column_after (PsppSheetView *tree_view,
9572 PsppSheetViewColumn *column,
9573 PsppSheetViewColumn *base_column)
9575 GList *column_list_el, *base_el = NULL;
9577 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9579 column_list_el = g_list_find (tree_view->priv->columns, column);
9580 g_return_if_fail (column_list_el != NULL);
9584 base_el = g_list_find (tree_view->priv->columns, base_column);
9585 g_return_if_fail (base_el != NULL);
9588 if (column_list_el->prev == base_el)
9591 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9592 if (base_el == NULL)
9594 column_list_el->prev = NULL;
9595 column_list_el->next = tree_view->priv->columns;
9596 if (column_list_el->next)
9597 column_list_el->next->prev = column_list_el;
9598 tree_view->priv->columns = column_list_el;
9602 column_list_el->prev = base_el;
9603 column_list_el->next = base_el->next;
9604 if (column_list_el->next)
9605 column_list_el->next->prev = column_list_el;
9606 base_el->next = column_list_el;
9609 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9611 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9612 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9615 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9619 * pspp_sheet_view_set_column_drag_function:
9620 * @tree_view: A #PsppSheetView.
9621 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9622 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9623 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9625 * Sets a user function for determining where a column may be dropped when
9626 * dragged. This function is called on every column pair in turn at the
9627 * beginning of a column drag to determine where a drop can take place. The
9628 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9629 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9630 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9631 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9632 * @tree_view reverts to the default behavior of allowing all columns to be
9633 * dropped everywhere.
9636 pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view,
9637 PsppSheetViewColumnDropFunc func,
9639 GDestroyNotify destroy)
9641 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9643 if (tree_view->priv->column_drop_func_data_destroy)
9644 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9646 tree_view->priv->column_drop_func = func;
9647 tree_view->priv->column_drop_func_data = user_data;
9648 tree_view->priv->column_drop_func_data_destroy = destroy;
9652 * pspp_sheet_view_scroll_to_point:
9653 * @tree_view: a #PsppSheetView
9654 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9655 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9657 * Scrolls the tree view such that the top-left corner of the visible
9658 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9659 * in tree coordinates. The @tree_view must be realized before
9660 * this function is called. If it isn't, you probably want to be
9661 * using pspp_sheet_view_scroll_to_cell().
9663 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9666 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9670 GtkAdjustment *hadj;
9671 GtkAdjustment *vadj;
9673 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9674 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9676 hadj = tree_view->priv->hadjustment;
9677 vadj = tree_view->priv->vadjustment;
9680 gtk_adjustment_set_value (hadj, CLAMP (tree_x, hadj->lower, hadj->upper - hadj->page_size));
9682 gtk_adjustment_set_value (vadj, CLAMP (tree_y, vadj->lower, vadj->upper - vadj->page_size));
9686 * pspp_sheet_view_scroll_to_cell:
9687 * @tree_view: A #PsppSheetView.
9688 * @path: (allow-none): The path of the row to move to, or %NULL.
9689 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9690 * @use_align: whether to use alignment arguments, or %FALSE.
9691 * @row_align: The vertical alignment of the row specified by @path.
9692 * @col_align: The horizontal alignment of the column specified by @column.
9694 * Moves the alignments of @tree_view to the position specified by @column and
9695 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9696 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9697 * or @path need to be non-%NULL. @row_align determines where the row is
9698 * placed, and @col_align determines where @column is placed. Both are expected
9699 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9700 * right/bottom alignment, 0.5 means center.
9702 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9703 * tree does the minimum amount of work to scroll the cell onto the screen.
9704 * This means that the cell will be scrolled to the edge closest to its current
9705 * position. If the cell is currently visible on the screen, nothing is done.
9707 * This function only works if the model is set, and @path is a valid row on the
9708 * model. If the model changes before the @tree_view is realized, the centered
9709 * path will be modified to reflect this change.
9712 pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view,
9714 PsppSheetViewColumn *column,
9719 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9720 g_return_if_fail (tree_view->priv->model != NULL);
9721 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9722 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9723 g_return_if_fail (path != NULL || column != NULL);
9726 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9727 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9729 row_align = CLAMP (row_align, 0.0, 1.0);
9730 col_align = CLAMP (col_align, 0.0, 1.0);
9733 /* Note: Despite the benefits that come from having one code path for the
9734 * scrolling code, we short-circuit validate_visible_area's immplementation as
9735 * it is much slower than just going to the point.
9737 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9738 !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9739 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9741 if (tree_view->priv->scroll_to_path)
9742 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9744 tree_view->priv->scroll_to_path = NULL;
9745 tree_view->priv->scroll_to_column = NULL;
9748 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
9750 tree_view->priv->scroll_to_column = column;
9751 tree_view->priv->scroll_to_use_align = use_align;
9752 tree_view->priv->scroll_to_row_align = row_align;
9753 tree_view->priv->scroll_to_col_align = col_align;
9755 install_presize_handler (tree_view);
9759 GdkRectangle cell_rect;
9760 GdkRectangle vis_rect;
9761 gint dest_x, dest_y;
9763 pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
9764 pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
9766 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
9768 dest_x = vis_rect.x;
9769 dest_y = vis_rect.y;
9775 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
9779 if (cell_rect.x < vis_rect.x)
9780 dest_x = cell_rect.x;
9781 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
9782 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
9790 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
9791 dest_y = MAX (dest_y, 0);
9795 if (cell_rect.y < vis_rect.y)
9796 dest_y = cell_rect.y;
9797 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
9798 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
9802 pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
9807 * pspp_sheet_view_row_activated:
9808 * @tree_view: A #PsppSheetView
9809 * @path: The #GtkTreePath to be activated.
9810 * @column: The #PsppSheetViewColumn to be activated.
9812 * Activates the cell determined by @path and @column.
9815 pspp_sheet_view_row_activated (PsppSheetView *tree_view,
9817 PsppSheetViewColumn *column)
9819 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9821 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
9826 * pspp_sheet_view_get_reorderable:
9827 * @tree_view: a #PsppSheetView
9829 * Retrieves whether the user can reorder the tree via drag-and-drop. See
9830 * pspp_sheet_view_set_reorderable().
9832 * Return value: %TRUE if the tree can be reordered.
9835 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
9837 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9839 return tree_view->priv->reorderable;
9843 * pspp_sheet_view_set_reorderable:
9844 * @tree_view: A #PsppSheetView.
9845 * @reorderable: %TRUE, if the tree can be reordered.
9847 * This function is a convenience function to allow you to reorder
9848 * models that support the #GtkDragSourceIface and the
9849 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
9850 * these. If @reorderable is %TRUE, then the user can reorder the
9851 * model by dragging and dropping rows. The developer can listen to
9852 * these changes by connecting to the model's row_inserted and
9853 * row_deleted signals. The reordering is implemented by setting up
9854 * the tree view as a drag source and destination. Therefore, drag and
9855 * drop can not be used in a reorderable view for any other purpose.
9857 * This function does not give you any degree of control over the order -- any
9858 * reordering is allowed. If more control is needed, you should probably
9859 * handle drag and drop manually.
9862 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
9863 gboolean reorderable)
9865 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9867 reorderable = reorderable != FALSE;
9869 if (tree_view->priv->reorderable == reorderable)
9874 const GtkTargetEntry row_targets[] = {
9875 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
9878 pspp_sheet_view_enable_model_drag_source (tree_view,
9881 G_N_ELEMENTS (row_targets),
9883 pspp_sheet_view_enable_model_drag_dest (tree_view,
9885 G_N_ELEMENTS (row_targets),
9890 pspp_sheet_view_unset_rows_drag_source (tree_view);
9891 pspp_sheet_view_unset_rows_drag_dest (tree_view);
9894 tree_view->priv->reorderable = reorderable;
9896 g_object_notify (G_OBJECT (tree_view), "reorderable");
9899 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
9900 is pressed, other rows will be unselected.
9902 If CLAMP_NODE is true, then the sheetview will scroll to make the row
9905 pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
9907 gboolean clear_and_select,
9908 gboolean clamp_node)
9912 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9914 GtkTreePath *cursor_path;
9915 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9916 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
9917 gtk_tree_path_free (cursor_path);
9920 gtk_tree_row_reference_free (tree_view->priv->cursor);
9921 tree_view->priv->cursor = NULL;
9923 _pspp_sheet_view_find_node (tree_view, path, &node);
9924 tree_view->priv->cursor =
9925 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
9926 tree_view->priv->model,
9929 if (tree_view->priv->row_count > 0)
9933 if (clear_and_select && !tree_view->priv->ctrl_pressed)
9935 GtkTreeSelectMode mode = 0;
9937 if (tree_view->priv->ctrl_pressed)
9938 mode |= GTK_TREE_SELECT_MODE_TOGGLE;
9939 if (tree_view->priv->shift_pressed)
9940 mode |= GTK_TREE_SELECT_MODE_EXTEND;
9942 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
9947 /* We have to re-find tree and node here again, somebody might have
9948 * cleared the node or the whole tree in the PsppSheetSelection::changed
9949 * callback. If the nodes differ we bail out here.
9951 _pspp_sheet_view_find_node (tree_view, path, &new_node);
9953 if (node != new_node)
9958 pspp_sheet_view_clamp_node_visible (tree_view, node);
9959 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
9963 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
9967 * pspp_sheet_view_get_cursor:
9968 * @tree_view: A #PsppSheetView
9969 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
9970 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
9972 * Fills in @path and @focus_column with the current path and focus column. If
9973 * the cursor isn't currently set, then *@path will be %NULL. If no column
9974 * currently has focus, then *@focus_column will be %NULL.
9976 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
9977 * you are done with it.
9980 pspp_sheet_view_get_cursor (PsppSheetView *tree_view,
9982 PsppSheetViewColumn **focus_column)
9984 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9988 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9989 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9996 *focus_column = tree_view->priv->focus_column;
10001 * pspp_sheet_view_set_cursor:
10002 * @tree_view: A #PsppSheetView
10003 * @path: A #GtkTreePath
10004 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10005 * @start_editing: %TRUE if the specified cell should start being edited.
10007 * Sets the current keyboard focus to be at @path, and selects it. This is
10008 * useful when you want to focus the user's attention on a particular row. If
10009 * @focus_column is not %NULL, then focus is given to the column specified by
10010 * it. Additionally, if @focus_column is specified, and @start_editing is
10011 * %TRUE, then editing should be started in the specified cell.
10012 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10013 * in order to give keyboard focus to the widget. Please note that editing
10014 * can only happen when the widget is realized.
10016 * If @path is invalid for @model, the current cursor (if any) will be unset
10017 * and the function will return without failing.
10020 pspp_sheet_view_set_cursor (PsppSheetView *tree_view,
10022 PsppSheetViewColumn *focus_column,
10023 gboolean start_editing)
10025 pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10026 NULL, start_editing);
10030 * pspp_sheet_view_set_cursor_on_cell:
10031 * @tree_view: A #PsppSheetView
10032 * @path: A #GtkTreePath
10033 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10034 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10035 * @start_editing: %TRUE if the specified cell should start being edited.
10037 * Sets the current keyboard focus to be at @path, and selects it. This is
10038 * useful when you want to focus the user's attention on a particular row. If
10039 * @focus_column is not %NULL, then focus is given to the column specified by
10040 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10041 * contains 2 or more editable or activatable cells, then focus is given to
10042 * the cell specified by @focus_cell. Additionally, if @focus_column is
10043 * specified, and @start_editing is %TRUE, then editing should be started in
10044 * the specified cell. This function is often followed by
10045 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10046 * widget. Please note that editing can only happen when the widget is
10049 * If @path is invalid for @model, the current cursor (if any) will be unset
10050 * and the function will return without failing.
10055 pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view,
10057 PsppSheetViewColumn *focus_column,
10058 GtkCellRenderer *focus_cell,
10059 gboolean start_editing)
10061 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10062 g_return_if_fail (path != NULL);
10063 g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10065 if (!tree_view->priv->model)
10070 g_return_if_fail (focus_column);
10071 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10074 /* cancel the current editing, if it exists */
10075 if (tree_view->priv->edited_column &&
10076 tree_view->priv->edited_column->editable_widget)
10077 pspp_sheet_view_stop_editing (tree_view, TRUE);
10079 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE);
10081 if (focus_column && focus_column->visible)
10084 gboolean column_in_tree = FALSE;
10086 for (list = tree_view->priv->columns; list; list = list->next)
10087 if (list->data == focus_column)
10089 column_in_tree = TRUE;
10092 g_return_if_fail (column_in_tree);
10093 tree_view->priv->focus_column = focus_column;
10095 pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10097 pspp_sheet_view_start_editing (tree_view, path);
10099 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10100 pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10106 * pspp_sheet_view_get_bin_window:
10107 * @tree_view: A #PsppSheetView
10109 * Returns the window that @tree_view renders to. This is used primarily to
10110 * compare to <literal>event->window</literal> to confirm that the event on
10111 * @tree_view is on the right window.
10113 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10116 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10118 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10120 return tree_view->priv->bin_window;
10124 * pspp_sheet_view_get_path_at_pos:
10125 * @tree_view: A #PsppSheetView.
10126 * @x: The x position to be identified (relative to bin_window).
10127 * @y: The y position to be identified (relative to bin_window).
10128 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10129 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10130 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10131 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10133 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10134 * (please see pspp_sheet_view_get_bin_window()).
10135 * That is, @x and @y are relative to an events coordinates. @x and @y must
10136 * come from an event on the @tree_view only where <literal>event->window ==
10137 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10138 * things like popup menus. If @path is non-%NULL, then it will be filled
10139 * with the #GtkTreePath at that point. This path should be freed with
10140 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10141 * with the column at that point. @cell_x and @cell_y return the coordinates
10142 * relative to the cell background (i.e. the @background_area passed to
10143 * gtk_cell_renderer_render()). This function is only meaningful if
10144 * @tree_view is realized. Therefore this function will always return %FALSE
10145 * if @tree_view is not realized or does not have a model.
10147 * For converting widget coordinates (eg. the ones you get from
10148 * GtkWidget::query-tooltip), please see
10149 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10151 * Return value: %TRUE if a row exists at that coordinate.
10154 pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view,
10157 GtkTreePath **path,
10158 PsppSheetViewColumn **column,
10165 g_return_val_if_fail (tree_view != NULL, FALSE);
10172 if (tree_view->priv->bin_window == NULL)
10175 if (tree_view->priv->row_count == 0)
10178 if (x > tree_view->priv->hadjustment->upper)
10181 if (x < 0 || y < 0)
10184 if (column || cell_x)
10186 PsppSheetViewColumn *tmp_column;
10187 PsppSheetViewColumn *last_column = NULL;
10189 gint remaining_x = x;
10190 gboolean found = FALSE;
10193 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10194 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10196 list = (rtl ? list->prev : list->next))
10198 tmp_column = list->data;
10200 if (tmp_column->visible == FALSE)
10203 last_column = tmp_column;
10204 if (remaining_x <= tmp_column->width)
10209 *column = tmp_column;
10212 *cell_x = remaining_x;
10216 remaining_x -= tmp_column->width;
10219 /* If found is FALSE and there is a last_column, then it the remainder
10220 * space is in that area
10227 *column = last_column;
10230 *cell_x = last_column->width + remaining_x;
10239 y_offset = pspp_sheet_view_find_offset (tree_view,
10240 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10247 *cell_y = y_offset;
10250 *path = _pspp_sheet_view_find_path (tree_view, node);
10255 /* Computes 'cell_area' from 'background_area', which must be the background
10256 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10257 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10258 the cell area as passed to _pspp_sheet_view_column_cell_render().
10260 'column' is required to properly adjust 'cell_area->x' and
10261 'cell_area->width'. It may be set to NULL if these values are not of
10262 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10265 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
10266 PsppSheetViewColumn *column,
10267 const GdkRectangle *background_area,
10268 gboolean subtract_focus_rect,
10269 GdkRectangle *cell_area)
10271 gint vertical_separator;
10272 gint horizontal_separator;
10274 *cell_area = *background_area;
10276 gtk_widget_style_get (GTK_WIDGET (tree_view),
10277 "vertical-separator", &vertical_separator,
10278 "horizontal-separator", &horizontal_separator,
10280 cell_area->x += horizontal_separator / 2;
10281 cell_area->y += vertical_separator / 2;
10282 cell_area->width -= horizontal_separator;
10283 cell_area->height -= vertical_separator;
10285 if (subtract_focus_rect)
10287 int focus_line_width;
10289 gtk_widget_style_get (GTK_WIDGET (tree_view),
10290 "focus-line-width", &focus_line_width,
10292 cell_area->x += focus_line_width;
10293 cell_area->y += focus_line_width;
10294 cell_area->width -= 2 * focus_line_width;
10295 cell_area->height -= 2 * focus_line_width;
10298 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10300 gint grid_line_width;
10301 gtk_widget_style_get (GTK_WIDGET (tree_view),
10302 "grid-line-width", &grid_line_width,
10305 if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10306 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10309 PsppSheetViewColumn *first_column, *last_column;
10312 /* Find the last visible column. */
10313 last_column = NULL;
10314 for (list = g_list_last (tree_view->priv->columns);
10318 PsppSheetViewColumn *c = list->data;
10326 /* Find the first visible column. */
10327 first_column = NULL;
10328 for (list = g_list_first (tree_view->priv->columns);
10332 PsppSheetViewColumn *c = list->data;
10340 if (column == first_column)
10342 cell_area->width -= grid_line_width / 2;
10344 else if (column == last_column)
10346 cell_area->x += grid_line_width / 2;
10347 cell_area->width -= grid_line_width / 2;
10351 cell_area->x += grid_line_width / 2;
10352 cell_area->width -= grid_line_width;
10356 if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10357 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10359 cell_area->y += grid_line_width / 2;
10360 cell_area->height -= grid_line_width;
10364 if (column == NULL)
10367 cell_area->width = 0;
10372 * pspp_sheet_view_get_cell_area:
10373 * @tree_view: a #PsppSheetView
10374 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10375 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10376 * @rect: rectangle to fill with cell rect
10378 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10379 * row specified by @path and the column specified by @column. If @path is
10380 * %NULL, or points to a path not currently displayed, the @y and @height fields
10381 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10382 * fields will be filled with 0. The sum of all cell rects does not cover the
10383 * entire tree; there are extra pixels in between rows, for example. The
10384 * returned rectangle is equivalent to the @cell_area passed to
10385 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10389 pspp_sheet_view_get_cell_area (PsppSheetView *tree_view,
10391 PsppSheetViewColumn *column,
10392 GdkRectangle *rect)
10394 GdkRectangle background_area;
10396 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10397 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10398 g_return_if_fail (rect != NULL);
10399 g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10400 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10402 pspp_sheet_view_get_background_area (tree_view, path, column,
10404 pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10409 * pspp_sheet_view_get_background_area:
10410 * @tree_view: a #PsppSheetView
10411 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10412 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10413 * @rect: rectangle to fill with cell background rect
10415 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10416 * row specified by @path and the column specified by @column. If @path is
10417 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10418 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10419 * fields will be filled with 0. The returned rectangle is equivalent to the
10420 * @background_area passed to gtk_cell_renderer_render(). These background
10421 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10422 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10423 * itself, excluding surrounding borders.
10427 pspp_sheet_view_get_background_area (PsppSheetView *tree_view,
10429 PsppSheetViewColumn *column,
10430 GdkRectangle *rect)
10434 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10435 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10436 g_return_if_fail (rect != NULL);
10445 /* Get vertical coords */
10447 _pspp_sheet_view_find_node (tree_view, path, &node);
10451 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10453 rect->height = ROW_HEIGHT (tree_view);
10460 pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10461 rect->width = x2 - rect->x;
10466 * pspp_sheet_view_get_visible_rect:
10467 * @tree_view: a #PsppSheetView
10468 * @visible_rect: rectangle to fill
10470 * Fills @visible_rect with the currently-visible region of the
10471 * buffer, in tree coordinates. Convert to bin_window coordinates with
10472 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10473 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10474 * scrollable area of the tree.
10477 pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view,
10478 GdkRectangle *visible_rect)
10482 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10484 widget = GTK_WIDGET (tree_view);
10488 visible_rect->x = tree_view->priv->hadjustment->value;
10489 visible_rect->y = tree_view->priv->vadjustment->value;
10490 visible_rect->width = widget->allocation.width;
10491 visible_rect->height = widget->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10496 * pspp_sheet_view_widget_to_tree_coords:
10497 * @tree_view: a #PsppSheetView
10498 * @wx: X coordinate relative to bin_window
10499 * @wy: Y coordinate relative to bin_window
10500 * @tx: return location for tree X coordinate
10501 * @ty: return location for tree Y coordinate
10503 * Converts bin_window coordinates to coordinates for the
10504 * tree (the full scrollable area of the tree).
10506 * Deprecated: 2.12: Due to historial reasons the name of this function is
10507 * incorrect. For converting coordinates relative to the widget to
10508 * bin_window coordinates, please see
10509 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10513 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10519 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10522 *tx = wx + tree_view->priv->hadjustment->value;
10524 *ty = wy + tree_view->priv->dy;
10528 * pspp_sheet_view_tree_to_widget_coords:
10529 * @tree_view: a #PsppSheetView
10530 * @tx: tree X coordinate
10531 * @ty: tree Y coordinate
10532 * @wx: return location for X coordinate relative to bin_window
10533 * @wy: return location for Y coordinate relative to bin_window
10535 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10536 * to bin_window coordinates.
10538 * Deprecated: 2.12: Due to historial reasons the name of this function is
10539 * incorrect. For converting bin_window coordinates to coordinates relative
10540 * to bin_window, please see
10541 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10545 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10551 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10554 *wx = tx - tree_view->priv->hadjustment->value;
10556 *wy = ty - tree_view->priv->dy;
10561 * pspp_sheet_view_convert_widget_to_tree_coords:
10562 * @tree_view: a #PsppSheetView
10563 * @wx: X coordinate relative to the widget
10564 * @wy: Y coordinate relative to the widget
10565 * @tx: return location for tree X coordinate
10566 * @ty: return location for tree Y coordinate
10568 * Converts widget coordinates to coordinates for the
10569 * tree (the full scrollable area of the tree).
10574 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10582 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10584 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10587 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10593 * pspp_sheet_view_convert_tree_to_widget_coords:
10594 * @tree_view: a #PsppSheetView
10595 * @tx: X coordinate relative to the tree
10596 * @ty: Y coordinate relative to the tree
10597 * @wx: return location for widget X coordinate
10598 * @wy: return location for widget Y coordinate
10600 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10601 * to widget coordinates.
10606 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10614 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10616 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10619 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10625 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10626 * @tree_view: a #PsppSheetView
10627 * @wx: X coordinate relative to the widget
10628 * @wy: Y coordinate relative to the widget
10629 * @bx: return location for bin_window X coordinate
10630 * @by: return location for bin_window Y coordinate
10632 * Converts widget coordinates to coordinates for the bin_window
10633 * (see pspp_sheet_view_get_bin_window()).
10638 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10644 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10647 *bx = wx + tree_view->priv->hadjustment->value;
10649 *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10653 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10654 * @tree_view: a #PsppSheetView
10655 * @bx: bin_window X coordinate
10656 * @by: bin_window Y coordinate
10657 * @wx: return location for widget X coordinate
10658 * @wy: return location for widget Y coordinate
10660 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10661 * to widget relative coordinates.
10666 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10672 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10675 *wx = bx - tree_view->priv->hadjustment->value;
10677 *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10681 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10682 * @tree_view: a #PsppSheetView
10683 * @tx: tree X coordinate
10684 * @ty: tree Y coordinate
10685 * @bx: return location for X coordinate relative to bin_window
10686 * @by: return location for Y coordinate relative to bin_window
10688 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10689 * to bin_window coordinates.
10694 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10700 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10705 *by = ty - tree_view->priv->dy;
10709 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10710 * @tree_view: a #PsppSheetView
10711 * @bx: X coordinate relative to bin_window
10712 * @by: Y coordinate relative to bin_window
10713 * @tx: return location for tree X coordinate
10714 * @ty: return location for tree Y coordinate
10716 * Converts bin_window coordinates to coordinates for the
10717 * tree (the full scrollable area of the tree).
10722 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10728 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10733 *ty = by + tree_view->priv->dy;
10739 * pspp_sheet_view_get_visible_range:
10740 * @tree_view: A #PsppSheetView
10741 * @start_path: (allow-none): Return location for start of region, or %NULL.
10742 * @end_path: (allow-none): Return location for end of region, or %NULL.
10744 * Sets @start_path and @end_path to be the first and last visible path.
10745 * Note that there may be invisible paths in between.
10747 * The paths should be freed with gtk_tree_path_free() after use.
10749 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
10754 pspp_sheet_view_get_visible_range (PsppSheetView *tree_view,
10755 GtkTreePath **start_path,
10756 GtkTreePath **end_path)
10761 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10763 if (!tree_view->priv->row_count)
10770 pspp_sheet_view_find_offset (tree_view,
10771 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
10774 *start_path = _pspp_sheet_view_find_path (tree_view, node);
10783 if (tree_view->priv->height < tree_view->priv->vadjustment->page_size)
10784 y = tree_view->priv->height - 1;
10786 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, tree_view->priv->vadjustment->page_size) - 1;
10788 pspp_sheet_view_find_offset (tree_view, y, &node);
10790 *end_path = _pspp_sheet_view_find_path (tree_view, node);
10799 unset_reorderable (PsppSheetView *tree_view)
10801 if (tree_view->priv->reorderable)
10803 tree_view->priv->reorderable = FALSE;
10804 g_object_notify (G_OBJECT (tree_view), "reorderable");
10809 * pspp_sheet_view_enable_model_drag_source:
10810 * @tree_view: a #PsppSheetView
10811 * @start_button_mask: Mask of allowed buttons to start drag
10812 * @targets: the table of targets that the drag will support
10813 * @n_targets: the number of items in @targets
10814 * @actions: the bitmask of possible actions for a drag from this
10817 * Turns @tree_view into a drag source for automatic DND. Calling this
10818 * method sets #PsppSheetView:reorderable to %FALSE.
10821 pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view,
10822 GdkModifierType start_button_mask,
10823 const GtkTargetEntry *targets,
10825 GdkDragAction actions)
10827 TreeViewDragInfo *di;
10829 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10831 gtk_drag_source_set (GTK_WIDGET (tree_view),
10837 di = ensure_info (tree_view);
10839 di->start_button_mask = start_button_mask;
10840 di->source_actions = actions;
10841 di->source_set = TRUE;
10843 unset_reorderable (tree_view);
10847 * pspp_sheet_view_enable_model_drag_dest:
10848 * @tree_view: a #PsppSheetView
10849 * @targets: the table of targets that the drag will support
10850 * @n_targets: the number of items in @targets
10851 * @actions: the bitmask of possible actions for a drag from this
10854 * Turns @tree_view into a drop destination for automatic DND. Calling
10855 * this method sets #PsppSheetView:reorderable to %FALSE.
10858 pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view,
10859 const GtkTargetEntry *targets,
10861 GdkDragAction actions)
10863 TreeViewDragInfo *di;
10865 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10867 gtk_drag_dest_set (GTK_WIDGET (tree_view),
10873 di = ensure_info (tree_view);
10874 di->dest_set = TRUE;
10876 unset_reorderable (tree_view);
10880 * pspp_sheet_view_unset_rows_drag_source:
10881 * @tree_view: a #PsppSheetView
10883 * Undoes the effect of
10884 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
10885 * #PsppSheetView:reorderable to %FALSE.
10888 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
10890 TreeViewDragInfo *di;
10892 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10894 di = get_info (tree_view);
10898 if (di->source_set)
10900 gtk_drag_source_unset (GTK_WIDGET (tree_view));
10901 di->source_set = FALSE;
10904 if (!di->dest_set && !di->source_set)
10905 remove_info (tree_view);
10908 unset_reorderable (tree_view);
10912 * pspp_sheet_view_unset_rows_drag_dest:
10913 * @tree_view: a #PsppSheetView
10915 * Undoes the effect of
10916 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
10917 * #PsppSheetView:reorderable to %FALSE.
10920 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
10922 TreeViewDragInfo *di;
10924 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10926 di = get_info (tree_view);
10932 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
10933 di->dest_set = FALSE;
10936 if (!di->dest_set && !di->source_set)
10937 remove_info (tree_view);
10940 unset_reorderable (tree_view);
10944 * pspp_sheet_view_set_drag_dest_row:
10945 * @tree_view: a #PsppSheetView
10946 * @path: (allow-none): The path of the row to highlight, or %NULL.
10947 * @pos: Specifies whether to drop before, after or into the row
10949 * Sets the row that is highlighted for feedback.
10952 pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view,
10954 PsppSheetViewDropPosition pos)
10956 GtkTreePath *current_dest;
10958 /* Note; this function is exported to allow a custom DND
10959 * implementation, so it can't touch TreeViewDragInfo
10962 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10964 current_dest = NULL;
10966 if (tree_view->priv->drag_dest_row)
10968 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
10969 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
10972 /* special case a drop on an empty model */
10973 tree_view->priv->empty_view_drop = 0;
10975 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
10976 && gtk_tree_path_get_depth (path) == 1
10977 && gtk_tree_path_get_indices (path)[0] == 0)
10981 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
10985 tree_view->priv->empty_view_drop = 1;
10988 tree_view->priv->drag_dest_pos = pos;
10992 tree_view->priv->drag_dest_row =
10993 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
10994 pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
10997 tree_view->priv->drag_dest_row = NULL;
11001 int node, new_node;
11003 _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11004 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11008 new_node = pspp_sheet_view_node_next (tree_view, node);
11010 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11012 new_node = pspp_sheet_view_node_prev (tree_view, node);
11014 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11016 gtk_tree_path_free (current_dest);
11021 * pspp_sheet_view_get_drag_dest_row:
11022 * @tree_view: a #PsppSheetView
11023 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11024 * @pos: (allow-none): Return location for the drop position, or %NULL
11026 * Gets information about the row that is highlighted for feedback.
11029 pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view,
11030 GtkTreePath **path,
11031 PsppSheetViewDropPosition *pos)
11033 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11037 if (tree_view->priv->drag_dest_row)
11038 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11041 if (tree_view->priv->empty_view_drop)
11042 *path = gtk_tree_path_new_from_indices (0, -1);
11049 *pos = tree_view->priv->drag_dest_pos;
11053 * pspp_sheet_view_get_dest_row_at_pos:
11054 * @tree_view: a #PsppSheetView
11055 * @drag_x: the position to determine the destination row for
11056 * @drag_y: the position to determine the destination row for
11057 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11058 * @pos: (allow-none): Return location for the drop position, or %NULL
11060 * Determines the destination row for a given position. @drag_x and
11061 * @drag_y are expected to be in widget coordinates. This function is only
11062 * meaningful if @tree_view is realized. Therefore this function will always
11063 * return %FALSE if @tree_view is not realized or does not have a model.
11065 * Return value: whether there is a row at the given position, %TRUE if this
11066 * is indeed the case.
11069 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view,
11072 GtkTreePath **path,
11073 PsppSheetViewDropPosition *pos)
11077 gdouble offset_into_row;
11080 PsppSheetViewColumn *column = NULL;
11081 GtkTreePath *tmp_path = NULL;
11083 /* Note; this function is exported to allow a custom DND
11084 * implementation, so it can't touch TreeViewDragInfo
11087 g_return_val_if_fail (tree_view != NULL, FALSE);
11088 g_return_val_if_fail (drag_x >= 0, FALSE);
11089 g_return_val_if_fail (drag_y >= 0, FALSE);
11094 if (tree_view->priv->bin_window == NULL)
11097 if (tree_view->priv->row_count == 0)
11100 /* If in the top third of a row, we drop before that row; if
11101 * in the bottom third, drop after that row; if in the middle,
11102 * and the row has children, drop into the row.
11104 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11107 if (!pspp_sheet_view_get_path_at_pos (tree_view,
11116 pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11119 offset_into_row = cell_y;
11124 gtk_tree_path_free (tmp_path);
11128 third = cell.height / 3.0;
11132 if (offset_into_row < third)
11134 *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11136 else if (offset_into_row < (cell.height / 2.0))
11138 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11140 else if (offset_into_row < third * 2.0)
11142 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11146 *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11155 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11157 * pspp_sheet_view_create_row_drag_icon:
11158 * @tree_view: a #PsppSheetView
11159 * @path: a #GtkTreePath in @tree_view
11161 * Creates a #GdkPixmap representation of the row at @path.
11162 * This image is used for a drag icon.
11164 * Return value: a newly-allocated pixmap of the drag icon.
11167 pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view,
11174 GdkRectangle background_area;
11175 GdkRectangle expose_area;
11177 /* start drawing inside the black outline */
11179 GdkDrawable *drawable;
11180 gint bin_window_width;
11183 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11184 g_return_val_if_fail (path != NULL, NULL);
11186 widget = GTK_WIDGET (tree_view);
11188 if (!gtk_widget_get_realized (widget))
11191 _pspp_sheet_view_find_node (tree_view,
11198 if (!gtk_tree_model_get_iter (tree_view->priv->model,
11205 background_area.y = y;
11206 background_area.height = ROW_HEIGHT (tree_view);
11208 gdk_drawable_get_size (tree_view->priv->bin_window,
11209 &bin_window_width, NULL);
11211 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11212 bin_window_width + 2,
11213 background_area.height + 2,
11218 expose_area.width = bin_window_width + 2;
11219 expose_area.height = background_area.height + 2;
11221 gdk_draw_rectangle (drawable,
11222 widget->style->base_gc [gtk_widget_get_state (widget)],
11225 bin_window_width + 2,
11226 background_area.height + 2);
11228 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11230 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11232 list = (rtl ? list->prev : list->next))
11234 PsppSheetViewColumn *column = list->data;
11235 GdkRectangle cell_area;
11236 gint vertical_separator;
11238 if (!column->visible)
11241 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11243 background_area.x = cell_offset;
11244 background_area.width = column->width;
11246 gtk_widget_style_get (widget,
11247 "vertical-separator", &vertical_separator,
11250 cell_area = background_area;
11252 cell_area.y += vertical_separator / 2;
11253 cell_area.height -= vertical_separator;
11255 if (pspp_sheet_view_column_cell_is_visible (column))
11256 _pspp_sheet_view_column_cell_render (column,
11262 cell_offset += column->width;
11265 gdk_draw_rectangle (drawable,
11266 widget->style->black_gc,
11269 bin_window_width + 1,
11270 background_area.height + 1);
11277 * pspp_sheet_view_set_destroy_count_func:
11278 * @tree_view: A #PsppSheetView
11279 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11280 * @data: (allow-none): User data to be passed to @func, or %NULL
11281 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11283 * This function should almost never be used. It is meant for private use by
11284 * ATK for determining the number of visible children that are removed when a row is deleted.
11287 pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view,
11288 PsppSheetDestroyCountFunc func,
11290 GDestroyNotify destroy)
11292 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11294 if (tree_view->priv->destroy_count_destroy)
11295 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11297 tree_view->priv->destroy_count_func = func;
11298 tree_view->priv->destroy_count_data = data;
11299 tree_view->priv->destroy_count_destroy = destroy;
11304 * Interactive search
11308 * pspp_sheet_view_set_enable_search:
11309 * @tree_view: A #PsppSheetView
11310 * @enable_search: %TRUE, if the user can search interactively
11312 * If @enable_search is set, then the user can type in text to search through
11313 * the tree interactively (this is sometimes called "typeahead find").
11315 * Note that even if this is %FALSE, the user can still initiate a search
11316 * using the "start-interactive-search" key binding.
11319 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11320 gboolean enable_search)
11322 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11324 enable_search = !!enable_search;
11326 if (tree_view->priv->enable_search != enable_search)
11328 tree_view->priv->enable_search = enable_search;
11329 g_object_notify (G_OBJECT (tree_view), "enable-search");
11334 * pspp_sheet_view_get_enable_search:
11335 * @tree_view: A #PsppSheetView
11337 * Returns whether or not the tree allows to start interactive searching
11338 * by typing in text.
11340 * Return value: whether or not to let the user search interactively
11343 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11345 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11347 return tree_view->priv->enable_search;
11352 * pspp_sheet_view_get_search_column:
11353 * @tree_view: A #PsppSheetView
11355 * Gets the column searched on by the interactive search code.
11357 * Return value: the column the interactive search code searches in.
11360 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11362 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11364 return (tree_view->priv->search_column);
11368 * pspp_sheet_view_set_search_column:
11369 * @tree_view: A #PsppSheetView
11370 * @column: the column of the model to search in, or -1 to disable searching
11372 * Sets @column as the column where the interactive search code should
11373 * search in for the current model.
11375 * If the search column is set, users can use the "start-interactive-search"
11376 * key binding to bring up search popup. The enable-search property controls
11377 * whether simply typing text will also start an interactive search.
11379 * Note that @column refers to a column of the current model. The search
11380 * column is reset to -1 when the model is changed.
11383 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11386 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11387 g_return_if_fail (column >= -1);
11389 if (tree_view->priv->search_column == column)
11392 tree_view->priv->search_column = column;
11393 g_object_notify (G_OBJECT (tree_view), "search-column");
11397 * pspp_sheet_view_get_search_equal_func:
11398 * @tree_view: A #PsppSheetView
11400 * Returns the compare function currently in use.
11402 * Return value: the currently used compare function for the search code.
11405 PsppSheetViewSearchEqualFunc
11406 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11408 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11410 return tree_view->priv->search_equal_func;
11414 * pspp_sheet_view_set_search_equal_func:
11415 * @tree_view: A #PsppSheetView
11416 * @search_equal_func: the compare function to use during the search
11417 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11418 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11420 * Sets the compare function for the interactive search capabilities; note
11421 * that somewhat like strcmp() returning 0 for equality
11422 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11425 pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view,
11426 PsppSheetViewSearchEqualFunc search_equal_func,
11427 gpointer search_user_data,
11428 GDestroyNotify search_destroy)
11430 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11431 g_return_if_fail (search_equal_func != NULL);
11433 if (tree_view->priv->search_destroy)
11434 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11436 tree_view->priv->search_equal_func = search_equal_func;
11437 tree_view->priv->search_user_data = search_user_data;
11438 tree_view->priv->search_destroy = search_destroy;
11439 if (tree_view->priv->search_equal_func == NULL)
11440 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11444 * pspp_sheet_view_get_search_entry:
11445 * @tree_view: A #PsppSheetView
11447 * Returns the #GtkEntry which is currently in use as interactive search
11448 * entry for @tree_view. In case the built-in entry is being used, %NULL
11449 * will be returned.
11451 * Return value: the entry currently in use as search entry.
11456 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11458 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11460 if (tree_view->priv->search_custom_entry_set)
11461 return GTK_ENTRY (tree_view->priv->search_entry);
11467 * pspp_sheet_view_set_search_entry:
11468 * @tree_view: A #PsppSheetView
11469 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11471 * Sets the entry which the interactive search code will use for this
11472 * @tree_view. This is useful when you want to provide a search entry
11473 * in our interface at all time at a fixed position. Passing %NULL for
11474 * @entry will make the interactive search code use the built-in popup
11480 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11483 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11484 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11486 if (tree_view->priv->search_custom_entry_set)
11488 if (tree_view->priv->search_entry_changed_id)
11490 g_signal_handler_disconnect (tree_view->priv->search_entry,
11491 tree_view->priv->search_entry_changed_id);
11492 tree_view->priv->search_entry_changed_id = 0;
11494 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11495 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11498 g_object_unref (tree_view->priv->search_entry);
11500 else if (tree_view->priv->search_window)
11502 gtk_widget_destroy (tree_view->priv->search_window);
11504 tree_view->priv->search_window = NULL;
11509 tree_view->priv->search_entry = g_object_ref (entry);
11510 tree_view->priv->search_custom_entry_set = TRUE;
11512 if (tree_view->priv->search_entry_changed_id == 0)
11514 tree_view->priv->search_entry_changed_id =
11515 g_signal_connect (tree_view->priv->search_entry, "changed",
11516 G_CALLBACK (pspp_sheet_view_search_init),
11520 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11521 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11524 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11528 tree_view->priv->search_entry = NULL;
11529 tree_view->priv->search_custom_entry_set = FALSE;
11534 * pspp_sheet_view_set_search_position_func:
11535 * @tree_view: A #PsppSheetView
11536 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11537 * to use the default search position function
11538 * @data: (allow-none): user data to pass to @func, or %NULL
11539 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11541 * Sets the function to use when positioning the search dialog.
11546 pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view,
11547 PsppSheetViewSearchPositionFunc func,
11548 gpointer user_data,
11549 GDestroyNotify destroy)
11551 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11553 if (tree_view->priv->search_position_destroy)
11554 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11556 tree_view->priv->search_position_func = func;
11557 tree_view->priv->search_position_user_data = user_data;
11558 tree_view->priv->search_position_destroy = destroy;
11559 if (tree_view->priv->search_position_func == NULL)
11560 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11564 * pspp_sheet_view_get_search_position_func:
11565 * @tree_view: A #PsppSheetView
11567 * Returns the positioning function currently in use.
11569 * Return value: the currently used function for positioning the search dialog.
11573 PsppSheetViewSearchPositionFunc
11574 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11576 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11578 return tree_view->priv->search_position_func;
11583 pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
11584 PsppSheetView *tree_view)
11586 if (tree_view->priv->disable_popdown)
11589 if (tree_view->priv->search_entry_changed_id)
11591 g_signal_handler_disconnect (tree_view->priv->search_entry,
11592 tree_view->priv->search_entry_changed_id);
11593 tree_view->priv->search_entry_changed_id = 0;
11595 if (tree_view->priv->typeselect_flush_timeout)
11597 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11598 tree_view->priv->typeselect_flush_timeout = 0;
11601 if (gtk_widget_get_visible (search_dialog))
11603 /* send focus-in event */
11604 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11605 gtk_widget_hide (search_dialog);
11606 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11607 send_focus_change (GTK_WIDGET (tree_view), TRUE);
11612 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11613 GtkWidget *search_dialog,
11614 gpointer user_data)
11617 gint tree_x, tree_y;
11618 gint tree_width, tree_height;
11619 GdkWindow *tree_window = GTK_WIDGET (tree_view)->window;
11620 GdkScreen *screen = gdk_drawable_get_screen (tree_window);
11621 GtkRequisition requisition;
11623 GdkRectangle monitor;
11625 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11626 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11628 gtk_widget_realize (search_dialog);
11630 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11631 gdk_drawable_get_size (tree_window,
11634 gtk_widget_size_request (search_dialog, &requisition);
11636 if (tree_x + tree_width > gdk_screen_get_width (screen))
11637 x = gdk_screen_get_width (screen) - requisition.width;
11638 else if (tree_x + tree_width - requisition.width < 0)
11641 x = tree_x + tree_width - requisition.width;
11643 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11644 y = gdk_screen_get_height (screen) - requisition.height;
11645 else if (tree_y + tree_height < 0) /* isn't really possible ... */
11648 y = tree_y + tree_height;
11650 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11654 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11658 PsppSheetView *tree_view = (PsppSheetView *)data;
11660 tree_view->priv->disable_popdown = 1;
11661 g_signal_connect (menu, "hide",
11662 G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11665 /* Because we're visible but offscreen, we just set a flag in the preedit
11669 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11670 PsppSheetView *tree_view)
11672 tree_view->priv->imcontext_changed = 1;
11673 if (tree_view->priv->typeselect_flush_timeout)
11675 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11676 tree_view->priv->typeselect_flush_timeout =
11677 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11678 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11685 pspp_sheet_view_search_activate (GtkEntry *entry,
11686 PsppSheetView *tree_view)
11691 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11694 /* If we have a row selected and it's the cursor row, we activate
11696 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11698 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11700 _pspp_sheet_view_find_node (tree_view, path, &node);
11702 if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11703 pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11705 gtk_tree_path_free (path);
11710 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11712 PsppSheetView *tree_view = (PsppSheetView *)data;
11714 tree_view->priv->disable_popdown = 0;
11720 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11723 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11727 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11728 GdkEventAny *event,
11729 PsppSheetView *tree_view)
11731 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11733 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11739 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11740 GdkEventButton *event,
11741 PsppSheetView *tree_view)
11743 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11745 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11747 if (event->window == tree_view->priv->bin_window)
11748 pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
11754 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
11755 GdkEventScroll *event,
11756 PsppSheetView *tree_view)
11758 gboolean retval = FALSE;
11760 if (event->direction == GDK_SCROLL_UP)
11762 pspp_sheet_view_search_move (widget, tree_view, TRUE);
11765 else if (event->direction == GDK_SCROLL_DOWN)
11767 pspp_sheet_view_search_move (widget, tree_view, FALSE);
11771 /* renew the flush timeout */
11772 if (retval && tree_view->priv->typeselect_flush_timeout
11773 && !tree_view->priv->search_custom_entry_set)
11775 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11776 tree_view->priv->typeselect_flush_timeout =
11777 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11778 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11786 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
11787 GdkEventKey *event,
11788 PsppSheetView *tree_view)
11790 gboolean retval = FALSE;
11792 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11793 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11795 /* close window and cancel the search */
11796 if (!tree_view->priv->search_custom_entry_set
11797 && (event->keyval == GDK_Escape ||
11798 event->keyval == GDK_Tab ||
11799 event->keyval == GDK_KP_Tab ||
11800 event->keyval == GDK_ISO_Left_Tab))
11802 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11806 /* select previous matching iter */
11807 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
11809 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11810 gtk_widget_error_bell (widget);
11815 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
11816 && (event->keyval == GDK_g || event->keyval == GDK_G))
11818 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11819 gtk_widget_error_bell (widget);
11824 /* select next matching iter */
11825 if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
11827 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11828 gtk_widget_error_bell (widget);
11833 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
11834 && (event->keyval == GDK_g || event->keyval == GDK_G))
11836 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11837 gtk_widget_error_bell (widget);
11842 /* renew the flush timeout */
11843 if (retval && tree_view->priv->typeselect_flush_timeout
11844 && !tree_view->priv->search_custom_entry_set)
11846 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11847 tree_view->priv->typeselect_flush_timeout =
11848 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11849 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11856 /* this function returns FALSE if there is a search string but
11857 * nothing was found, and TRUE otherwise.
11860 pspp_sheet_view_search_move (GtkWidget *window,
11861 PsppSheetView *tree_view,
11869 GtkTreeModel *model;
11870 PsppSheetSelection *selection;
11872 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
11874 g_return_val_if_fail (text != NULL, FALSE);
11876 len = strlen (text);
11878 if (up && tree_view->priv->selected_iter == 1)
11879 return strlen (text) < 1;
11881 len = strlen (text);
11886 model = pspp_sheet_view_get_model (tree_view);
11887 selection = pspp_sheet_view_get_selection (tree_view);
11890 pspp_sheet_selection_unselect_all (selection);
11891 if (!gtk_tree_model_get_iter_first (model, &iter))
11894 ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
11895 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
11900 tree_view->priv->selected_iter += up?(-1):(1);
11905 /* return to old iter */
11907 gtk_tree_model_get_iter_first (model, &iter);
11908 pspp_sheet_view_search_iter (model, selection,
11910 &count, tree_view->priv->selected_iter);
11916 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
11920 gpointer search_data)
11922 gboolean retval = TRUE;
11924 gchar *normalized_string;
11925 gchar *normalized_key;
11926 gchar *case_normalized_string = NULL;
11927 gchar *case_normalized_key = NULL;
11928 GValue value = {0,};
11929 GValue transformed = {0,};
11931 gtk_tree_model_get_value (model, iter, column, &value);
11933 g_value_init (&transformed, G_TYPE_STRING);
11935 if (!g_value_transform (&value, &transformed))
11937 g_value_unset (&value);
11941 g_value_unset (&value);
11943 str = g_value_get_string (&transformed);
11946 g_value_unset (&transformed);
11950 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
11951 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
11953 if (normalized_string && normalized_key)
11955 case_normalized_string = g_utf8_casefold (normalized_string, -1);
11956 case_normalized_key = g_utf8_casefold (normalized_key, -1);
11958 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
11962 g_value_unset (&transformed);
11963 g_free (normalized_key);
11964 g_free (normalized_string);
11965 g_free (case_normalized_key);
11966 g_free (case_normalized_string);
11972 pspp_sheet_view_search_iter (GtkTreeModel *model,
11973 PsppSheetSelection *selection,
11982 PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
11984 path = gtk_tree_model_get_path (model, iter);
11985 _pspp_sheet_view_find_node (tree_view, path, &node);
11989 gboolean done = FALSE;
11991 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
11996 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
11998 pspp_sheet_selection_select_iter (selection, iter);
11999 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE);
12002 gtk_tree_path_free (path);
12011 node = pspp_sheet_view_node_next (tree_view, node);
12017 has_next = gtk_tree_model_iter_next (model, iter);
12020 gtk_tree_path_next (path);
12023 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12028 gtk_tree_path_free (path);
12030 /* we've run out of tree, done with this func */
12042 pspp_sheet_view_search_init (GtkWidget *entry,
12043 PsppSheetView *tree_view)
12049 GtkTreeModel *model;
12050 PsppSheetSelection *selection;
12052 g_return_if_fail (GTK_IS_ENTRY (entry));
12053 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12055 text = gtk_entry_get_text (GTK_ENTRY (entry));
12057 model = pspp_sheet_view_get_model (tree_view);
12058 selection = pspp_sheet_view_get_selection (tree_view);
12061 pspp_sheet_selection_unselect_all (selection);
12062 if (tree_view->priv->typeselect_flush_timeout
12063 && !tree_view->priv->search_custom_entry_set)
12065 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12066 tree_view->priv->typeselect_flush_timeout =
12067 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12068 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12075 if (!gtk_tree_model_get_iter_first (model, &iter))
12078 ret = pspp_sheet_view_search_iter (model, selection,
12083 tree_view->priv->selected_iter = 1;
12087 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12088 PsppSheetView *tree_view)
12090 if (tree_view->priv->edited_column == NULL)
12093 _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12094 tree_view->priv->edited_column = NULL;
12096 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12097 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12099 g_signal_handlers_disconnect_by_func (cell_editable,
12100 pspp_sheet_view_remove_widget,
12102 g_signal_handlers_disconnect_by_func (cell_editable,
12103 pspp_sheet_view_editable_button_press_event,
12105 g_signal_handlers_disconnect_by_func (cell_editable,
12106 pspp_sheet_view_editable_clicked,
12109 gtk_container_remove (GTK_CONTAINER (tree_view),
12110 GTK_WIDGET (cell_editable));
12112 /* FIXME should only redraw a single node */
12113 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12117 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12118 GtkTreePath *cursor_path)
12121 GdkRectangle background_area;
12122 GdkRectangle cell_area;
12123 GtkCellEditable *editable_widget = NULL;
12124 gchar *path_string;
12125 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12126 gint retval = FALSE;
12129 g_assert (tree_view->priv->focus_column);
12131 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12134 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12135 if (cursor_node < 0)
12138 path_string = gtk_tree_path_to_string (cursor_path);
12139 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12141 pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12142 tree_view->priv->model,
12144 pspp_sheet_view_get_background_area (tree_view,
12146 tree_view->priv->focus_column,
12148 pspp_sheet_view_get_cell_area (tree_view,
12150 tree_view->priv->focus_column,
12153 if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12162 if (editable_widget != NULL)
12166 GtkCellRenderer *cell;
12169 cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12171 _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12174 area.width -= right + left;
12176 pspp_sheet_view_real_start_editing (tree_view,
12177 tree_view->priv->focus_column,
12186 g_free (path_string);
12191 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12192 GdkEventButton *event,
12193 PsppSheetView *sheet_view)
12197 node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12198 "pspp-sheet-view-node"));
12199 return pspp_sheet_view_row_head_clicked (sheet_view,
12201 sheet_view->priv->edited_column,
12206 pspp_sheet_view_editable_clicked (GtkButton *button,
12207 PsppSheetView *sheet_view)
12209 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12214 is_all_selected (GtkWidget *widget)
12216 GtkEntryBuffer *buffer;
12217 gint start_pos, end_pos;
12219 if (!GTK_IS_ENTRY (widget))
12222 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12223 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12224 &start_pos, &end_pos)
12226 && end_pos == gtk_entry_buffer_get_length (buffer));
12230 is_at_left (GtkWidget *widget)
12232 return (GTK_IS_ENTRY (widget)
12233 && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12237 is_at_right (GtkWidget *widget)
12239 GtkEntryBuffer *buffer;
12242 if (!GTK_IS_ENTRY (widget))
12245 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12246 length = gtk_entry_buffer_get_length (buffer);
12247 return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12251 pspp_sheet_view_event (GtkWidget *widget,
12252 GdkEventKey *event,
12253 PsppSheetView *tree_view)
12255 PsppSheetViewColumn *column;
12263 /* Intercept only key press events.
12264 It would make sense to use "key-press-event" instead of "event", but
12265 GtkEntry attaches its own signal handler to "key-press-event" that runs
12266 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12268 if (event->type != GDK_KEY_PRESS)
12271 if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12273 /* Pass through most keys that include modifiers. */
12274 if ((event->keyval == GDK_Tab || event->keyval == GDK_ISO_Left_Tab)
12275 && !(event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
12277 /* Special case for Shift-Tab. */
12283 keyval = event->keyval;
12284 state = event->state & ~(GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK);
12286 switch (event->keyval)
12288 case GDK_Left: case GDK_KP_Left:
12289 if (!is_all_selected (widget) && !is_at_left (widget))
12293 case GDK_Right: case GDK_KP_Right:
12294 if (!is_all_selected (widget) && !is_at_right (widget))
12298 case GDK_Up: case GDK_KP_Up:
12299 case GDK_Down: case GDK_KP_Down:
12302 case GDK_Page_Up: case GDK_KP_Page_Up:
12303 case GDK_Page_Down: case GDK_KP_Page_Down:
12314 case GDK_Tab: case GDK_KP_Tab:
12315 case GDK_ISO_Left_Tab:
12317 state |= event->state & GDK_SHIFT_MASK;
12324 row = tree_view->priv->edited_row;
12325 column = tree_view->priv->edited_column;
12326 path = gtk_tree_path_new_from_indices (row, -1);
12328 pspp_sheet_view_stop_editing (tree_view, cancel);
12329 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12331 pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12332 gtk_tree_path_free (path);
12334 handled = gtk_binding_set_activate (edit_bindings, keyval, state,
12335 GTK_OBJECT (tree_view));
12337 g_signal_stop_emission_by_name (widget, "event");
12339 pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12340 pspp_sheet_view_start_editing (tree_view, path);
12341 gtk_tree_path_free (path);
12347 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12350 PsppSheetView *sheet_view = data;
12352 g_signal_connect (widget, "event",
12353 G_CALLBACK (pspp_sheet_view_event),
12356 if (GTK_IS_CONTAINER (widget))
12357 gtk_container_foreach (GTK_CONTAINER (widget),
12358 pspp_sheet_view_override_cell_keypresses,
12363 pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
12364 PsppSheetViewColumn *column,
12366 GtkCellEditable *cell_editable,
12367 GdkRectangle *cell_area,
12371 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12372 gint pre_val = tree_view->priv->vadjustment->value;
12373 GtkRequisition requisition;
12376 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12378 tree_view->priv->edited_column = column;
12379 _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12381 row = gtk_tree_path_get_indices (path)[0];
12382 tree_view->priv->edited_row = row;
12383 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE);
12384 cell_area->y += pre_val - (int)tree_view->priv->vadjustment->value;
12386 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12387 pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12388 tree_view->priv->anchor_column = column;
12390 gtk_widget_size_request (GTK_WIDGET (cell_editable), &requisition);
12392 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12394 if (requisition.height < cell_area->height)
12396 gint diff = cell_area->height - requisition.height;
12397 pspp_sheet_view_put (tree_view,
12398 GTK_WIDGET (cell_editable),
12399 cell_area->x, cell_area->y + diff/2,
12400 cell_area->width, requisition.height);
12404 pspp_sheet_view_put (tree_view,
12405 GTK_WIDGET (cell_editable),
12406 cell_area->x, cell_area->y,
12407 cell_area->width, cell_area->height);
12410 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12411 (GdkEvent *)event);
12413 gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12414 g_signal_connect (cell_editable, "remove-widget",
12415 G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12416 if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12417 GTK_IS_BUTTON (cell_editable))
12419 g_signal_connect (cell_editable, "button-press-event",
12420 G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12422 g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12423 GINT_TO_POINTER (row));
12424 g_signal_connect (cell_editable, "clicked",
12425 G_CALLBACK (pspp_sheet_view_editable_clicked),
12429 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12434 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12435 gboolean cancel_editing)
12437 PsppSheetViewColumn *column;
12438 GtkCellRenderer *cell;
12440 if (tree_view->priv->edited_column == NULL)
12444 * This is very evil. We need to do this, because
12445 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12446 * later on. If pspp_sheet_view_row_changed notices
12447 * tree_view->priv->edited_column != NULL, it'll call
12448 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12450 * Please read that again if you intend to modify anything here.
12453 column = tree_view->priv->edited_column;
12454 tree_view->priv->edited_column = NULL;
12456 cell = _pspp_sheet_view_column_get_edited_cell (column);
12457 gtk_cell_renderer_stop_editing (cell, cancel_editing);
12459 if (!cancel_editing)
12460 gtk_cell_editable_editing_done (column->editable_widget);
12462 tree_view->priv->edited_column = column;
12464 gtk_cell_editable_remove_widget (column->editable_widget);
12469 * pspp_sheet_view_set_hover_selection:
12470 * @tree_view: a #PsppSheetView
12471 * @hover: %TRUE to enable hover selection mode
12473 * Enables of disables the hover selection mode of @tree_view.
12474 * Hover selection makes the selected row follow the pointer.
12475 * Currently, this works only for the selection modes
12476 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12481 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12484 hover = hover != FALSE;
12486 if (hover != tree_view->priv->hover_selection)
12488 tree_view->priv->hover_selection = hover;
12490 g_object_notify (G_OBJECT (tree_view), "hover-selection");
12495 * pspp_sheet_view_get_hover_selection:
12496 * @tree_view: a #PsppSheetView
12498 * Returns whether hover selection mode is turned on for @tree_view.
12500 * Return value: %TRUE if @tree_view is in hover selection mode
12505 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12507 return tree_view->priv->hover_selection;
12511 * pspp_sheet_view_set_rubber_banding:
12512 * @tree_view: a #PsppSheetView
12513 * @enable: %TRUE to enable rubber banding
12515 * Enables or disables rubber banding in @tree_view. If the selection mode is
12516 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12517 * banding will allow the user to select multiple rows by dragging the mouse.
12522 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12525 enable = enable != FALSE;
12527 if (enable != tree_view->priv->rubber_banding_enable)
12529 tree_view->priv->rubber_banding_enable = enable;
12531 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12536 * pspp_sheet_view_get_rubber_banding:
12537 * @tree_view: a #PsppSheetView
12539 * Returns whether rubber banding is turned on for @tree_view. If the
12540 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12541 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12542 * select multiple rows by dragging the mouse.
12544 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12549 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12551 return tree_view->priv->rubber_banding_enable;
12555 * pspp_sheet_view_is_rubber_banding_active:
12556 * @tree_view: a #PsppSheetView
12558 * Returns whether a rubber banding operation is currently being done
12561 * Return value: %TRUE if a rubber banding operation is currently being
12562 * done in @tree_view.
12567 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12569 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12571 if (tree_view->priv->rubber_banding_enable
12572 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12579 pspp_sheet_view_grab_notify (GtkWidget *widget,
12580 gboolean was_grabbed)
12582 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12584 tree_view->priv->in_grab = !was_grabbed;
12588 tree_view->priv->pressed_button = -1;
12590 if (tree_view->priv->rubber_band_status)
12591 pspp_sheet_view_stop_rubber_band (tree_view);
12596 pspp_sheet_view_state_changed (GtkWidget *widget,
12597 GtkStateType previous_state)
12599 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12601 if (gtk_widget_get_realized (widget))
12603 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
12604 gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
12607 gtk_widget_queue_draw (widget);
12611 * pspp_sheet_view_get_grid_lines:
12612 * @tree_view: a #PsppSheetView
12614 * Returns which grid lines are enabled in @tree_view.
12616 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12621 PsppSheetViewGridLines
12622 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12624 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12626 return tree_view->priv->grid_lines;
12630 * pspp_sheet_view_set_grid_lines:
12631 * @tree_view: a #PsppSheetView
12632 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12635 * Sets which grid lines to draw in @tree_view.
12640 pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view,
12641 PsppSheetViewGridLines grid_lines)
12643 PsppSheetViewPrivate *priv;
12644 PsppSheetViewGridLines old_grid_lines;
12646 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12648 priv = tree_view->priv;
12650 old_grid_lines = priv->grid_lines;
12651 priv->grid_lines = grid_lines;
12653 if (old_grid_lines != grid_lines)
12655 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12657 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12662 * pspp_sheet_view_get_special_cells:
12663 * @tree_view: a #PsppSheetView
12665 * Returns which grid lines are enabled in @tree_view.
12667 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12668 * the sheet view contain special cells.
12670 PsppSheetViewSpecialCells
12671 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12673 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12675 return tree_view->priv->special_cells;
12679 * pspp_sheet_view_set_special_cells:
12680 * @tree_view: a #PsppSheetView
12681 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12682 * the sheet view contain special cells.
12684 * Sets whether rows in the sheet view contain special cells, controlling the
12685 * rendering of row selections.
12688 pspp_sheet_view_set_special_cells (PsppSheetView *tree_view,
12689 PsppSheetViewSpecialCells special_cells)
12691 PsppSheetViewPrivate *priv;
12693 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12695 priv = tree_view->priv;
12697 if (priv->special_cells != special_cells)
12699 priv->special_cells = special_cells;
12700 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12701 g_object_notify (G_OBJECT (tree_view), "special-cells");
12706 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12708 /* XXX (re)calculate fixed_height if necessary */
12709 return tree_view->priv->fixed_height;
12713 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
12716 g_return_if_fail (fixed_height > 0);
12718 if (tree_view->priv->fixed_height != fixed_height)
12720 tree_view->priv->fixed_height = fixed_height;
12721 g_object_notify (G_OBJECT (tree_view), "fixed-height");
12723 if (!tree_view->priv->fixed_height_set)
12725 tree_view->priv->fixed_height_set = TRUE;
12726 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
12731 * pspp_sheet_view_set_tooltip_row:
12732 * @tree_view: a #PsppSheetView
12733 * @tooltip: a #GtkTooltip
12734 * @path: a #GtkTreePath
12736 * Sets the tip area of @tooltip to be the area covered by the row at @path.
12737 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12738 * See also gtk_tooltip_set_tip_area().
12743 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
12744 GtkTooltip *tooltip,
12747 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12748 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12750 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
12754 * pspp_sheet_view_set_tooltip_cell:
12755 * @tree_view: a #PsppSheetView
12756 * @tooltip: a #GtkTooltip
12757 * @path: (allow-none): a #GtkTreePath or %NULL
12758 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
12759 * @cell: (allow-none): a #GtkCellRenderer or %NULL
12761 * Sets the tip area of @tooltip to the area @path, @column and @cell have
12762 * in common. For example if @path is %NULL and @column is set, the tip
12763 * area will be set to the full area covered by @column. See also
12764 * gtk_tooltip_set_tip_area().
12766 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12771 pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view,
12772 GtkTooltip *tooltip,
12774 PsppSheetViewColumn *column,
12775 GtkCellRenderer *cell)
12779 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12780 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12781 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
12782 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
12784 /* Determine x values. */
12785 if (column && cell)
12790 pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
12791 pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
12793 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12796 rect.width = width;
12802 pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
12803 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12806 rect.width = tmp.width;
12811 rect.width = GTK_WIDGET (tree_view)->allocation.width;
12814 /* Determine y values. */
12819 pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
12820 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12823 rect.height = tmp.height;
12828 rect.height = tree_view->priv->vadjustment->page_size;
12831 gtk_tooltip_set_tip_area (tooltip, &rect);
12835 * pspp_sheet_view_get_tooltip_context:
12836 * @tree_view: a #PsppSheetView
12837 * @x: the x coordinate (relative to widget coordinates)
12838 * @y: the y coordinate (relative to widget coordinates)
12839 * @keyboard_tip: whether this is a keyboard tooltip or not
12840 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
12841 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
12842 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
12844 * This function is supposed to be used in a #GtkWidget::query-tooltip
12845 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
12846 * which are received in the signal handler, should be passed to this
12847 * function without modification.
12849 * The return value indicates whether there is a tree view row at the given
12850 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
12851 * tooltips the row returned will be the cursor row. When %TRUE, then any of
12852 * @model, @path and @iter which have been provided will be set to point to
12853 * that row and the corresponding model. @x and @y will always be converted
12854 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
12856 * Return value: whether or not the given tooltip context points to a row.
12861 pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view,
12864 gboolean keyboard_tip,
12865 GtkTreeModel **model,
12866 GtkTreePath **path,
12869 GtkTreePath *tmppath = NULL;
12871 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12872 g_return_val_if_fail (x != NULL, FALSE);
12873 g_return_val_if_fail (y != NULL, FALSE);
12877 pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
12884 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
12887 if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
12888 &tmppath, NULL, NULL, NULL))
12893 *model = pspp_sheet_view_get_model (tree_view);
12896 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
12902 gtk_tree_path_free (tmppath);
12908 pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget,
12911 gboolean keyboard_tip,
12912 GtkTooltip *tooltip,
12915 GValue value = { 0, };
12916 GValue transformed = { 0, };
12919 GtkTreeModel *model;
12920 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12922 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
12925 &model, &path, &iter))
12928 gtk_tree_model_get_value (model, &iter,
12929 tree_view->priv->tooltip_column, &value);
12931 g_value_init (&transformed, G_TYPE_STRING);
12933 if (!g_value_transform (&value, &transformed))
12935 g_value_unset (&value);
12936 gtk_tree_path_free (path);
12941 g_value_unset (&value);
12943 if (!g_value_get_string (&transformed))
12945 g_value_unset (&transformed);
12946 gtk_tree_path_free (path);
12951 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
12952 pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
12954 gtk_tree_path_free (path);
12955 g_value_unset (&transformed);
12961 * pspp_sheet_view_set_tooltip_column:
12962 * @tree_view: a #PsppSheetView
12963 * @column: an integer, which is a valid column number for @tree_view's model
12965 * If you only plan to have simple (text-only) tooltips on full rows, you
12966 * can use this function to have #PsppSheetView handle these automatically
12967 * for you. @column should be set to the column in @tree_view's model
12968 * containing the tooltip texts, or -1 to disable this feature.
12970 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
12971 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
12973 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
12974 * so &, <, etc have to be escaped in the text.
12979 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
12982 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12984 if (column == tree_view->priv->tooltip_column)
12989 g_signal_handlers_disconnect_by_func (tree_view,
12990 pspp_sheet_view_set_tooltip_query_cb,
12992 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
12996 if (tree_view->priv->tooltip_column == -1)
12998 g_signal_connect (tree_view, "query-tooltip",
12999 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13000 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13004 tree_view->priv->tooltip_column = column;
13005 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13009 * pspp_sheet_view_get_tooltip_column:
13010 * @tree_view: a #PsppSheetView
13012 * Returns the column of @tree_view's model which is being used for
13013 * displaying tooltips on @tree_view's rows.
13015 * Return value: the index of the tooltip column that is currently being
13016 * used, or -1 if this is disabled.
13021 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13023 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13025 return tree_view->priv->tooltip_column;
13029 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13030 GValue *return_accu,
13031 const GValue *handler_return,
13034 gboolean continue_emission;
13035 gboolean signal_handled;
13037 signal_handled = g_value_get_boolean (handler_return);
13038 g_value_set_boolean (return_accu, signal_handled);
13039 continue_emission = !signal_handled;
13041 return continue_emission;
13045 pspp_sheet_view_grid_lines_get_type (void)
13047 static GType etype = 0;
13048 if (G_UNLIKELY(etype == 0)) {
13049 static const GEnumValue values[] = {
13050 { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13051 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13052 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13053 { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13056 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13062 pspp_sheet_view_special_cells_get_type (void)
13064 static GType etype = 0;
13065 if (G_UNLIKELY(etype == 0)) {
13066 static const GEnumValue values[] = {
13067 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13068 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13069 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13072 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);