1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012, 2013, 2015 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>
43 #include <gdk/gdkkeysyms-compat.h>
46 #include "ui/gui/psppire-marshal.h"
47 #include "ui/gui/pspp-sheet-selection.h"
49 #define P_(STRING) STRING
50 #define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
51 #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
53 /* Many keyboard shortcuts for Mac are the same as for X
54 * except they use Command key instead of Control (e.g. Cut,
55 * Copy, Paste). This symbol is for those simple cases. */
56 #ifndef GDK_WINDOWING_QUARTZ
57 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_CONTROL_MASK
59 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_META_MASK
62 #define PSPP_SHEET_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
63 #define PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC (PSPP_SHEET_VIEW_PRIORITY_VALIDATE + 2)
64 #define PSPP_SHEET_VIEW_TIME_MS_PER_IDLE 30
65 #define SCROLL_EDGE_SIZE 15
66 #define EXPANDER_EXTRA_PADDING 4
67 #define PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT 5000
69 /* The "background" areas of all rows/cells add up to cover the entire tree.
70 * The background includes all inter-row and inter-cell spacing.
71 * The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
72 * i.e. just the cells, no spacing.
75 #define BACKGROUND_HEIGHT(tree_view) (tree_view->priv->fixed_height)
76 #define CELL_HEIGHT(tree_view, separator) ((BACKGROUND_HEIGHT (tree_view)) - (separator))
78 /* Translate from bin_window coordinates to rbtree (tree coordinates) and
81 #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->priv->dy)
82 #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->priv->dy)
84 /* This is in bin_window coordinates */
85 #define BACKGROUND_FIRST_PIXEL(tree_view,node) (RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, pspp_sheet_view_node_find_offset (tree_view, (node))))
86 #define CELL_FIRST_PIXEL(tree_view,node,separator) (BACKGROUND_FIRST_PIXEL (tree_view,node) + separator/2)
88 #define ROW_HEIGHT(tree_view) \
89 ((tree_view->priv->fixed_height > 0) ? (tree_view->priv->fixed_height) : (tree_view)->priv->expander_size)
92 typedef struct _PsppSheetViewChild PsppSheetViewChild;
93 struct _PsppSheetViewChild
96 PsppSheetViewColumn *column;
101 typedef struct _TreeViewDragInfo TreeViewDragInfo;
102 struct _TreeViewDragInfo
104 GdkModifierType start_button_mask;
105 GtkTargetList *_unused_source_target_list;
106 GdkDragAction source_actions;
108 GtkTargetList *_unused_dest_target_list;
110 guint source_set : 1;
126 START_INTERACTIVE_SEARCH,
138 PROP_HEADERS_VISIBLE,
139 PROP_HEADERS_CLICKABLE,
144 PROP_HOVER_SELECTION,
146 PROP_ENABLE_GRID_LINES,
150 PROP_FIXED_HEIGHT_SET
154 static void pspp_sheet_view_finalize (GObject *object);
155 static void pspp_sheet_view_set_property (GObject *object,
159 static void pspp_sheet_view_get_property (GObject *object,
164 static void pspp_sheet_view_dispose (GObject *object);
166 /* gtkwidget signals */
167 static void pspp_sheet_view_realize (GtkWidget *widget);
168 static void pspp_sheet_view_unrealize (GtkWidget *widget);
169 static void pspp_sheet_view_map (GtkWidget *widget);
170 static void pspp_sheet_view_size_request (GtkWidget *widget,
171 GtkRequisition *requisition);
172 static void pspp_sheet_view_size_allocate (GtkWidget *widget,
173 GtkAllocation *allocation);
174 static gboolean pspp_sheet_view_draw (GtkWidget *widget,
176 static gboolean pspp_sheet_view_key_press (GtkWidget *widget,
178 static gboolean pspp_sheet_view_key_release (GtkWidget *widget,
180 static gboolean pspp_sheet_view_motion (GtkWidget *widget,
181 GdkEventMotion *event);
182 static gboolean pspp_sheet_view_enter_notify (GtkWidget *widget,
183 GdkEventCrossing *event);
184 static gboolean pspp_sheet_view_leave_notify (GtkWidget *widget,
185 GdkEventCrossing *event);
186 static gboolean pspp_sheet_view_button_press (GtkWidget *widget,
187 GdkEventButton *event);
188 static gboolean pspp_sheet_view_button_release (GtkWidget *widget,
189 GdkEventButton *event);
190 static gboolean pspp_sheet_view_grab_broken (GtkWidget *widget,
191 GdkEventGrabBroken *event);
193 static void pspp_sheet_view_set_focus_child (GtkContainer *container,
195 static gint pspp_sheet_view_focus_out (GtkWidget *widget,
196 GdkEventFocus *event);
197 static gint pspp_sheet_view_focus (GtkWidget *widget,
198 GtkDirectionType direction);
199 static void pspp_sheet_view_grab_focus (GtkWidget *widget);
200 static void pspp_sheet_view_style_updated (GtkWidget *widget);
201 static void pspp_sheet_view_grab_notify (GtkWidget *widget,
202 gboolean was_grabbed);
203 static void pspp_sheet_view_state_changed (GtkWidget *widget,
204 GtkStateType previous_state);
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 PsppSheetSelectMode mode);
261 static gboolean pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view);
262 static void pspp_sheet_view_row_changed (GtkTreeModel *model,
266 static void pspp_sheet_view_row_inserted (GtkTreeModel *model,
270 static void pspp_sheet_view_row_deleted (GtkTreeModel *model,
273 static void pspp_sheet_view_rows_reordered (GtkTreeModel *model,
279 /* Incremental reflow */
280 static gint validate_row (PsppSheetView *tree_view,
284 static void validate_visible_area (PsppSheetView *tree_view);
285 static gboolean validate_rows_handler (PsppSheetView *tree_view);
286 static gboolean presize_handler_callback (gpointer data);
287 static void install_presize_handler (PsppSheetView *tree_view);
288 static void install_scroll_sync_handler (PsppSheetView *tree_view);
289 static void pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
292 static void pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view);
293 static void pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view);
294 static void invalidate_empty_focus (PsppSheetView *tree_view);
296 /* Internal functions */
297 static GtkAdjustment *pspp_sheet_view_do_get_hadjustment (PsppSheetView *);
298 static GtkAdjustment *pspp_sheet_view_do_get_vadjustment (PsppSheetView *);
299 static void pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
300 GtkAdjustment *adjustment);
301 static void pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
302 GtkAdjustment *adjustment);
303 static void pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
306 gboolean add_shifted_binding,
307 GtkMovementStep step,
309 static void pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
311 const GdkRectangle *clip_rect);
312 static gint pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
315 static void pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
316 PsppSheetView *tree_view);
317 static void pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
319 static void pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
320 PsppSheetViewColumn *column,
321 gboolean focus_to_cell);
322 static gboolean pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
323 GdkEventMotion *event);
324 static void pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view);
325 static gboolean pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
327 PsppSheetSelectMode mode);
328 static void pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
330 PsppSheetSelectMode mode);
331 static void pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
333 PsppSheetSelectMode mode);
334 static void pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
336 PsppSheetSelectMode mode);
337 static void pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
339 static void pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
341 PsppSheetSelectMode mode);
342 static void pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
344 gboolean clear_and_select,
346 PsppSheetSelectMode mode);
347 static gboolean pspp_sheet_view_has_special_cell (PsppSheetView *tree_view);
348 static void pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view);
349 static void update_prelight (PsppSheetView *tree_view,
352 static void initialize_fixed_height_mode (PsppSheetView *tree_view);
354 /* interactive search */
355 static void pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view);
356 static void pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
357 PsppSheetView *tree_view);
358 static void pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
359 GtkWidget *search_dialog,
361 static void pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
365 static void pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
366 PsppSheetView *tree_view);
368 static void pspp_sheet_view_search_activate (GtkEntry *entry,
369 PsppSheetView *tree_view);
370 static gboolean pspp_sheet_view_real_search_enable_popdown(gpointer data);
371 static void pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
373 static gboolean pspp_sheet_view_search_delete_event (GtkWidget *widget,
375 PsppSheetView *tree_view);
376 static gboolean pspp_sheet_view_search_button_press_event (GtkWidget *widget,
377 GdkEventButton *event,
378 PsppSheetView *tree_view);
379 static gboolean pspp_sheet_view_search_scroll_event (GtkWidget *entry,
380 GdkEventScroll *event,
381 PsppSheetView *tree_view);
382 static gboolean pspp_sheet_view_search_key_press_event (GtkWidget *entry,
384 PsppSheetView *tree_view);
385 static gboolean pspp_sheet_view_search_move (GtkWidget *window,
386 PsppSheetView *tree_view,
388 static gboolean pspp_sheet_view_search_equal_func (GtkTreeModel *model,
392 gpointer search_data);
393 static gboolean pspp_sheet_view_search_iter (GtkTreeModel *model,
394 PsppSheetSelection *selection,
399 static void pspp_sheet_view_search_init (GtkWidget *entry,
400 PsppSheetView *tree_view);
401 static void pspp_sheet_view_put (PsppSheetView *tree_view,
402 GtkWidget *child_widget,
404 PsppSheetViewColumn *column);
405 static gboolean pspp_sheet_view_start_editing (PsppSheetView *tree_view,
406 GtkTreePath *cursor_path);
407 static gboolean pspp_sheet_view_editable_button_press_event (GtkWidget *,
410 static void pspp_sheet_view_editable_clicked (GtkButton *, PsppSheetView *);
411 static void pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
412 PsppSheetViewColumn *column,
414 GtkCellEditable *cell_editable,
415 GdkRectangle *cell_area,
418 static gboolean pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
419 gboolean keybinding);
420 static gboolean pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view);
421 static PsppSheetViewColumn *pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
422 PsppSheetViewColumn *column,
425 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
426 PsppSheetViewColumn *column,
427 const GdkRectangle *background_area,
428 gboolean subtract_focus_rect,
429 GdkRectangle *cell_area);
430 static gint pspp_sheet_view_find_offset (PsppSheetView *tree_view,
435 static void pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
439 static void pspp_sheet_view_buildable_init (GtkBuildableIface *iface);
442 static gboolean scroll_row_timeout (gpointer data);
443 static void add_scroll_timeout (PsppSheetView *tree_view);
444 static void remove_scroll_timeout (PsppSheetView *tree_view);
446 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
448 static GtkBindingSet *edit_bindings;
455 G_DEFINE_TYPE_WITH_CODE (PsppSheetView, pspp_sheet_view, GTK_TYPE_CONTAINER,
456 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
457 pspp_sheet_view_buildable_init)
458 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
461 pspp_sheet_view_get_preferred_width (GtkWidget *widget,
465 GtkRequisition requisition;
467 pspp_sheet_view_size_request (widget, &requisition);
469 *minimal_width = *natural_width = requisition.width;
473 pspp_sheet_view_get_preferred_height (GtkWidget *widget,
474 gint *minimal_height,
475 gint *natural_height)
477 GtkRequisition requisition;
479 pspp_sheet_view_size_request (widget, &requisition);
481 *minimal_height = *natural_height = requisition.height;
485 pspp_sheet_view_class_init (PsppSheetViewClass *class)
487 GObjectClass *o_class;
488 GtkWidgetClass *widget_class;
489 GtkContainerClass *container_class;
490 GtkBindingSet *binding_set[2];
493 binding_set[0] = gtk_binding_set_by_class (class);
495 binding_set[1] = gtk_binding_set_new ("PsppSheetViewEditing");
496 edit_bindings = binding_set[1];
498 o_class = (GObjectClass *) class;
499 widget_class = (GtkWidgetClass *) class;
500 container_class = (GtkContainerClass *) class;
502 /* GObject signals */
503 o_class->set_property = pspp_sheet_view_set_property;
504 o_class->get_property = pspp_sheet_view_get_property;
505 o_class->finalize = pspp_sheet_view_finalize;
506 o_class->dispose = pspp_sheet_view_dispose;
508 /* GtkWidget signals */
509 widget_class->map = pspp_sheet_view_map;
510 widget_class->realize = pspp_sheet_view_realize;
511 widget_class->unrealize = pspp_sheet_view_unrealize;
512 widget_class->get_preferred_width = pspp_sheet_view_get_preferred_width;
513 widget_class->get_preferred_height = pspp_sheet_view_get_preferred_height;
514 widget_class->size_allocate = pspp_sheet_view_size_allocate;
515 widget_class->button_press_event = pspp_sheet_view_button_press;
516 widget_class->button_release_event = pspp_sheet_view_button_release;
517 widget_class->grab_broken_event = pspp_sheet_view_grab_broken;
518 /*widget_class->configure_event = pspp_sheet_view_configure;*/
519 widget_class->motion_notify_event = pspp_sheet_view_motion;
520 widget_class->draw = pspp_sheet_view_draw;
521 widget_class->key_press_event = pspp_sheet_view_key_press;
522 widget_class->key_release_event = pspp_sheet_view_key_release;
523 widget_class->enter_notify_event = pspp_sheet_view_enter_notify;
524 widget_class->leave_notify_event = pspp_sheet_view_leave_notify;
525 widget_class->focus_out_event = pspp_sheet_view_focus_out;
526 widget_class->drag_begin = pspp_sheet_view_drag_begin;
527 widget_class->drag_end = pspp_sheet_view_drag_end;
528 widget_class->drag_data_get = pspp_sheet_view_drag_data_get;
529 widget_class->drag_data_delete = pspp_sheet_view_drag_data_delete;
530 widget_class->drag_leave = pspp_sheet_view_drag_leave;
531 widget_class->drag_motion = pspp_sheet_view_drag_motion;
532 widget_class->drag_drop = pspp_sheet_view_drag_drop;
533 widget_class->drag_data_received = pspp_sheet_view_drag_data_received;
534 widget_class->focus = pspp_sheet_view_focus;
535 widget_class->grab_focus = pspp_sheet_view_grab_focus;
536 widget_class->style_updated = pspp_sheet_view_style_updated;
537 widget_class->grab_notify = pspp_sheet_view_grab_notify;
538 widget_class->state_changed = pspp_sheet_view_state_changed;
540 /* GtkContainer signals */
541 container_class->remove = pspp_sheet_view_remove;
542 container_class->forall = pspp_sheet_view_forall;
543 container_class->set_focus_child = pspp_sheet_view_set_focus_child;
545 class->set_scroll_adjustments = pspp_sheet_view_set_adjustments;
546 class->move_cursor = pspp_sheet_view_real_move_cursor;
547 class->select_all = pspp_sheet_view_real_select_all;
548 class->unselect_all = pspp_sheet_view_real_unselect_all;
549 class->select_cursor_row = pspp_sheet_view_real_select_cursor_row;
550 class->toggle_cursor_row = pspp_sheet_view_real_toggle_cursor_row;
551 class->start_interactive_search = pspp_sheet_view_start_interactive_search;
555 g_object_class_install_property (o_class,
557 g_param_spec_object ("model",
558 P_("TreeView Model"),
559 P_("The model for the tree view"),
561 GTK_PARAM_READWRITE));
563 g_object_class_override_property (o_class, PROP_HADJUSTMENT, "hadjustment");
564 g_object_class_override_property (o_class, PROP_VADJUSTMENT, "vadjustment");
565 g_object_class_override_property (o_class, PROP_HSCROLL_POLICY, "hscroll-policy");
566 g_object_class_override_property (o_class, PROP_VSCROLL_POLICY, "vscroll-policy");
568 g_object_class_install_property (o_class,
569 PROP_HEADERS_VISIBLE,
570 g_param_spec_boolean ("headers-visible",
571 P_("Headers Visible"),
572 P_("Show the column header buttons"),
574 GTK_PARAM_READWRITE));
576 g_object_class_install_property (o_class,
577 PROP_HEADERS_CLICKABLE,
578 g_param_spec_boolean ("headers-clickable",
579 P_("Headers Clickable"),
580 P_("Column headers respond to click events"),
582 GTK_PARAM_READWRITE));
584 g_object_class_install_property (o_class,
586 g_param_spec_boolean ("reorderable",
588 P_("View is reorderable"),
590 GTK_PARAM_READWRITE));
592 g_object_class_install_property (o_class,
594 g_param_spec_boolean ("rules-hint",
596 P_("Set a hint to the theme engine to draw rows in alternating colors"),
598 GTK_PARAM_READWRITE));
600 g_object_class_install_property (o_class,
602 g_param_spec_boolean ("enable-search",
604 P_("View allows user to search through columns interactively"),
606 GTK_PARAM_READWRITE));
608 g_object_class_install_property (o_class,
610 g_param_spec_int ("search-column",
612 P_("Model column to search through during interactive search"),
616 GTK_PARAM_READWRITE));
619 * PsppSheetView:hover-selection:
621 * Enables of disables the hover selection mode of @tree_view.
622 * Hover selection makes the selected row follow the pointer.
623 * Currently, this works only for the selection modes
624 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
626 * This mode is primarily intended for treeviews in popups, e.g.
627 * in #GtkComboBox or #GtkEntryCompletion.
631 g_object_class_install_property (o_class,
632 PROP_HOVER_SELECTION,
633 g_param_spec_boolean ("hover-selection",
634 P_("Hover Selection"),
635 P_("Whether the selection should follow the pointer"),
637 GTK_PARAM_READWRITE));
639 g_object_class_install_property (o_class,
641 g_param_spec_boolean ("rubber-banding",
642 P_("Rubber Banding"),
643 P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
645 GTK_PARAM_READWRITE));
647 g_object_class_install_property (o_class,
648 PROP_ENABLE_GRID_LINES,
649 g_param_spec_enum ("enable-grid-lines",
650 P_("Enable Grid Lines"),
651 P_("Whether grid lines should be drawn in the tree view"),
652 PSPP_TYPE_SHEET_VIEW_GRID_LINES,
653 PSPP_SHEET_VIEW_GRID_LINES_NONE,
654 GTK_PARAM_READWRITE));
656 g_object_class_install_property (o_class,
658 g_param_spec_int ("tooltip-column",
659 P_("Tooltip Column"),
660 P_("The column in the model containing the tooltip texts for the rows"),
664 GTK_PARAM_READWRITE));
666 g_object_class_install_property (o_class,
668 g_param_spec_enum ("special-cells",
670 P_("Whether rows have special cells."),
671 PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS,
672 PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT,
673 GTK_PARAM_READWRITE));
675 g_object_class_install_property (o_class,
677 g_param_spec_int ("fixed-height",
679 P_("Height of a single row. Normally the height of a row is determined automatically. Writing this property sets fixed-height-set to true, preventing this property's value from changing."),
683 GTK_PARAM_READWRITE));
685 g_object_class_install_property (o_class,
686 PROP_FIXED_HEIGHT_SET,
687 g_param_spec_boolean ("fixed-height-set",
688 P_("Fixed Height Set"),
689 P_("Whether fixed-height was set externally."),
691 GTK_PARAM_READWRITE));
693 /* Style properties */
694 #define _TREE_VIEW_EXPANDER_SIZE 12
695 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
696 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
698 gtk_widget_class_install_style_property (widget_class,
699 g_param_spec_int ("expander-size",
701 P_("Size of the expander arrow"),
704 _TREE_VIEW_EXPANDER_SIZE,
705 GTK_PARAM_READABLE));
707 gtk_widget_class_install_style_property (widget_class,
708 g_param_spec_int ("vertical-separator",
709 P_("Vertical Separator Width"),
710 P_("Vertical space between cells. Must be an even number"),
713 _TREE_VIEW_VERTICAL_SEPARATOR,
714 GTK_PARAM_READABLE));
716 gtk_widget_class_install_style_property (widget_class,
717 g_param_spec_int ("horizontal-separator",
718 P_("Horizontal Separator Width"),
719 P_("Horizontal space between cells. Must be an even number"),
722 _TREE_VIEW_HORIZONTAL_SEPARATOR,
723 GTK_PARAM_READABLE));
725 gtk_widget_class_install_style_property (widget_class,
726 g_param_spec_boolean ("allow-rules",
728 P_("Allow drawing of alternating color rows"),
730 GTK_PARAM_READABLE));
732 gtk_widget_class_install_style_property (widget_class,
733 g_param_spec_boxed ("even-row-color",
734 P_("Even Row Color"),
735 P_("Color to use for even rows"),
737 GTK_PARAM_READABLE));
739 gtk_widget_class_install_style_property (widget_class,
740 g_param_spec_boxed ("odd-row-color",
742 P_("Color to use for odd rows"),
744 GTK_PARAM_READABLE));
746 gtk_widget_class_install_style_property (widget_class,
747 g_param_spec_boolean ("row-ending-details",
748 P_("Row Ending details"),
749 P_("Enable extended row background theming"),
751 GTK_PARAM_READABLE));
753 gtk_widget_class_install_style_property (widget_class,
754 g_param_spec_int ("grid-line-width",
755 P_("Grid line width"),
756 P_("Width, in pixels, of the tree view grid lines"),
758 GTK_PARAM_READABLE));
760 gtk_widget_class_install_style_property (widget_class,
761 g_param_spec_int ("tree-line-width",
762 P_("Tree line width"),
763 P_("Width, in pixels, of the tree view lines"),
765 GTK_PARAM_READABLE));
767 gtk_widget_class_install_style_property (widget_class,
768 g_param_spec_string ("tree-line-pattern",
769 P_("Tree line pattern"),
770 P_("Dash pattern used to draw the tree view lines"),
772 GTK_PARAM_READABLE));
777 * PsppSheetView::set-scroll-adjustments
778 * @horizontal: the horizontal #GtkAdjustment
779 * @vertical: the vertical #GtkAdjustment
781 * Set the scroll adjustments for the tree view. Usually scrolled containers
782 * like #GtkScrolledWindow will emit this signal to connect two instances
783 * of #GtkScrollbar to the scroll directions of the #PsppSheetView.
785 widget_class->set_scroll_adjustments_signal =
786 g_signal_new ("set-scroll-adjustments",
787 G_TYPE_FROM_CLASS (o_class),
788 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
789 G_STRUCT_OFFSET (PsppSheetViewClass, set_scroll_adjustments),
791 psppire_marshal_VOID__OBJECT_OBJECT,
794 GTK_TYPE_ADJUSTMENT);
798 * PsppSheetView::row-activated:
799 * @tree_view: the object on which the signal is emitted
800 * @path: the #GtkTreePath for the activated row
801 * @column: the #PsppSheetViewColumn in which the activation occurred
803 * The "row-activated" signal is emitted when the method
804 * pspp_sheet_view_row_activated() is called or the user double clicks
805 * a treeview row. It is also emitted when a non-editable row is
806 * selected and one of the keys: Space, Shift+Space, Return or
809 * For selection handling refer to the <link linkend="TreeWidget">tree
810 * widget conceptual overview</link> as well as #PsppSheetSelection.
812 tree_view_signals[ROW_ACTIVATED] =
813 g_signal_new ("row-activated",
814 G_TYPE_FROM_CLASS (o_class),
815 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
816 G_STRUCT_OFFSET (PsppSheetViewClass, row_activated),
818 psppire_marshal_VOID__BOXED_OBJECT,
821 PSPP_TYPE_SHEET_VIEW_COLUMN);
824 * PsppSheetView::columns-changed:
825 * @tree_view: the object on which the signal is emitted
827 * The number of columns of the treeview has changed.
829 tree_view_signals[COLUMNS_CHANGED] =
830 g_signal_new ("columns-changed",
831 G_TYPE_FROM_CLASS (o_class),
833 G_STRUCT_OFFSET (PsppSheetViewClass, columns_changed),
835 g_cclosure_marshal_VOID__VOID,
839 * PsppSheetView::cursor-changed:
840 * @tree_view: the object on which the signal is emitted
842 * The position of the cursor (focused cell) has changed.
844 tree_view_signals[CURSOR_CHANGED] =
845 g_signal_new ("cursor-changed",
846 G_TYPE_FROM_CLASS (o_class),
848 G_STRUCT_OFFSET (PsppSheetViewClass, cursor_changed),
850 g_cclosure_marshal_VOID__VOID,
853 tree_view_signals[MOVE_CURSOR] =
854 g_signal_new ("move-cursor",
855 G_TYPE_FROM_CLASS (o_class),
856 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
857 G_STRUCT_OFFSET (PsppSheetViewClass, move_cursor),
859 psppire_marshal_BOOLEAN__ENUM_INT,
861 GTK_TYPE_MOVEMENT_STEP,
864 tree_view_signals[SELECT_ALL] =
865 g_signal_new ("select-all",
866 G_TYPE_FROM_CLASS (o_class),
867 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
868 G_STRUCT_OFFSET (PsppSheetViewClass, select_all),
870 psppire_marshal_BOOLEAN__VOID,
873 tree_view_signals[UNSELECT_ALL] =
874 g_signal_new ("unselect-all",
875 G_TYPE_FROM_CLASS (o_class),
876 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
877 G_STRUCT_OFFSET (PsppSheetViewClass, unselect_all),
879 psppire_marshal_BOOLEAN__VOID,
882 tree_view_signals[SELECT_CURSOR_ROW] =
883 g_signal_new ("select-cursor-row",
884 G_TYPE_FROM_CLASS (o_class),
885 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
886 G_STRUCT_OFFSET (PsppSheetViewClass, select_cursor_row),
888 psppire_marshal_BOOLEAN__BOOLEAN,
890 G_TYPE_BOOLEAN, G_TYPE_INT);
892 tree_view_signals[TOGGLE_CURSOR_ROW] =
893 g_signal_new ("toggle-cursor-row",
894 G_TYPE_FROM_CLASS (o_class),
895 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
896 G_STRUCT_OFFSET (PsppSheetViewClass, toggle_cursor_row),
898 psppire_marshal_BOOLEAN__VOID,
901 tree_view_signals[START_INTERACTIVE_SEARCH] =
902 g_signal_new ("start-interactive-search",
903 G_TYPE_FROM_CLASS (o_class),
904 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
905 G_STRUCT_OFFSET (PsppSheetViewClass, start_interactive_search),
907 psppire_marshal_BOOLEAN__VOID,
911 for (i = 0; i < 2; i++)
913 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Up, 0, TRUE,
914 GTK_MOVEMENT_DISPLAY_LINES, -1);
915 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Up, 0, TRUE,
916 GTK_MOVEMENT_DISPLAY_LINES, -1);
918 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Down, 0, TRUE,
919 GTK_MOVEMENT_DISPLAY_LINES, 1);
920 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Down, 0, TRUE,
921 GTK_MOVEMENT_DISPLAY_LINES, 1);
923 pspp_sheet_view_add_move_binding (binding_set[i], GDK_p, GDK_CONTROL_MASK, FALSE,
924 GTK_MOVEMENT_DISPLAY_LINES, -1);
926 pspp_sheet_view_add_move_binding (binding_set[i], GDK_n, GDK_CONTROL_MASK, FALSE,
927 GTK_MOVEMENT_DISPLAY_LINES, 1);
929 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Home, 0, TRUE,
930 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
931 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Home, 0, TRUE,
932 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
934 pspp_sheet_view_add_move_binding (binding_set[i], GDK_End, 0, TRUE,
935 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
936 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_End, 0, TRUE,
937 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
939 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Up, 0, TRUE,
940 GTK_MOVEMENT_PAGES, -1);
941 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Up, 0, TRUE,
942 GTK_MOVEMENT_PAGES, -1);
944 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Down, 0, TRUE,
945 GTK_MOVEMENT_PAGES, 1);
946 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Down, 0, TRUE,
947 GTK_MOVEMENT_PAGES, 1);
950 gtk_binding_entry_add_signal (binding_set[i], GDK_Up, GDK_CONTROL_MASK, "move-cursor", 2,
951 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
954 gtk_binding_entry_add_signal (binding_set[i], GDK_Down, GDK_CONTROL_MASK, "move-cursor", 2,
955 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
958 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, 0, "move-cursor", 2,
959 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
962 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, 0, "move-cursor", 2,
963 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
966 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, 0, "move-cursor", 2,
967 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
970 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, GDK_SHIFT_MASK, "move-cursor", 2,
971 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
974 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, 0, "move-cursor", 2,
975 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
978 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, 0, "move-cursor", 2,
979 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
982 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, GDK_CONTROL_MASK,
984 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
987 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, GDK_CONTROL_MASK,
989 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
992 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, GDK_CONTROL_MASK,
994 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
997 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, GDK_CONTROL_MASK,
999 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1002 gtk_binding_entry_add_signal (binding_set[i], GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
1004 gtk_binding_entry_add_signal (binding_set[i], GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
1007 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1008 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1010 gtk_binding_entry_add_signal (binding_set[0], GDK_a, GDK_CONTROL_MASK, "select-all", 0);
1011 gtk_binding_entry_add_signal (binding_set[0], GDK_slash, GDK_CONTROL_MASK, "select-all", 0);
1013 gtk_binding_entry_add_signal (binding_set[0], GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
1014 gtk_binding_entry_add_signal (binding_set[0], GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0);
1016 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1017 G_TYPE_BOOLEAN, TRUE,
1018 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1019 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1020 G_TYPE_BOOLEAN, TRUE,
1021 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1023 gtk_binding_entry_add_signal (binding_set[0], GDK_space, 0, "select-cursor-row", 1,
1024 G_TYPE_BOOLEAN, TRUE,
1026 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, 0, "select-cursor-row", 1,
1027 G_TYPE_BOOLEAN, TRUE,
1029 gtk_binding_entry_add_signal (binding_set[0], GDK_Return, 0, "select-cursor-row", 1,
1030 G_TYPE_BOOLEAN, TRUE,
1032 gtk_binding_entry_add_signal (binding_set[0], GDK_ISO_Enter, 0, "select-cursor-row", 1,
1033 G_TYPE_BOOLEAN, TRUE,
1035 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Enter, 0, "select-cursor-row", 1,
1036 G_TYPE_BOOLEAN, TRUE,
1039 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, 0, "select-cursor-parent", 0);
1040 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0);
1042 g_type_class_add_private (o_class, sizeof (PsppSheetViewPrivate));
1046 pspp_sheet_view_buildable_init (GtkBuildableIface *iface)
1048 iface->add_child = pspp_sheet_view_buildable_add_child;
1052 pspp_sheet_view_init (PsppSheetView *tree_view)
1054 tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, PSPP_TYPE_SHEET_VIEW, PsppSheetViewPrivate);
1056 gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
1057 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE);
1059 tree_view->priv->flags = PSPP_SHEET_VIEW_DRAW_KEYFOCUS
1060 | PSPP_SHEET_VIEW_HEADERS_VISIBLE;
1062 /* We need some padding */
1063 tree_view->priv->selected = range_tower_create ();
1064 tree_view->priv->dy = 0;
1065 tree_view->priv->cursor_offset = 0;
1066 tree_view->priv->n_columns = 0;
1067 tree_view->priv->header_height = 1;
1068 tree_view->priv->x_drag = 0;
1069 tree_view->priv->drag_pos = -1;
1070 tree_view->priv->header_has_focus = FALSE;
1071 tree_view->priv->pressed_button = -1;
1072 tree_view->priv->press_start_x = -1;
1073 tree_view->priv->press_start_y = -1;
1074 tree_view->priv->reorderable = FALSE;
1075 tree_view->priv->presize_handler_timer = 0;
1076 tree_view->priv->scroll_sync_timer = 0;
1077 tree_view->priv->fixed_height = -1;
1078 tree_view->priv->fixed_height_set = FALSE;
1079 pspp_sheet_view_set_adjustments (tree_view, NULL, NULL);
1080 tree_view->priv->selection = _pspp_sheet_selection_new_with_tree_view (tree_view);
1081 tree_view->priv->enable_search = TRUE;
1082 tree_view->priv->search_column = -1;
1083 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
1084 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
1085 tree_view->priv->search_custom_entry_set = FALSE;
1086 tree_view->priv->typeselect_flush_timeout = 0;
1087 tree_view->priv->init_hadjust_value = TRUE;
1088 tree_view->priv->width = 0;
1090 tree_view->priv->hover_selection = FALSE;
1092 tree_view->priv->rubber_banding_enable = FALSE;
1094 tree_view->priv->grid_lines = PSPP_SHEET_VIEW_GRID_LINES_NONE;
1096 tree_view->priv->tooltip_column = -1;
1098 tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT;
1100 tree_view->priv->post_validation_flag = FALSE;
1102 tree_view->priv->last_button_x = -1;
1103 tree_view->priv->last_button_y = -1;
1105 tree_view->priv->event_last_x = -10000;
1106 tree_view->priv->event_last_y = -10000;
1108 tree_view->priv->prelight_node = -1;
1109 tree_view->priv->rubber_band_start_node = -1;
1110 tree_view->priv->rubber_band_end_node = -1;
1112 tree_view->priv->anchor_column = NULL;
1114 tree_view->priv->button_style = NULL;
1116 tree_view->dispose_has_run = FALSE;
1118 pspp_sheet_view_do_set_vadjustment (tree_view, NULL);
1119 pspp_sheet_view_do_set_hadjustment (tree_view, NULL);
1120 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (tree_view)),
1121 GTK_STYLE_CLASS_VIEW);
1130 pspp_sheet_view_set_property (GObject *object,
1132 const GValue *value,
1135 PsppSheetView *tree_view;
1137 tree_view = PSPP_SHEET_VIEW (object);
1142 pspp_sheet_view_set_model (tree_view, g_value_get_object (value));
1144 case PROP_HADJUSTMENT:
1145 pspp_sheet_view_do_set_hadjustment (tree_view, g_value_get_object (value));
1147 case PROP_VADJUSTMENT:
1148 pspp_sheet_view_do_set_vadjustment (tree_view, g_value_get_object (value));
1150 case PROP_HSCROLL_POLICY:
1151 tree_view->priv->hscroll_policy = g_value_get_enum (value);
1152 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1154 case PROP_VSCROLL_POLICY:
1155 tree_view->priv->vscroll_policy = g_value_get_enum (value);
1156 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1158 case PROP_HEADERS_VISIBLE:
1159 pspp_sheet_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1161 case PROP_HEADERS_CLICKABLE:
1162 pspp_sheet_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1164 case PROP_REORDERABLE:
1165 pspp_sheet_view_set_reorderable (tree_view, g_value_get_boolean (value));
1167 case PROP_RULES_HINT:
1168 pspp_sheet_view_set_rules_hint (tree_view, g_value_get_boolean (value));
1170 case PROP_ENABLE_SEARCH:
1171 pspp_sheet_view_set_enable_search (tree_view, g_value_get_boolean (value));
1173 case PROP_SEARCH_COLUMN:
1174 pspp_sheet_view_set_search_column (tree_view, g_value_get_int (value));
1176 case PROP_HOVER_SELECTION:
1177 tree_view->priv->hover_selection = g_value_get_boolean (value);
1179 case PROP_RUBBER_BANDING:
1180 tree_view->priv->rubber_banding_enable = g_value_get_boolean (value);
1182 case PROP_ENABLE_GRID_LINES:
1183 pspp_sheet_view_set_grid_lines (tree_view, g_value_get_enum (value));
1185 case PROP_TOOLTIP_COLUMN:
1186 pspp_sheet_view_set_tooltip_column (tree_view, g_value_get_int (value));
1188 case PROP_SPECIAL_CELLS:
1189 pspp_sheet_view_set_special_cells (tree_view, g_value_get_enum (value));
1191 case PROP_FIXED_HEIGHT:
1192 pspp_sheet_view_set_fixed_height (tree_view, g_value_get_int (value));
1194 case PROP_FIXED_HEIGHT_SET:
1195 if (g_value_get_boolean (value))
1197 if (!tree_view->priv->fixed_height_set
1198 && tree_view->priv->fixed_height >= 0)
1200 tree_view->priv->fixed_height_set = true;
1201 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1206 if (tree_view->priv->fixed_height_set)
1208 tree_view->priv->fixed_height_set = false;
1209 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1210 install_presize_handler (tree_view);
1215 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1221 pspp_sheet_view_get_property (GObject *object,
1226 PsppSheetView *tree_view;
1228 tree_view = PSPP_SHEET_VIEW (object);
1233 g_value_set_object (value, tree_view->priv->model);
1235 case PROP_HADJUSTMENT:
1236 g_value_set_object (value, tree_view->priv->hadjustment);
1238 case PROP_VADJUSTMENT:
1239 g_value_set_object (value, tree_view->priv->vadjustment);
1241 case PROP_HSCROLL_POLICY:
1242 g_value_set_enum (value, tree_view->priv->hscroll_policy);
1244 case PROP_VSCROLL_POLICY:
1245 g_value_set_enum (value, tree_view->priv->vscroll_policy);
1247 case PROP_HEADERS_VISIBLE:
1248 g_value_set_boolean (value, pspp_sheet_view_get_headers_visible (tree_view));
1250 case PROP_HEADERS_CLICKABLE:
1251 g_value_set_boolean (value, pspp_sheet_view_get_headers_clickable (tree_view));
1253 case PROP_REORDERABLE:
1254 g_value_set_boolean (value, tree_view->priv->reorderable);
1256 case PROP_RULES_HINT:
1257 g_value_set_boolean (value, tree_view->priv->has_rules);
1259 case PROP_ENABLE_SEARCH:
1260 g_value_set_boolean (value, tree_view->priv->enable_search);
1262 case PROP_SEARCH_COLUMN:
1263 g_value_set_int (value, tree_view->priv->search_column);
1265 case PROP_HOVER_SELECTION:
1266 g_value_set_boolean (value, tree_view->priv->hover_selection);
1268 case PROP_RUBBER_BANDING:
1269 g_value_set_boolean (value, tree_view->priv->rubber_banding_enable);
1271 case PROP_ENABLE_GRID_LINES:
1272 g_value_set_enum (value, tree_view->priv->grid_lines);
1274 case PROP_TOOLTIP_COLUMN:
1275 g_value_set_int (value, tree_view->priv->tooltip_column);
1277 case PROP_SPECIAL_CELLS:
1278 g_value_set_enum (value, tree_view->priv->special_cells);
1280 case PROP_FIXED_HEIGHT:
1281 g_value_set_int (value, pspp_sheet_view_get_fixed_height (tree_view));
1283 case PROP_FIXED_HEIGHT_SET:
1284 g_value_set_boolean (value, tree_view->priv->fixed_height_set);
1287 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1293 pspp_sheet_view_dispose (GObject *object)
1295 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1297 if (tree_view->dispose_has_run)
1300 tree_view->dispose_has_run = TRUE;
1302 if (tree_view->priv->selection != NULL)
1304 _pspp_sheet_selection_set_tree_view (tree_view->priv->selection, NULL);
1305 g_object_unref (tree_view->priv->selection);
1306 tree_view->priv->selection = NULL;
1309 if (tree_view->priv->hadjustment)
1311 g_object_unref (tree_view->priv->hadjustment);
1312 tree_view->priv->hadjustment = NULL;
1314 if (tree_view->priv->vadjustment)
1316 g_object_unref (tree_view->priv->vadjustment);
1317 tree_view->priv->vadjustment = NULL;
1320 if (tree_view->priv->button_style)
1322 g_object_unref (tree_view->priv->button_style);
1323 tree_view->priv->button_style = NULL;
1327 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->dispose (object);
1333 pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
1334 GtkBuilder *builder,
1338 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), PSPP_SHEET_VIEW_COLUMN (child));
1342 pspp_sheet_view_finalize (GObject *object)
1344 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1346 pspp_sheet_view_stop_editing (tree_view, TRUE);
1348 if (tree_view->priv->selected != NULL)
1350 range_tower_destroy (tree_view->priv->selected);
1351 tree_view->priv->selected = NULL;
1355 tree_view->priv->prelight_node = -1;
1358 if (tree_view->priv->scroll_to_path != NULL)
1360 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
1361 tree_view->priv->scroll_to_path = NULL;
1364 if (tree_view->priv->drag_dest_row != NULL)
1366 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
1367 tree_view->priv->drag_dest_row = NULL;
1370 if (tree_view->priv->top_row != NULL)
1372 gtk_tree_row_reference_free (tree_view->priv->top_row);
1373 tree_view->priv->top_row = NULL;
1376 if (tree_view->priv->column_drop_func_data &&
1377 tree_view->priv->column_drop_func_data_destroy)
1379 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
1380 tree_view->priv->column_drop_func_data = NULL;
1383 if (tree_view->priv->destroy_count_destroy &&
1384 tree_view->priv->destroy_count_data)
1386 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
1387 tree_view->priv->destroy_count_data = NULL;
1390 gtk_tree_row_reference_free (tree_view->priv->cursor);
1391 tree_view->priv->cursor = NULL;
1393 gtk_tree_row_reference_free (tree_view->priv->anchor);
1394 tree_view->priv->anchor = NULL;
1396 /* destroy interactive search dialog */
1397 if (tree_view->priv->search_window)
1399 gtk_widget_destroy (tree_view->priv->search_window);
1400 tree_view->priv->search_window = NULL;
1401 tree_view->priv->search_entry = NULL;
1402 if (tree_view->priv->typeselect_flush_timeout)
1404 g_source_remove (tree_view->priv->typeselect_flush_timeout);
1405 tree_view->priv->typeselect_flush_timeout = 0;
1409 if (tree_view->priv->search_destroy && tree_view->priv->search_user_data)
1411 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
1412 tree_view->priv->search_user_data = NULL;
1415 if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data)
1417 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
1418 tree_view->priv->search_position_user_data = NULL;
1421 pspp_sheet_view_set_model (tree_view, NULL);
1424 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->finalize (object);
1429 /* GtkWidget Methods
1432 /* GtkWidget::map helper */
1434 pspp_sheet_view_map_buttons (PsppSheetView *tree_view)
1438 g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
1440 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
1442 PsppSheetViewColumn *column;
1444 for (list = tree_view->priv->columns; list; list = list->next)
1446 column = list->data;
1447 if (column->button != NULL &&
1448 gtk_widget_get_visible (column->button) &&
1449 !gtk_widget_get_mapped (column->button))
1450 gtk_widget_map (column->button);
1452 for (list = tree_view->priv->columns; list; list = list->next)
1454 column = list->data;
1455 if (column->visible == FALSE || column->window == NULL)
1457 if (column->resizable)
1459 gdk_window_raise (column->window);
1460 gdk_window_show (column->window);
1463 gdk_window_hide (column->window);
1465 gdk_window_show (tree_view->priv->header_window);
1470 pspp_sheet_view_map (GtkWidget *widget)
1472 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1475 gtk_widget_set_mapped (widget, TRUE);
1477 tmp_list = tree_view->priv->children;
1480 PsppSheetViewChild *child = tmp_list->data;
1481 tmp_list = tmp_list->next;
1483 if (gtk_widget_get_visible (child->widget))
1485 if (!gtk_widget_get_mapped (child->widget))
1486 gtk_widget_map (child->widget);
1489 gdk_window_show (tree_view->priv->bin_window);
1491 pspp_sheet_view_map_buttons (tree_view);
1493 gdk_window_show (gtk_widget_get_window (widget));
1497 pspp_sheet_view_realize (GtkWidget *widget)
1499 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1502 GdkWindowAttr attributes;
1503 gint attributes_mask;
1504 GtkAllocation allocation;
1506 gtk_widget_set_realized (widget, TRUE);
1508 gtk_widget_get_allocation (widget, &allocation);
1510 /* Make the main, clipping window */
1511 attributes.window_type = GDK_WINDOW_CHILD;
1512 attributes.x = allocation.x;
1513 attributes.y = allocation.y;
1514 attributes.width = allocation.width;
1515 attributes.height = allocation.height;
1516 attributes.wclass = GDK_INPUT_OUTPUT;
1517 attributes.visual = gtk_widget_get_visual (widget);
1518 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1520 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
1522 window = gdk_window_new (gtk_widget_get_parent_window (widget),
1523 &attributes, attributes_mask);
1524 gtk_widget_set_window (widget, window);
1526 gtk_widget_register_window (widget, window);
1527 gtk_widget_get_allocation (widget, &allocation);
1529 /* Make the window for the tree */
1531 attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
1532 attributes.width = MAX (tree_view->priv->width, allocation.width);
1533 attributes.height = allocation.height;
1534 attributes.event_mask = (GDK_EXPOSURE_MASK |
1536 GDK_POINTER_MOTION_MASK |
1537 GDK_ENTER_NOTIFY_MASK |
1538 GDK_LEAVE_NOTIFY_MASK |
1539 GDK_BUTTON_PRESS_MASK |
1540 GDK_BUTTON_RELEASE_MASK |
1541 gtk_widget_get_events (widget));
1543 tree_view->priv->bin_window = gdk_window_new (window,
1544 &attributes, attributes_mask);
1545 gtk_widget_register_window (widget, tree_view->priv->bin_window);
1546 gtk_widget_get_allocation (widget, &allocation);
1548 /* Make the column header window */
1551 attributes.width = MAX (tree_view->priv->width, allocation.width);
1552 attributes.height = tree_view->priv->header_height;
1553 attributes.event_mask = (GDK_EXPOSURE_MASK |
1555 GDK_BUTTON_PRESS_MASK |
1556 GDK_BUTTON_RELEASE_MASK |
1557 GDK_KEY_PRESS_MASK |
1558 GDK_KEY_RELEASE_MASK |
1559 gtk_widget_get_events (widget));
1561 tree_view->priv->header_window = gdk_window_new (window,
1562 &attributes, attributes_mask);
1563 gtk_widget_register_window (widget, tree_view->priv->header_window);
1565 { /* Ensure Background */
1566 GtkStyleContext *context;
1568 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
1570 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
1571 gtk_style_context_set_background (context, tree_view->priv->header_window);
1574 tmp_list = tree_view->priv->children;
1577 PsppSheetViewChild *child = tmp_list->data;
1578 tmp_list = tmp_list->next;
1580 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
1583 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
1584 _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list->data));
1586 /* Need to call those here, since they create GCs */
1587 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
1589 install_presize_handler (tree_view);
1593 pspp_sheet_view_unrealize (GtkWidget *widget)
1595 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1596 PsppSheetViewPrivate *priv = tree_view->priv;
1599 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget);
1601 if (priv->scroll_timeout != 0)
1603 g_source_remove (priv->scroll_timeout);
1604 priv->scroll_timeout = 0;
1607 if (priv->open_dest_timeout != 0)
1609 g_source_remove (priv->open_dest_timeout);
1610 priv->open_dest_timeout = 0;
1613 if (priv->presize_handler_timer != 0)
1615 g_source_remove (priv->presize_handler_timer);
1616 priv->presize_handler_timer = 0;
1619 if (priv->validate_rows_timer != 0)
1621 g_source_remove (priv->validate_rows_timer);
1622 priv->validate_rows_timer = 0;
1625 if (priv->scroll_sync_timer != 0)
1627 g_source_remove (priv->scroll_sync_timer);
1628 priv->scroll_sync_timer = 0;
1631 if (priv->typeselect_flush_timeout)
1633 g_source_remove (priv->typeselect_flush_timeout);
1634 priv->typeselect_flush_timeout = 0;
1637 for (list = priv->columns; list; list = list->next)
1638 _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list->data));
1640 gdk_window_set_user_data (priv->bin_window, NULL);
1641 gdk_window_destroy (priv->bin_window);
1642 priv->bin_window = NULL;
1644 gdk_window_set_user_data (priv->header_window, NULL);
1645 gdk_window_destroy (priv->header_window);
1646 priv->header_window = NULL;
1648 if (priv->drag_window)
1650 gdk_window_set_user_data (priv->drag_window, NULL);
1651 gdk_window_destroy (priv->drag_window);
1652 priv->drag_window = NULL;
1655 if (priv->drag_highlight_window)
1657 gdk_window_set_user_data (priv->drag_highlight_window, NULL);
1658 gdk_window_destroy (priv->drag_highlight_window);
1659 priv->drag_highlight_window = NULL;
1662 if (tree_view->priv->columns != NULL)
1664 list = tree_view->priv->columns;
1667 PsppSheetViewColumn *column;
1668 column = PSPP_SHEET_VIEW_COLUMN (list->data);
1670 pspp_sheet_view_remove_column (tree_view, column);
1672 tree_view->priv->columns = NULL;
1676 /* GtkWidget::size_request helper */
1678 pspp_sheet_view_size_request_columns (PsppSheetView *tree_view)
1682 tree_view->priv->header_height = 0;
1684 if (tree_view->priv->model)
1686 for (list = tree_view->priv->columns; list; list = list->next)
1688 GtkRequisition requisition;
1689 PsppSheetViewColumn *column = list->data;
1691 pspp_sheet_view_column_size_request (column, &requisition);
1692 column->button_request = requisition.width;
1693 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
1699 /* Called only by ::size_request */
1701 pspp_sheet_view_update_size (PsppSheetView *tree_view)
1704 PsppSheetViewColumn *column;
1707 if (tree_view->priv->model == NULL)
1709 tree_view->priv->width = 0;
1710 tree_view->priv->prev_width = 0;
1711 tree_view->priv->height = 0;
1715 tree_view->priv->prev_width = tree_view->priv->width;
1716 tree_view->priv->width = 0;
1718 /* keep this in sync with size_allocate below */
1719 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
1721 gint real_requested_width = 0;
1722 column = list->data;
1723 if (!column->visible)
1726 if (column->use_resized_width)
1728 real_requested_width = column->resized_width;
1732 real_requested_width = column->fixed_width;
1735 if (column->min_width != -1)
1736 real_requested_width = MAX (real_requested_width, column->min_width);
1737 if (column->max_width != -1)
1738 real_requested_width = MIN (real_requested_width, column->max_width);
1740 tree_view->priv->width += real_requested_width;
1743 tree_view->priv->height = tree_view->priv->fixed_height * tree_view->priv->row_count;
1747 pspp_sheet_view_size_request (GtkWidget *widget,
1748 GtkRequisition *requisition)
1750 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1753 /* we validate some rows initially just to make sure we have some size.
1754 * In practice, with a lot of static lists, this should get a good width.
1756 initialize_fixed_height_mode (tree_view);
1757 pspp_sheet_view_size_request_columns (tree_view);
1758 pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget));
1760 requisition->width = tree_view->priv->width;
1761 requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
1763 tmp_list = tree_view->priv->children;
1767 PsppSheetViewChild *child = tmp_list->data;
1768 GtkRequisition child_requisition;
1770 tmp_list = tmp_list->next;
1772 if (gtk_widget_get_visible (child->widget))
1774 gtk_widget_get_preferred_size (child->widget, NULL, &child_requisition);
1780 invalidate_column (PsppSheetView *tree_view,
1781 PsppSheetViewColumn *column)
1783 gint column_offset = 0;
1785 GtkWidget *widget = GTK_WIDGET (tree_view);
1788 if (!gtk_widget_get_realized (widget))
1791 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1792 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
1794 list = (rtl ? list->prev : list->next))
1796 PsppSheetViewColumn *tmpcolumn = list->data;
1797 if (tmpcolumn == column)
1799 GdkRectangle invalid_rect;
1800 GtkAllocation allocation;
1802 gtk_widget_get_allocation (widget, &allocation);
1803 invalid_rect.x = column_offset;
1805 invalid_rect.width = column->width;
1806 invalid_rect.height = allocation.height;
1808 gdk_window_invalidate_rect (gtk_widget_get_window (widget), &invalid_rect, TRUE);
1812 column_offset += tmpcolumn->width;
1817 invalidate_last_column (PsppSheetView *tree_view)
1822 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1824 for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
1826 last_column = (rtl ? last_column->next : last_column->prev))
1828 if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible)
1830 invalidate_column (tree_view, last_column->data);
1837 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_view,
1838 PsppSheetViewColumn *column)
1840 gint real_requested_width;
1842 if (column->use_resized_width)
1844 real_requested_width = column->resized_width;
1848 real_requested_width = column->fixed_width;
1851 if (column->min_width != -1)
1852 real_requested_width = MAX (real_requested_width, column->min_width);
1853 if (column->max_width != -1)
1854 real_requested_width = MIN (real_requested_width, column->max_width);
1856 return real_requested_width;
1860 span_intersects (int a0, int a_width,
1861 int b0, int b_width)
1863 int a1 = a0 + a_width;
1864 int b1 = b0 + b_width;
1865 return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1);
1868 /* GtkWidget::size_allocate helper */
1870 pspp_sheet_view_size_allocate_columns (GtkWidget *widget,
1871 gboolean *width_changed)
1873 PsppSheetView *tree_view;
1874 GList *list, *first_column, *last_column;
1875 PsppSheetViewColumn *column;
1876 GtkAllocation col_allocation;
1877 GtkAllocation allocation;
1879 gint extra, extra_per_column;
1880 gint full_requested_width = 0;
1881 gint number_of_expand_columns = 0;
1882 gboolean column_changed = FALSE;
1885 tree_view = PSPP_SHEET_VIEW (widget);
1887 for (last_column = g_list_last (tree_view->priv->columns);
1888 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
1889 last_column = last_column->prev)
1892 if (last_column == NULL)
1895 for (first_column = g_list_first (tree_view->priv->columns);
1896 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
1897 first_column = first_column->next)
1900 col_allocation.y = 0;
1901 col_allocation.height = tree_view->priv->header_height;
1903 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1905 /* find out how many extra space and expandable columns we have */
1906 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
1908 column = (PsppSheetViewColumn *)list->data;
1910 if (!column->visible)
1913 full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1916 number_of_expand_columns++;
1919 gtk_widget_get_allocation (widget, &allocation);
1920 extra = MAX (allocation.width - full_requested_width, 0);
1921 if (number_of_expand_columns > 0)
1922 extra_per_column = extra/number_of_expand_columns;
1924 extra_per_column = 0;
1926 for (list = (rtl ? last_column : first_column);
1927 list != (rtl ? first_column->prev : last_column->next);
1928 list = (rtl ? list->prev : list->next))
1930 gint real_requested_width = 0;
1933 column = list->data;
1934 old_width = column->width;
1936 if (!column->visible)
1939 /* We need to handle the dragged button specially.
1941 if (column == tree_view->priv->drag_column)
1943 GtkAllocation drag_allocation;
1944 drag_allocation.width = gdk_window_get_width (tree_view->priv->drag_window);
1945 drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window);
1946 drag_allocation.x = 0;
1947 drag_allocation.y = 0;
1948 pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column,
1950 width += drag_allocation.width;
1954 real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1956 col_allocation.x = width;
1957 column->width = real_requested_width;
1961 if (number_of_expand_columns == 1)
1963 /* We add the remander to the last column as
1965 column->width += extra;
1969 column->width += extra_per_column;
1970 extra -= extra_per_column;
1971 number_of_expand_columns --;
1975 if (column->width != old_width)
1976 g_object_notify (G_OBJECT (column), "width");
1978 col_allocation.width = column->width;
1979 width += column->width;
1981 if (column->width > old_width)
1982 column_changed = TRUE;
1984 pspp_sheet_view_column_size_allocate (column, &col_allocation);
1987 gdk_window_move_resize (column->window,
1988 col_allocation.x + (rtl ? 0 : col_allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
1990 TREE_VIEW_DRAG_WIDTH, col_allocation.height);
1993 /* We change the width here. The user might have been resizing columns,
1994 * so the total width of the tree view changes.
1996 tree_view->priv->width = width;
1998 *width_changed = TRUE;
2001 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2005 update_childrens_allocation (PsppSheetView *tree_view)
2008 for (tmp_list = tree_view->priv->children; tmp_list; tmp_list = tmp_list->next)
2010 PsppSheetViewChild *child = tmp_list->data;
2011 GtkAllocation allocation;
2014 /* totally ignore our child's requisition */
2015 path = _pspp_sheet_view_find_path (tree_view, child->node);
2016 pspp_sheet_view_get_cell_area (tree_view, path, child->column, &allocation);
2017 gtk_tree_path_free (path);
2018 gtk_widget_size_allocate (child->widget, &allocation);
2023 pspp_sheet_view_size_allocate (GtkWidget *widget,
2024 GtkAllocation *allocation)
2026 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2028 gboolean width_changed = FALSE;
2029 GtkAllocation old_allocation;
2030 gtk_widget_get_allocation (widget, &old_allocation);
2032 if (allocation->width != old_allocation.width)
2033 width_changed = TRUE;
2035 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2036 allocation->x += allocation->width - tree_view->priv->width ;
2038 gtk_widget_set_allocation (widget, allocation);
2040 /* We size-allocate the columns first because the width of the
2041 * tree view (used in updating the adjustments below) might change.
2043 pspp_sheet_view_size_allocate_columns (widget, &width_changed);
2045 gtk_adjustment_set_page_size (tree_view->priv->hadjustment, allocation->width);
2046 gtk_adjustment_set_page_increment (tree_view->priv->hadjustment, allocation->width * 0.9);
2047 gtk_adjustment_set_step_increment (tree_view->priv->hadjustment, allocation->width * 0.1);
2048 gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0);
2049 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->hadjustment), tree_view->priv->width));
2051 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2053 if (allocation->width < tree_view->priv->width)
2055 if (tree_view->priv->init_hadjust_value)
2057 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2058 tree_view->priv->init_hadjust_value = FALSE;
2060 else if (allocation->width != old_allocation.width)
2062 gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) - allocation->width + old_allocation.width, 0, tree_view->priv->width - allocation->width));
2065 gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (tree_view->priv->width - (tree_view->priv->prev_width - gtk_adjustment_get_value (tree_view->priv->hadjustment)), 0, tree_view->priv->width - allocation->width));
2069 gtk_adjustment_set_value (tree_view->priv->hadjustment, 0);
2070 tree_view->priv->init_hadjust_value = TRUE;
2074 if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + allocation->width > tree_view->priv->width)
2075 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2077 gtk_adjustment_changed (tree_view->priv->hadjustment);
2079 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2080 gtk_adjustment_set_step_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.1);
2081 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.9);
2082 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
2083 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->vadjustment), tree_view->priv->height));
2085 gtk_adjustment_changed (tree_view->priv->vadjustment);
2087 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2088 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
2089 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2090 else if (gtk_adjustment_get_value (tree_view->priv->vadjustment) + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
2091 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2092 tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
2093 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2094 pspp_sheet_view_top_row_to_dy (tree_view);
2096 pspp_sheet_view_dy_to_top_row (tree_view);
2098 if (gtk_widget_get_realized (widget))
2100 gdk_window_move_resize (gtk_widget_get_window (widget),
2101 allocation->x, allocation->y,
2102 allocation->width, allocation->height);
2103 gdk_window_move_resize (tree_view->priv->header_window,
2104 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2106 MAX (tree_view->priv->width, allocation->width),
2107 tree_view->priv->header_height);
2108 gdk_window_move_resize (tree_view->priv->bin_window,
2109 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2110 TREE_VIEW_HEADER_HEIGHT (tree_view),
2111 MAX (tree_view->priv->width, allocation->width),
2112 allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2115 if (tree_view->priv->row_count == 0)
2116 invalidate_empty_focus (tree_view);
2118 if (gtk_widget_get_realized (widget))
2120 gboolean has_expand_column = FALSE;
2121 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2123 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2125 has_expand_column = TRUE;
2130 /* This little hack only works if we have an LTR locale, and no column has the */
2133 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2134 ! has_expand_column)
2135 invalidate_last_column (tree_view);
2137 gtk_widget_queue_draw (widget);
2139 update_childrens_allocation(tree_view);
2143 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2145 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2147 GtkWidget *widget = GTK_WIDGET (tree_view);
2149 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2150 gtk_widget_grab_focus (widget);
2151 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2155 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2158 return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2162 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2165 range_tower_set1 (tree_view->priv->selected, node, 1);
2169 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2172 range_tower_set0 (tree_view->priv->selected, node, 1);
2176 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2179 return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2183 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2186 return node > 0 ? node - 1 : -1;
2190 all_columns_selected (PsppSheetView *tree_view)
2194 for (list = tree_view->priv->columns; list; list = list->next)
2196 PsppSheetViewColumn *column = list->data;
2197 if (column->selectable && !column->selected)
2205 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2207 PsppSheetViewColumn *column,
2208 GdkEventButton *event)
2210 PsppSheetSelection *selection;
2211 PsppSheetSelectionMode mode;
2213 gboolean update_anchor;
2217 g_return_val_if_fail (tree_view != NULL, FALSE);
2218 g_return_val_if_fail (column != NULL, FALSE);
2220 selection = tree_view->priv->selection;
2221 mode = pspp_sheet_selection_get_mode (selection);
2222 if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2225 if (!column->row_head)
2230 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2231 if (event->type != GDK_BUTTON_PRESS
2232 || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2238 path = gtk_tree_path_new_from_indices (node, -1);
2241 pspp_sheet_selection_unselect_all (selection);
2242 pspp_sheet_selection_select_path (selection, path);
2243 pspp_sheet_selection_select_all_columns (selection);
2244 update_anchor = TRUE;
2247 else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2249 if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2250 || !all_columns_selected (tree_view))
2252 pspp_sheet_selection_unselect_all (selection);
2253 pspp_sheet_selection_select_path (selection, path);
2254 pspp_sheet_selection_select_all_columns (selection);
2255 update_anchor = TRUE;
2259 update_anchor = handled = FALSE;
2261 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2262 && modifiers == GDK_CONTROL_MASK)
2264 if (!all_columns_selected (tree_view))
2266 pspp_sheet_selection_unselect_all (selection);
2267 pspp_sheet_selection_select_all_columns (selection);
2270 if (pspp_sheet_selection_path_is_selected (selection, path))
2271 pspp_sheet_selection_unselect_path (selection, path);
2273 pspp_sheet_selection_select_path (selection, path);
2274 update_anchor = TRUE;
2277 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2278 && modifiers == GDK_SHIFT_MASK)
2280 GtkTreeRowReference *anchor = tree_view->priv->anchor;
2281 GtkTreePath *anchor_path;
2283 if (all_columns_selected (tree_view)
2284 && gtk_tree_row_reference_valid (anchor))
2286 update_anchor = FALSE;
2287 anchor_path = gtk_tree_row_reference_get_path (anchor);
2291 update_anchor = TRUE;
2292 anchor_path = gtk_tree_path_copy (path);
2295 pspp_sheet_selection_unselect_all (selection);
2296 pspp_sheet_selection_select_range (selection, anchor_path, path);
2297 pspp_sheet_selection_select_all_columns (selection);
2299 gtk_tree_path_free (anchor_path);
2304 update_anchor = handled = FALSE;
2308 if (tree_view->priv->anchor)
2309 gtk_tree_row_reference_free (tree_view->priv->anchor);
2310 tree_view->priv->anchor =
2311 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2312 tree_view->priv->model,
2316 gtk_tree_path_free (path);
2321 find_click (PsppSheetView *tree_view,
2324 PsppSheetViewColumn **column,
2325 GdkRectangle *background_area,
2326 GdkRectangle *cell_area)
2333 /* find the node that was clicked */
2334 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2337 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2342 background_area->y = y_offset + y;
2343 background_area->height = ROW_HEIGHT (tree_view);
2344 background_area->x = 0;
2346 /* Let the column have a chance at selecting it. */
2347 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2348 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2349 list; list = (rtl ? list->prev : list->next))
2351 PsppSheetViewColumn *candidate = list->data;
2353 if (!candidate->visible)
2356 background_area->width = candidate->width;
2357 if ((background_area->x > x) ||
2358 (background_area->x + background_area->width <= x))
2360 background_area->x += background_area->width;
2364 /* we found the focus column */
2366 pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2368 *column = candidate;
2376 pspp_sheet_view_button_press (GtkWidget *widget,
2377 GdkEventButton *event)
2379 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2381 PsppSheetViewColumn *column = NULL;
2383 GdkRectangle background_area;
2384 GdkRectangle cell_area;
2387 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2388 pspp_sheet_view_stop_editing (tree_view, FALSE);
2391 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2392 * we're done handling the button press.
2395 if (event->window == tree_view->priv->bin_window)
2400 gint pre_val, aft_val;
2401 PsppSheetViewColumn *column = NULL;
2402 GtkCellRenderer *focus_cell = NULL;
2403 gboolean row_double_click = FALSE;
2406 if (tree_view->priv->row_count == 0)
2408 grab_focus_and_unset_draw_keyfocus (tree_view);
2412 if (!find_click (tree_view, event->x, event->y, &node, &column,
2413 &background_area, &cell_area))
2415 grab_focus_and_unset_draw_keyfocus (tree_view);
2419 tree_view->priv->focus_column = column;
2421 if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2425 pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2427 path = _pspp_sheet_view_find_path (tree_view, node);
2429 /* we only handle selection modifications on the first button press
2431 if (event->type == GDK_BUTTON_PRESS)
2433 PsppSheetSelectionMode mode = 0;
2435 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2436 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
2437 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2438 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
2440 focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2442 pspp_sheet_view_column_focus_cell (column, focus_cell);
2444 if (event->state & GDK_CONTROL_MASK)
2446 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, mode);
2447 pspp_sheet_view_real_toggle_cursor_row (tree_view);
2449 else if (event->state & GDK_SHIFT_MASK)
2451 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
2452 pspp_sheet_view_real_select_cursor_row (tree_view, FALSE, mode);
2456 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
2459 if (tree_view->priv->anchor_column == NULL ||
2460 !(event->state & GDK_SHIFT_MASK))
2461 tree_view->priv->anchor_column = column;
2462 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2463 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2464 tree_view->priv->anchor_column,
2468 /* the treeview may have been scrolled because of _set_cursor,
2472 aft_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2473 dval = pre_val - aft_val;
2475 cell_area.y += dval;
2476 background_area.y += dval;
2478 /* Save press to possibly begin a drag
2480 if (!tree_view->priv->in_grab &&
2481 tree_view->priv->pressed_button < 0)
2483 tree_view->priv->pressed_button = event->button;
2484 tree_view->priv->press_start_x = event->x;
2485 tree_view->priv->press_start_y = event->y;
2486 tree_view->priv->press_start_node = node;
2488 if (tree_view->priv->rubber_banding_enable
2489 && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2490 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2492 tree_view->priv->press_start_y += tree_view->priv->dy;
2493 tree_view->priv->rubber_band_x = event->x;
2494 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2495 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2497 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2498 tree_view->priv->rubber_band_ctrl = TRUE;
2499 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2500 tree_view->priv->rubber_band_shift = TRUE;
2505 /* Test if a double click happened on the same row. */
2506 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2508 int double_click_time, double_click_distance;
2510 g_object_get (gtk_settings_get_for_screen (
2511 gtk_widget_get_screen (widget)),
2512 "gtk-double-click-time", &double_click_time,
2513 "gtk-double-click-distance", &double_click_distance,
2516 /* Same conditions as _gdk_event_button_generate */
2517 if (tree_view->priv->last_button_x != -1 &&
2518 (event->time < tree_view->priv->last_button_time + double_click_time) &&
2519 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2520 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2522 /* We do no longer compare paths of this row and the
2523 * row clicked previously. We use the double click
2524 * distance to decide whether this is a valid click,
2525 * allowing the mouse to slightly move over another row.
2527 row_double_click = TRUE;
2529 tree_view->priv->last_button_time = 0;
2530 tree_view->priv->last_button_x = -1;
2531 tree_view->priv->last_button_y = -1;
2535 tree_view->priv->last_button_time = event->time;
2536 tree_view->priv->last_button_x = event->x;
2537 tree_view->priv->last_button_y = event->y;
2541 if (row_double_click)
2543 gtk_grab_remove (widget);
2544 pspp_sheet_view_row_activated (tree_view, path, column);
2546 if (tree_view->priv->pressed_button == event->button)
2547 tree_view->priv->pressed_button = -1;
2550 gtk_tree_path_free (path);
2552 /* If we activated the row through a double click we don't want to grab
2553 * focus back, as moving focus to another widget is pretty common.
2555 if (!row_double_click)
2556 grab_focus_and_unset_draw_keyfocus (tree_view);
2561 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2563 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2565 column = list->data;
2566 if (event->window == column->window &&
2567 column->resizable &&
2572 if (GDK_GRAB_SUCCESS != gdk_device_grab (event->device,
2576 GDK_POINTER_MOTION_HINT_MASK |
2577 GDK_BUTTON1_MOTION_MASK |
2578 GDK_BUTTON_RELEASE_MASK,
2582 gtk_grab_add (widget);
2583 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2584 column->resized_width = column->width;
2586 /* block attached dnd signal handler */
2587 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2589 g_signal_handlers_block_matched (widget,
2590 G_SIGNAL_MATCH_DATA,
2594 tree_view->priv->drag_pos = i;
2595 tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2597 if (!gtk_widget_has_focus (widget))
2598 gtk_widget_grab_focus (widget);
2606 /* GtkWidget::button_release_event helper */
2608 pspp_sheet_view_button_release_drag_column (GtkWidget *widget,
2609 GdkEventButton *event)
2611 PsppSheetView *tree_view;
2615 tree_view = PSPP_SHEET_VIEW (widget);
2617 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2618 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2619 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2621 /* Move the button back */
2622 g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2624 g_object_ref (tree_view->priv->drag_column->button);
2625 gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2626 gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2627 gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2628 g_object_unref (tree_view->priv->drag_column->button);
2629 gtk_widget_queue_resize (widget);
2630 if (tree_view->priv->drag_column->resizable)
2632 gdk_window_raise (tree_view->priv->drag_column->window);
2633 gdk_window_show (tree_view->priv->drag_column->window);
2636 gdk_window_hide (tree_view->priv->drag_column->window);
2638 gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2642 if (tree_view->priv->cur_reorder &&
2643 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2644 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2645 tree_view->priv->cur_reorder->right_column);
2649 if (tree_view->priv->cur_reorder &&
2650 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2651 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2652 tree_view->priv->cur_reorder->left_column);
2654 tree_view->priv->drag_column = NULL;
2655 gdk_window_hide (tree_view->priv->drag_window);
2657 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2658 g_slice_free (PsppSheetViewColumnReorder, l->data);
2659 g_list_free (tree_view->priv->column_drag_info);
2660 tree_view->priv->column_drag_info = NULL;
2661 tree_view->priv->cur_reorder = NULL;
2663 if (tree_view->priv->drag_highlight_window)
2664 gdk_window_hide (tree_view->priv->drag_highlight_window);
2666 /* Reset our flags */
2667 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2668 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2673 /* GtkWidget::button_release_event helper */
2675 pspp_sheet_view_button_release_column_resize (GtkWidget *widget,
2676 GdkEventButton *event)
2678 PsppSheetView *tree_view;
2681 tree_view = PSPP_SHEET_VIEW (widget);
2683 tree_view->priv->drag_pos = -1;
2685 /* unblock attached dnd signal handler */
2686 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2688 g_signal_handlers_unblock_matched (widget,
2689 G_SIGNAL_MATCH_DATA,
2693 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2694 gtk_grab_remove (widget);
2695 gdk_device_ungrab (event->device, event->time);
2701 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2702 GdkEventButton *event)
2704 GtkCellEditable *cell_editable;
2709 PsppSheetViewColumn *column;
2710 GdkRectangle background_area;
2711 GdkRectangle cell_area;
2717 if (event->window != tree_view->priv->bin_window)
2720 /* Ignore a released button, if that button wasn't depressed */
2721 if (tree_view->priv->pressed_button != event->button)
2724 if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2728 /* decide if we edit */
2729 path = _pspp_sheet_view_find_path (tree_view, node);
2730 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2731 if (event->button != 1 || modifiers)
2734 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2735 pspp_sheet_view_column_cell_set_cell_data (column,
2736 tree_view->priv->model,
2739 if (!pspp_sheet_view_column_get_quick_edit (column)
2740 && _pspp_sheet_view_column_has_editable_cell (column))
2743 flags = 0; /* FIXME: get the right flags */
2744 path_string = gtk_tree_path_to_string (path);
2746 if (!_pspp_sheet_view_column_cell_event (column,
2754 if (cell_editable == NULL)
2757 pspp_sheet_view_real_set_cursor (tree_view, path,
2758 TRUE, TRUE, 0); /* XXX mode? */
2759 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2762 _pspp_sheet_view_column_get_neighbor_sizes (
2763 column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2766 area.width -= right + left;
2768 pspp_sheet_view_real_start_editing (tree_view,
2775 g_free (path_string);
2776 gtk_tree_path_free (path);
2781 pspp_sheet_view_button_release (GtkWidget *widget,
2782 GdkEventButton *event)
2784 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2786 pspp_sheet_view_stop_editing (tree_view, FALSE);
2787 if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2788 && pspp_sheet_view_button_release_edit (tree_view, event))
2790 if (tree_view->priv->pressed_button == event->button)
2791 tree_view->priv->pressed_button = -1;
2793 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2797 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2798 return pspp_sheet_view_button_release_drag_column (widget, event);
2800 if (tree_view->priv->rubber_band_status)
2801 pspp_sheet_view_stop_rubber_band (tree_view);
2803 if (tree_view->priv->pressed_button == event->button)
2804 tree_view->priv->pressed_button = -1;
2806 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2807 return pspp_sheet_view_button_release_column_resize (widget, event);
2813 pspp_sheet_view_grab_broken (GtkWidget *widget,
2814 GdkEventGrabBroken *event)
2816 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2818 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2819 pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2821 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2822 pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2827 /* GtkWidget::motion_event function set.
2831 do_prelight (PsppSheetView *tree_view,
2833 /* these are in bin_window coords */
2837 int prev_node = tree_view->priv->prelight_node;
2839 if (prev_node != node)
2841 tree_view->priv->prelight_node = node;
2844 _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2847 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2853 prelight_or_select (PsppSheetView *tree_view,
2855 /* these are in bin_window coords */
2859 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2861 if (tree_view->priv->hover_selection &&
2862 (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2863 !(tree_view->priv->edited_column &&
2864 tree_view->priv->edited_column->editable_widget))
2868 if (!pspp_sheet_view_node_is_selected (tree_view, node))
2872 path = _pspp_sheet_view_find_path (tree_view, node);
2873 pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2874 if (pspp_sheet_view_node_is_selected (tree_view, node))
2876 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2877 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE, 0); /* XXX mode? */
2879 gtk_tree_path_free (path);
2883 else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2884 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2887 do_prelight (tree_view, node, x, y);
2891 ensure_unprelighted (PsppSheetView *tree_view)
2893 do_prelight (tree_view,
2895 -1000, -1000); /* coords not possibly over an arrow */
2897 g_assert (tree_view->priv->prelight_node < 0);
2901 update_prelight (PsppSheetView *tree_view,
2908 if (tree_view->priv->row_count == 0)
2913 ensure_unprelighted (tree_view);
2917 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2921 pspp_sheet_view_find_offset (tree_view, new_y, &node);
2924 prelight_or_select (tree_view, node, x, y);
2930 /* Our motion arrow is either a box (in the case of the original spot)
2931 * or an arrow. It is expander_size wide.
2954 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2957 PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2958 GtkWidget *widget = GTK_WIDGET (tree_view);
2959 GdkBitmap *mask = NULL;
2964 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2965 GdkWindowAttr attributes;
2966 guint attributes_mask;
2969 reorder->left_column == tree_view->priv->drag_column ||
2970 reorder->right_column == tree_view->priv->drag_column)
2971 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2972 else if (reorder->left_column || reorder->right_column)
2974 GdkRectangle visible_rect;
2975 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2976 if (reorder->left_column)
2977 x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2979 x = reorder->right_column->allocation.x;
2981 if (x < visible_rect.x)
2982 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2983 else if (x > visible_rect.x + visible_rect.width)
2984 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2986 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2989 /* We want to draw the rectangle over the initial location. */
2990 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2995 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2997 if (tree_view->priv->drag_highlight_window)
2999 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3001 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3004 attributes.window_type = GDK_WINDOW_CHILD;
3005 attributes.wclass = GDK_INPUT_OUTPUT;
3006 attributes.x = tree_view->priv->drag_column_x;
3008 width = attributes.width = tree_view->priv->drag_column->allocation.width;
3009 height = attributes.height = tree_view->priv->drag_column->allocation.height;
3010 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3011 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3012 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3013 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3014 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
3015 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3017 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3018 gc = gdk_gc_new (mask);
3020 gdk_gc_set_foreground (gc, &col);
3021 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3023 gdk_gc_set_foreground(gc, &col);
3024 gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
3025 g_object_unref (gc);
3027 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3029 if (mask) g_object_unref (mask);
3030 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3033 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
3039 width = tree_view->priv->expander_size;
3041 /* Get x, y, width, height of arrow */
3042 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
3043 if (reorder->left_column)
3045 x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
3046 height = reorder->left_column->allocation.height;
3050 x += reorder->right_column->allocation.x - width/2;
3051 height = reorder->right_column->allocation.height;
3053 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3054 height += tree_view->priv->expander_size;
3056 /* Create the new window */
3057 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3059 if (tree_view->priv->drag_highlight_window)
3061 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3063 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3066 attributes.window_type = GDK_WINDOW_TEMP;
3067 attributes.wclass = GDK_INPUT_OUTPUT;
3068 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3069 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3070 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3071 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3074 attributes.width = width;
3075 attributes.height = height;
3076 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3077 &attributes, attributes_mask);
3078 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3080 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3081 gc = gdk_gc_new (mask);
3083 gdk_gc_set_foreground (gc, &col);
3084 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3086 /* Draw the 2 arrows as per above */
3088 gdk_gc_set_foreground (gc, &col);
3089 for (i = 0; i < width; i ++)
3091 if (i == (width/2 - 1))
3093 gdk_draw_line (mask, gc, i, j, i, height - j);
3094 if (i < (width/2 - 1))
3099 g_object_unref (gc);
3100 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3102 if (mask) g_object_unref (mask);
3105 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3106 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3108 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3109 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3115 width = tree_view->priv->expander_size;
3117 /* Get x, y, width, height of arrow */
3118 width = width/2; /* remember, the arrow only takes half the available width */
3119 gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
3120 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3121 x += widget->allocation.width - width;
3123 if (reorder->left_column)
3124 height = reorder->left_column->allocation.height;
3126 height = reorder->right_column->allocation.height;
3128 y -= tree_view->priv->expander_size;
3129 height += 2*tree_view->priv->expander_size;
3131 /* Create the new window */
3132 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3133 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3135 if (tree_view->priv->drag_highlight_window)
3137 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3139 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3142 attributes.window_type = GDK_WINDOW_TEMP;
3143 attributes.wclass = GDK_INPUT_OUTPUT;
3144 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3145 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3146 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3147 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3150 attributes.width = width;
3151 attributes.height = height;
3152 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3153 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3155 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3156 gc = gdk_gc_new (mask);
3158 gdk_gc_set_foreground (gc, &col);
3159 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3161 /* Draw the 2 arrows as per above */
3163 gdk_gc_set_foreground (gc, &col);
3164 j = tree_view->priv->expander_size;
3165 for (i = 0; i < width; i ++)
3168 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3172 gdk_draw_line (mask, gc, k, j, k, height - j);
3173 gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3174 gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3177 g_object_unref (gc);
3178 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3180 if (mask) g_object_unref (mask);
3183 tree_view->priv->drag_column_window_state = arrow_type;
3184 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3188 g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3189 gdk_window_hide (tree_view->priv->drag_highlight_window);
3193 gdk_window_show (tree_view->priv->drag_highlight_window);
3194 gdk_window_raise (tree_view->priv->drag_highlight_window);
3199 pspp_sheet_view_motion_resize_column (GtkWidget *widget,
3200 GdkEventMotion *event)
3204 PsppSheetViewColumn *column;
3205 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3207 column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3209 if (event->is_hint || event->window != gtk_widget_get_window (widget))
3210 gtk_widget_get_pointer (widget, &x, NULL);
3214 if (tree_view->priv->hadjustment)
3215 x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
3217 new_width = pspp_sheet_view_new_column_width (tree_view,
3218 tree_view->priv->drag_pos, &x);
3219 if (x != tree_view->priv->x_drag &&
3220 (new_width != column->fixed_width))
3222 column->use_resized_width = TRUE;
3223 column->resized_width = new_width;
3226 column->resized_width -= tree_view->priv->last_extra_space_per_column;
3228 gtk_widget_queue_resize (widget);
3236 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3238 PsppSheetViewColumnReorder *reorder = NULL;
3242 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3243 for (list = tree_view->priv->column_drag_info; list; list = list->next)
3245 reorder = (PsppSheetViewColumnReorder *) list->data;
3246 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3251 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3254 tree_view->priv->cur_reorder = reorder;
3255 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3259 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3261 GdkRectangle visible_rect;
3266 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3267 y += tree_view->priv->dy;
3269 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3271 /* see if we are near the edge. */
3272 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3275 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3280 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->vadjustment) + offset, 0.0,
3281 gtk_adjustment_get_upper (tree_view->priv->vadjustment) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
3282 gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3286 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3288 GdkRectangle visible_rect;
3293 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3295 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3297 /* See if we are near the edge. */
3298 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3301 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3307 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) + offset,
3308 0.0, gtk_adjustment_get_upper (tree_view->priv->hadjustment) - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
3309 gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3316 pspp_sheet_view_motion_drag_column (GtkWidget *widget,
3317 GdkEventMotion *event)
3319 PsppSheetView *tree_view = (PsppSheetView *) widget;
3320 PsppSheetViewColumn *column = tree_view->priv->drag_column;
3322 GtkAllocation allocation;
3325 if ((column == NULL) ||
3326 (event->window != tree_view->priv->drag_window))
3329 /* Handle moving the header */
3330 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3331 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
3332 x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3333 MAX (tree_view->priv->width, allocation.width) - column->allocation.width);
3334 gdk_window_move (tree_view->priv->drag_window, x, y);
3336 /* autoscroll, if needed */
3337 pspp_sheet_view_horizontal_autoscroll (tree_view);
3338 /* Update the current reorder position and arrow; */
3339 pspp_sheet_view_update_current_reorder (tree_view);
3345 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3347 remove_scroll_timeout (tree_view);
3348 gtk_grab_remove (GTK_WIDGET (tree_view));
3350 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3352 GtkTreePath *tmp_path;
3354 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3356 /* The anchor path should be set to the start path */
3357 tmp_path = _pspp_sheet_view_find_path (tree_view,
3358 tree_view->priv->rubber_band_start_node);
3360 if (tree_view->priv->anchor)
3361 gtk_tree_row_reference_free (tree_view->priv->anchor);
3363 tree_view->priv->anchor =
3364 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3365 tree_view->priv->model,
3368 gtk_tree_path_free (tmp_path);
3370 /* ... and the cursor to the end path */
3371 tmp_path = _pspp_sheet_view_find_path (tree_view,
3372 tree_view->priv->rubber_band_end_node);
3373 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE, 0); /* XXX mode? */
3374 gtk_tree_path_free (tmp_path);
3376 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3379 /* Clear status variables */
3380 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3381 tree_view->priv->rubber_band_shift = 0;
3382 tree_view->priv->rubber_band_ctrl = 0;
3384 tree_view->priv->rubber_band_start_node = -1;
3385 tree_view->priv->rubber_band_end_node = -1;
3389 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3393 gboolean skip_start,
3396 if (start_node == end_node)
3399 /* We skip the first node and jump inside the loop */
3405 /* Small optimization by assuming insensitive nodes are never
3410 if (tree_view->priv->rubber_band_shift)
3411 pspp_sheet_view_node_select (tree_view, start_node);
3412 else if (tree_view->priv->rubber_band_ctrl)
3414 /* Toggle the selection state */
3415 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3416 pspp_sheet_view_node_unselect (tree_view, start_node);
3418 pspp_sheet_view_node_select (tree_view, start_node);
3421 pspp_sheet_view_node_select (tree_view, start_node);
3425 /* Mirror the above */
3426 if (tree_view->priv->rubber_band_shift)
3427 pspp_sheet_view_node_unselect (tree_view, start_node);
3428 else if (tree_view->priv->rubber_band_ctrl)
3430 /* Toggle the selection state */
3431 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3432 pspp_sheet_view_node_unselect (tree_view, start_node);
3434 pspp_sheet_view_node_select (tree_view, start_node);
3437 pspp_sheet_view_node_unselect (tree_view, start_node);
3440 _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3442 if (start_node == end_node)
3447 start_node = pspp_sheet_view_node_next (tree_view, start_node);
3450 /* Ran out of tree */
3453 if (skip_end && start_node == end_node)
3460 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3463 return node * tree_view->priv->fixed_height;
3467 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3471 int fixed_height = tree_view->priv->fixed_height;
3472 if (fixed_height <= 0
3474 || height >= tree_view->priv->row_count * fixed_height)
3481 *new_node = height / fixed_height;
3482 return height % fixed_height;
3487 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3492 pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3493 pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3495 /* Handle the start area first */
3496 if (tree_view->priv->rubber_band_start_node < 0)
3498 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3505 else if (start_node < tree_view->priv->rubber_band_start_node)
3507 /* New node is above the old one; selection became bigger */
3508 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3510 tree_view->priv->rubber_band_start_node,
3515 else if (start_node > tree_view->priv->rubber_band_start_node)
3517 /* New node is below the old one; selection became smaller */
3518 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3519 tree_view->priv->rubber_band_start_node,
3526 tree_view->priv->rubber_band_start_node = start_node;
3528 /* Next, handle the end area */
3529 if (tree_view->priv->rubber_band_end_node < 0)
3531 /* In the event this happens, start_node was also -1; this case is
3535 else if (end_node < 0)
3537 /* Find the last node in the tree */
3538 pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3541 /* Selection reached end of the tree */
3542 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3543 tree_view->priv->rubber_band_end_node,
3549 else if (end_node > tree_view->priv->rubber_band_end_node)
3551 /* New node is below the old one; selection became bigger */
3552 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3553 tree_view->priv->rubber_band_end_node,
3559 else if (end_node < tree_view->priv->rubber_band_end_node)
3561 /* New node is above the old one; selection became smaller */
3562 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3564 tree_view->priv->rubber_band_end_node,
3570 tree_view->priv->rubber_band_end_node = end_node;
3573 #define GDK_RECTANGLE_PTR(X) ((GdkRectangle *)(X))
3576 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3579 cairo_rectangle_int_t old_area;
3580 cairo_rectangle_int_t new_area;
3581 cairo_rectangle_int_t common;
3582 cairo_region_t *invalid_region;
3583 PsppSheetViewColumn *column;
3585 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3586 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3587 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3588 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3590 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3593 y = MAX (y, 0) + tree_view->priv->dy;
3595 new_area.x = MIN (tree_view->priv->press_start_x, x);
3596 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3597 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3598 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3600 invalid_region = cairo_region_create_rectangle (&old_area);
3601 cairo_region_union_rectangle (invalid_region, &new_area);
3603 gdk_rectangle_intersect (GDK_RECTANGLE_PTR (&old_area),
3604 GDK_RECTANGLE_PTR (&new_area), GDK_RECTANGLE_PTR (&common));
3605 if (common.width > 2 && common.height > 2)
3607 cairo_region_t *common_region;
3609 /* make sure the border is invalidated */
3615 common_region = cairo_region_create_rectangle (&common);
3617 cairo_region_subtract (invalid_region, common_region);
3618 cairo_region_destroy (common_region);
3621 #if GTK_MAJOR_VERSION == 3
3622 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
3625 cairo_rectangle_int_t extents;
3627 cairo_region_get_extents (invalid_region, &extents);
3628 ereg = gdk_region_rectangle (GDK_RECTANGLE_PTR (&extents));
3629 gdk_window_invalidate_region (tree_view->priv->bin_window, ereg, TRUE);
3630 gdk_region_destroy (ereg);
3634 cairo_region_destroy (invalid_region);
3636 tree_view->priv->rubber_band_x = x;
3637 tree_view->priv->rubber_band_y = y;
3638 pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3640 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3641 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3642 tree_view->priv->anchor_column,
3645 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3647 pspp_sheet_view_update_rubber_band_selection (tree_view);
3652 pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view,
3657 GdkRectangle rubber_rect;
3661 rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3662 rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3663 rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3664 rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3666 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3669 cr = gdk_cairo_create (tree_view->priv->bin_window);
3670 cairo_set_line_width (cr, 1.0);
3672 style = gtk_widget_get_style (GTK_WIDGET (tree_view));
3673 cairo_set_source_rgba (cr,
3674 style->fg[GTK_STATE_NORMAL].red / 65535.,
3675 style->fg[GTK_STATE_NORMAL].green / 65535.,
3676 style->fg[GTK_STATE_NORMAL].blue / 65535.,
3679 gdk_cairo_rectangle (cr, &rect);
3683 cairo_set_source_rgb (cr,
3684 style->fg[GTK_STATE_NORMAL].red / 65535.,
3685 style->fg[GTK_STATE_NORMAL].green / 65535.,
3686 style->fg[GTK_STATE_NORMAL].blue / 65535.);
3688 cairo_rectangle (cr,
3689 rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3690 rubber_rect.width - 1, rubber_rect.height - 1);
3699 pspp_sheet_view_motion_bin_window (GtkWidget *widget,
3700 GdkEventMotion *event)
3702 PsppSheetView *tree_view;
3706 tree_view = (PsppSheetView *) widget;
3708 if (tree_view->priv->row_count == 0)
3711 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3713 GdkRectangle background_area, cell_area;
3714 PsppSheetViewColumn *column;
3716 if (find_click (tree_view, event->x, event->y, &node, &column,
3717 &background_area, &cell_area)
3718 && tree_view->priv->focus_column == column
3719 && tree_view->priv->press_start_node == node)
3722 gtk_grab_add (GTK_WIDGET (tree_view));
3723 pspp_sheet_view_update_rubber_band (tree_view);
3725 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3727 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3729 pspp_sheet_view_update_rubber_band (tree_view);
3731 add_scroll_timeout (tree_view);
3734 /* only check for an initiated drag when a button is pressed */
3735 if (tree_view->priv->pressed_button >= 0
3736 && !tree_view->priv->rubber_band_status)
3737 pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3739 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3743 pspp_sheet_view_find_offset (tree_view, new_y, &node);
3745 tree_view->priv->event_last_x = event->x;
3746 tree_view->priv->event_last_y = event->y;
3748 prelight_or_select (tree_view, node, event->x, event->y);
3754 pspp_sheet_view_motion (GtkWidget *widget,
3755 GdkEventMotion *event)
3757 PsppSheetView *tree_view;
3759 tree_view = (PsppSheetView *) widget;
3761 /* Resizing a column */
3762 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3763 return pspp_sheet_view_motion_resize_column (widget, event);
3766 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3767 return pspp_sheet_view_motion_drag_column (widget, event);
3769 /* Sanity check it */
3770 if (event->window == tree_view->priv->bin_window)
3771 return pspp_sheet_view_motion_bin_window (widget, event);
3776 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3777 * the tree is empty.
3780 invalidate_empty_focus (PsppSheetView *tree_view)
3784 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3789 area.width = gdk_window_get_width (tree_view->priv->bin_window);
3790 area.height = gdk_window_get_height (tree_view->priv->bin_window);
3791 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3794 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3798 draw_empty_focus (PsppSheetView *tree_view)
3800 GtkWidget *widget = GTK_WIDGET (tree_view);
3802 cairo_t *cr = gdk_cairo_create (tree_view->priv->bin_window);
3804 if (!gtk_widget_has_focus (widget))
3807 w = gdk_window_get_width (tree_view->priv->bin_window);
3808 h = gdk_window_get_height (tree_view->priv->bin_window);
3814 gtk_paint_focus (gtk_widget_get_style (widget),
3816 gtk_widget_get_state (widget),
3824 pspp_sheet_view_draw_vertical_grid_lines (PsppSheetView *tree_view,
3826 gint n_visible_columns,
3830 GList *list = tree_view->priv->columns;
3834 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3835 && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3838 /* Only draw the lines for visible rows and columns */
3839 gboolean rtl = (gtk_widget_get_direction (tree_view) == GTK_TEXT_DIR_RTL);
3841 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
3843 list = (rtl ? list->prev : list->next))
3845 PsppSheetViewColumn *column = list->data;
3848 if (! column->visible)
3851 current_x += column->width;
3853 /* Generally the grid lines should fit within the column, but for the
3854 last visible column we put it just past the end of the column.
3855 (Otherwise horizontal grid lines sometimes stick out by one pixel.) */
3857 if (i != n_visible_columns - 1)
3860 cairo_set_line_width (cr, 1.0);
3861 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
3862 cairo_move_to (cr, x + 0.5, min_y);
3863 cairo_line_to (cr, x + 0.5, max_y - min_y);
3868 /* Warning: Very scary function.
3869 * Modify at your own risk
3871 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3872 * FIXME: It's not...
3875 pspp_sheet_view_draw_bin (GtkWidget *widget,
3878 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3883 int drag_highlight = -1;
3886 gint y_offset, cell_offset;
3888 GdkRectangle background_area;
3889 GdkRectangle cell_area;
3891 gint bin_window_width;
3892 gint bin_window_height;
3893 GtkTreePath *cursor_path;
3894 GtkTreePath *drag_dest_path;
3895 GList *first_column, *last_column;
3896 gint vertical_separator;
3897 gint horizontal_separator;
3898 gint focus_line_width;
3899 gboolean allow_rules;
3901 gint n_visible_columns;
3902 gint grid_line_width;
3903 gboolean row_ending_details;
3904 gboolean draw_vgrid_lines, draw_hgrid_lines;
3906 GtkStyleContext *context;
3907 context = gtk_widget_get_style_context (widget);
3910 GtkAllocation allocation;
3911 gtk_widget_get_allocation (widget, &allocation);
3913 GdkRectangle exposed_rect;
3914 gdk_cairo_get_clip_rectangle (cr, &exposed_rect);
3918 Zarea.height = allocation.height;
3920 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3922 gtk_widget_style_get (widget,
3923 "horizontal-separator", &horizontal_separator,
3924 "vertical-separator", &vertical_separator,
3925 "allow-rules", &allow_rules,
3926 "focus-line-width", &focus_line_width,
3927 "row-ending-details", &row_ending_details,
3930 if (tree_view->priv->row_count == 0)
3932 draw_empty_focus (tree_view);
3937 /* clip event->area to the visible area */
3938 if (Zarea.height < 0.5)
3942 validate_visible_area (tree_view);
3944 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, Zarea.y);
3948 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3950 gdk_window_get_width (tree_view->priv->bin_window);
3953 gdk_window_get_height (tree_view->priv->bin_window);
3956 if (tree_view->priv->height < bin_window_height)
3958 gtk_paint_flat_box (gtk_widget_get_style (widget),
3960 gtk_widget_get_state (widget),
3964 0, tree_view->priv->height,
3966 bin_window_height - tree_view->priv->height);
3972 /* find the path for the node */
3973 path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3974 gtk_tree_model_get_iter (tree_view->priv->model,
3977 gtk_tree_path_free (path);
3980 drag_dest_path = NULL;
3982 if (tree_view->priv->cursor)
3983 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3986 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3988 if (tree_view->priv->drag_dest_row)
3989 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3992 _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3996 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3997 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3999 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
4000 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
4002 if (draw_vgrid_lines || draw_hgrid_lines)
4003 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
4005 n_visible_columns = 0;
4006 for (list = tree_view->priv->columns; list; list = list->next)
4008 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4010 n_visible_columns ++;
4013 /* Find the last column */
4014 for (last_column = g_list_last (tree_view->priv->columns);
4015 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
4016 last_column = last_column->prev)
4020 for (first_column = g_list_first (tree_view->priv->columns);
4021 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
4022 first_column = first_column->next)
4025 /* Actually process the expose event. To do this, we want to
4026 * start at the first node of the event, and walk the tree in
4027 * order, drawing each successive node.
4034 gboolean is_first = FALSE;
4035 gboolean is_last = FALSE;
4036 gboolean done = FALSE;
4039 max_height = ROW_HEIGHT (tree_view);
4043 background_area.y = y_offset + Zarea.y;
4044 background_area.height = max_height;
4045 max_y = background_area.y + max_height;
4049 if (node == tree_view->priv->prelight_node)
4050 flags |= GTK_CELL_RENDERER_PRELIT;
4052 selected = pspp_sheet_view_node_is_selected (tree_view, node);
4056 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4058 list = (rtl ? list->prev : list->next))
4060 PsppSheetViewColumn *column = list->data;
4061 const gchar *detail = NULL;
4062 gboolean selected_column;
4065 if (!column->visible)
4068 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
4069 selected_column = column->selected && column->selectable;
4071 selected_column = TRUE;
4074 if (cell_offset > Zarea.x + Zarea.width ||
4075 cell_offset + column->width < Zarea.x)
4077 cell_offset += column->width;
4082 if (selected && selected_column)
4083 flags |= GTK_CELL_RENDERER_SELECTED;
4085 flags &= ~GTK_CELL_RENDERER_SELECTED;
4087 if (column->show_sort_indicator)
4088 flags |= GTK_CELL_RENDERER_SORTED;
4090 flags &= ~GTK_CELL_RENDERER_SORTED;
4093 flags |= GTK_CELL_RENDERER_FOCUSED;
4095 flags &= ~GTK_CELL_RENDERER_FOCUSED;
4097 background_area.x = cell_offset;
4098 background_area.width = column->width;
4100 cell_area = background_area;
4101 cell_area.y += vertical_separator / 2;
4102 cell_area.x += horizontal_separator / 2;
4103 cell_area.height -= vertical_separator;
4104 cell_area.width -= horizontal_separator;
4106 if (draw_vgrid_lines)
4108 if (list == first_column)
4110 cell_area.width -= grid_line_width / 2;
4112 else if (list == last_column)
4114 cell_area.x += grid_line_width / 2;
4115 cell_area.width -= grid_line_width / 2;
4119 cell_area.x += grid_line_width / 2;
4120 cell_area.width -= grid_line_width;
4124 if (draw_hgrid_lines)
4126 cell_area.y += grid_line_width / 2;
4127 cell_area.height -= grid_line_width;
4131 if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4133 if (!gdk_rectangle_intersect (&background_area, &exposed_rect, NULL))
4136 cell_offset += column->width;
4141 pspp_sheet_view_column_cell_set_cell_data (column,
4142 tree_view->priv->model,
4145 /* Select the detail for drawing the cell. relevant
4146 * factors are parity, sortedness, and whether to
4149 if (allow_rules && tree_view->priv->has_rules)
4151 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4152 n_visible_columns >= 3)
4155 detail = "cell_odd_ruled_sorted";
4157 detail = "cell_even_ruled_sorted";
4162 detail = "cell_odd_ruled";
4164 detail = "cell_even_ruled";
4169 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4170 n_visible_columns >= 3)
4173 detail = "cell_odd_sorted";
4175 detail = "cell_even_sorted";
4180 detail = "cell_odd";
4182 detail = "cell_even";
4188 gtk_style_context_save (context);
4189 state = gtk_cell_renderer_get_state (NULL, widget, flags);
4190 gtk_style_context_set_state (context, state);
4191 gtk_style_context_add_class (context, GTK_STYLE_CLASS_CELL);
4193 /* Draw background */
4194 gtk_render_background (context, cr,
4197 background_area.width,
4198 background_area.height);
4201 gtk_render_frame (context, cr,
4204 background_area.width,
4205 background_area.height);
4207 if (draw_hgrid_lines)
4209 cairo_set_line_width (cr, 1.0);
4210 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
4212 if (background_area.y >= 0)
4215 gdk_draw_line (event->window,
4216 tree_view->priv->grid_line_gc[widget->state],
4217 background_area.x, background_area.y,
4218 background_area.x + background_area.width,
4221 cairo_move_to (cr, background_area.x, background_area.y - 0.5);
4222 cairo_line_to (cr, background_area.x + background_area.width,
4223 background_area.y - 0.5);
4227 if (y_offset + max_height <= Zarea.height - 0.5)
4230 gdk_draw_line (event->window,
4231 tree_view->priv->grid_line_gc[widget->state],
4232 background_area.x, background_area.y + max_height,
4233 background_area.x + background_area.width,
4234 background_area.y + max_height);
4237 cairo_move_to (cr, background_area.x, background_area.y + max_height - 0.5);
4238 cairo_line_to (cr, background_area.x + background_area.width,
4239 background_area.y + max_height - 0.5);
4245 _pspp_sheet_view_column_cell_render (column,
4252 cell_offset += column->width;
4253 gtk_style_context_restore (context);
4256 if (node == drag_highlight)
4258 /* Draw indicator for the drop
4260 gint highlight_y = -1;
4264 switch (tree_view->priv->drag_dest_pos)
4266 case PSPP_SHEET_VIEW_DROP_BEFORE:
4267 highlight_y = background_area.y - 1;
4268 if (highlight_y < 0)
4272 case PSPP_SHEET_VIEW_DROP_AFTER:
4273 highlight_y = background_area.y + background_area.height - 1;
4276 case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE:
4277 case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER:
4278 _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node);
4282 width = gdk_window_get_width (tree_view->priv->bin_window);
4284 if (row_ending_details)
4285 gtk_paint_focus (gtk_widget_get_style (widget),
4287 gtk_widget_get_state (widget),
4290 ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4291 : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4292 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4293 - focus_line_width / 2,
4294 width, ROW_HEIGHT (tree_view)
4295 - focus_line_width + 1);
4297 gtk_paint_focus (gtk_widget_get_style (widget),
4299 gtk_widget_get_state (widget),
4301 "treeview-drop-indicator",
4302 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4303 - focus_line_width / 2,
4304 width, ROW_HEIGHT (tree_view)
4305 - focus_line_width + 1);
4310 if (highlight_y >= 0)
4312 gdk_draw_line (event->window,
4313 widget->style->fg_gc[gtk_widget_get_state (widget)],
4316 rtl ? 0 : bin_window_width,
4322 y_offset += max_height;
4326 node = pspp_sheet_view_node_next (tree_view, node);
4329 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
4333 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
4340 while (y_offset < Zarea.height);
4343 pspp_sheet_view_draw_vertical_grid_lines (tree_view, cr, n_visible_columns,
4347 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4349 GdkRectangle *rectangles;
4352 gdk_region_get_rectangles (event->region,
4356 while (n_rectangles--)
4357 pspp_sheet_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
4359 g_free (rectangles);
4364 gtk_tree_path_free (cursor_path);
4367 gtk_tree_path_free (drag_dest_path);
4374 pspp_sheet_view_draw (GtkWidget *widget,
4377 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4378 GtkStyleContext *context;
4380 context = gtk_widget_get_style_context (widget);
4382 if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window))
4387 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->bin_window);
4388 pspp_sheet_view_draw_bin (widget, cr);
4391 /* We can't just chain up to Container::expose as it will try to send the
4392 * event to the headers, so we handle propagating it to our children
4393 * (eg. widgets being edited) ourselves.
4395 tmp_list = tree_view->priv->children;
4398 PsppSheetViewChild *child = tmp_list->data;
4399 tmp_list = tmp_list->next;
4401 gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
4406 gtk_render_background (context, cr,
4408 gtk_widget_get_allocated_width (widget),
4409 gtk_widget_get_allocated_height (widget));
4412 gtk_style_context_save (context);
4413 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_VIEW);
4415 if (gtk_cairo_should_draw_window (cr, tree_view->priv->header_window))
4417 gint n_visible_columns;
4420 for (list = tree_view->priv->columns; list != NULL; list = list->next)
4422 PsppSheetViewColumn *column = list->data;
4424 if (column == tree_view->priv->drag_column || !column->visible)
4427 if (span_intersects (column->allocation.x, column->allocation.width,
4428 (int) gtk_adjustment_get_value (tree_view->priv->hadjustment),
4429 (int) gtk_widget_get_allocated_width (widget))
4430 && column->button != NULL)
4431 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4432 column->button, cr);
4435 n_visible_columns = 0;
4436 for (list = tree_view->priv->columns; list; list = list->next)
4438 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4440 n_visible_columns ++;
4443 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->header_window);
4444 pspp_sheet_view_draw_vertical_grid_lines (tree_view,
4448 TREE_VIEW_HEADER_HEIGHT (tree_view));
4451 if (tree_view->priv->drag_window &&
4452 gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window))
4454 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4455 tree_view->priv->drag_column->button,
4459 gtk_style_context_restore (context);
4471 /* returns 0x1 when no column has been found -- yes it's hackish */
4472 static PsppSheetViewColumn *
4473 pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
4474 PsppSheetViewColumn *column,
4477 PsppSheetViewColumn *left_column = NULL;
4478 PsppSheetViewColumn *cur_column = NULL;
4481 if (!column->reorderable)
4482 return (PsppSheetViewColumn *)0x1;
4484 switch (drop_position)
4487 /* find first column where we can drop */
4488 tmp_list = tree_view->priv->columns;
4489 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4490 return (PsppSheetViewColumn *)0x1;
4494 g_assert (tmp_list);
4496 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4497 tmp_list = tmp_list->next;
4499 if (left_column && left_column->visible == FALSE)
4502 if (!tree_view->priv->column_drop_func)
4505 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4507 left_column = cur_column;
4514 if (!tree_view->priv->column_drop_func)
4517 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4520 return (PsppSheetViewColumn *)0x1;
4524 /* find first column after column where we can drop */
4525 tmp_list = tree_view->priv->columns;
4527 for (; tmp_list; tmp_list = tmp_list->next)
4528 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4531 if (!tmp_list || !tmp_list->next)
4532 return (PsppSheetViewColumn *)0x1;
4534 tmp_list = tmp_list->next;
4535 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4536 tmp_list = tmp_list->next;
4540 g_assert (tmp_list);
4542 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4543 tmp_list = tmp_list->next;
4545 if (left_column && left_column->visible == FALSE)
4547 left_column = cur_column;
4549 tmp_list = tmp_list->next;
4553 if (!tree_view->priv->column_drop_func)
4556 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4558 left_column = cur_column;
4565 if (!tree_view->priv->column_drop_func)
4568 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4571 return (PsppSheetViewColumn *)0x1;
4575 /* find first column before column where we can drop */
4576 tmp_list = tree_view->priv->columns;
4578 for (; tmp_list; tmp_list = tmp_list->next)
4579 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4582 if (!tmp_list || !tmp_list->prev)
4583 return (PsppSheetViewColumn *)0x1;
4585 tmp_list = tmp_list->prev;
4586 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4587 tmp_list = tmp_list->prev;
4591 g_assert (tmp_list);
4593 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4595 if (left_column && !left_column->visible)
4597 /*if (!tmp_list->prev)
4598 return (PsppSheetViewColumn *)0x1;
4601 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4602 tmp_list = tmp_list->prev->prev;
4605 cur_column = left_column;
4607 tmp_list = tmp_list->prev;
4611 if (!tree_view->priv->column_drop_func)
4614 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4617 cur_column = left_column;
4618 tmp_list = tmp_list->prev;
4621 if (!tree_view->priv->column_drop_func)
4624 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4627 return (PsppSheetViewColumn *)0x1;
4631 /* same as DROP_HOME case, but doing it backwards */
4632 tmp_list = g_list_last (tree_view->priv->columns);
4635 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4636 return (PsppSheetViewColumn *)0x1;
4640 g_assert (tmp_list);
4642 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4644 if (left_column && !left_column->visible)
4646 cur_column = left_column;
4647 tmp_list = tmp_list->prev;
4650 if (!tree_view->priv->column_drop_func)
4653 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4656 cur_column = left_column;
4657 tmp_list = tmp_list->prev;
4660 if (!tree_view->priv->column_drop_func)
4663 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4666 return (PsppSheetViewColumn *)0x1;
4670 return (PsppSheetViewColumn *)0x1;
4674 pspp_sheet_view_key_press (GtkWidget *widget,
4677 PsppSheetView *tree_view = (PsppSheetView *) widget;
4679 if (tree_view->priv->rubber_band_status)
4681 if (event->keyval == GDK_Escape)
4682 pspp_sheet_view_stop_rubber_band (tree_view);
4687 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4689 if (event->keyval == GDK_Escape)
4691 tree_view->priv->cur_reorder = NULL;
4692 pspp_sheet_view_button_release_drag_column (widget, NULL);
4697 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4699 GList *focus_column;
4702 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4704 for (focus_column = tree_view->priv->columns;
4706 focus_column = focus_column->next)
4708 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4710 if (column->button && gtk_widget_has_focus (column->button))
4715 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4716 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4717 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4719 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4721 if (!column->resizable)
4723 gtk_widget_error_bell (widget);
4727 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4728 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4730 gint old_width = column->resized_width;
4732 column->resized_width = MAX (column->resized_width,
4734 column->resized_width -= 2;
4735 if (column->resized_width < 0)
4736 column->resized_width = 0;
4738 if (column->min_width == -1)
4739 column->resized_width = MAX (column->button_request,
4740 column->resized_width);
4742 column->resized_width = MAX (column->min_width,
4743 column->resized_width);
4745 if (column->max_width != -1)
4746 column->resized_width = MIN (column->resized_width,
4749 column->use_resized_width = TRUE;
4751 if (column->resized_width != old_width)
4752 gtk_widget_queue_resize (widget);
4754 gtk_widget_error_bell (widget);
4756 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4757 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4759 gint old_width = column->resized_width;
4761 column->resized_width = MAX (column->resized_width,
4763 column->resized_width += 2;
4765 if (column->max_width != -1)
4766 column->resized_width = MIN (column->resized_width,
4769 column->use_resized_width = TRUE;
4771 if (column->resized_width != old_width)
4772 gtk_widget_queue_resize (widget);
4774 gtk_widget_error_bell (widget);
4781 (event->state & GDK_MOD1_MASK) &&
4782 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4783 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4784 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4785 || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4787 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4789 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4790 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4792 PsppSheetViewColumn *col;
4793 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
4794 if (col != (PsppSheetViewColumn *)0x1)
4795 pspp_sheet_view_move_column_after (tree_view, column, col);
4797 gtk_widget_error_bell (widget);
4799 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4800 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4802 PsppSheetViewColumn *col;
4803 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4804 if (col != (PsppSheetViewColumn *)0x1)
4805 pspp_sheet_view_move_column_after (tree_view, column, col);
4807 gtk_widget_error_bell (widget);
4809 else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4811 PsppSheetViewColumn *col;
4812 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4813 if (col != (PsppSheetViewColumn *)0x1)
4814 pspp_sheet_view_move_column_after (tree_view, column, col);
4816 gtk_widget_error_bell (widget);
4818 else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4820 PsppSheetViewColumn *col;
4821 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4822 if (col != (PsppSheetViewColumn *)0x1)
4823 pspp_sheet_view_move_column_after (tree_view, column, col);
4825 gtk_widget_error_bell (widget);
4832 /* Chain up to the parent class. It handles the keybindings. */
4833 if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4836 if (tree_view->priv->search_entry_avoid_unhandled_binding)
4838 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4842 /* We pass the event to the search_entry. If its text changes, then we start
4843 * the typeahead find capabilities. */
4844 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4845 && tree_view->priv->enable_search
4846 && !tree_view->priv->search_custom_entry_set)
4848 GdkEvent *new_event;
4850 const char *new_text;
4853 gboolean text_modified;
4854 gulong popup_menu_id;
4856 pspp_sheet_view_ensure_interactive_directory (tree_view);
4858 /* Make a copy of the current text */
4859 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4860 new_event = gdk_event_copy ((GdkEvent *) event);
4861 g_object_unref (((GdkEventKey *) new_event)->window);
4862 ((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window));
4863 gtk_widget_realize (tree_view->priv->search_window);
4865 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
4866 "popup-menu", G_CALLBACK (gtk_true),
4869 /* Move the entry off screen */
4870 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4871 gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4872 gdk_screen_get_width (screen) + 1,
4873 gdk_screen_get_height (screen) + 1);
4874 gtk_widget_show (tree_view->priv->search_window);
4876 /* Send the event to the window. If the preedit_changed signal is emitted
4877 * during this event, we will set priv->imcontext_changed */
4878 tree_view->priv->imcontext_changed = FALSE;
4879 retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4880 gdk_event_free (new_event);
4881 gtk_widget_hide (tree_view->priv->search_window);
4883 g_signal_handler_disconnect (tree_view->priv->search_entry,
4886 /* We check to make sure that the entry tried to handle the text, and that
4887 * the text has changed.
4889 new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
4890 text_modified = strcmp (old_text, new_text) != 0;
4892 if (tree_view->priv->imcontext_changed || /* we're in a preedit */
4893 (retval && text_modified)) /* ...or the text was modified */
4895 if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
4897 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
4902 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
4912 pspp_sheet_view_key_release (GtkWidget *widget,
4915 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4917 if (tree_view->priv->rubber_band_status)
4920 return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
4923 /* FIXME Is this function necessary? Can I get an enter_notify event
4924 * w/o either an expose event or a mouse motion event?
4927 pspp_sheet_view_enter_notify (GtkWidget *widget,
4928 GdkEventCrossing *event)
4930 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4934 /* Sanity check it */
4935 if (event->window != tree_view->priv->bin_window)
4938 if (tree_view->priv->row_count == 0)
4941 if (event->mode == GDK_CROSSING_GRAB ||
4942 event->mode == GDK_CROSSING_GTK_GRAB ||
4943 event->mode == GDK_CROSSING_GTK_UNGRAB ||
4944 event->mode == GDK_CROSSING_STATE_CHANGED)
4947 /* find the node internally */
4948 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
4951 pspp_sheet_view_find_offset (tree_view, new_y, &node);
4953 tree_view->priv->event_last_x = event->x;
4954 tree_view->priv->event_last_y = event->y;
4956 prelight_or_select (tree_view, node, event->x, event->y);
4962 pspp_sheet_view_leave_notify (GtkWidget *widget,
4963 GdkEventCrossing *event)
4965 PsppSheetView *tree_view;
4967 if (event->mode == GDK_CROSSING_GRAB)
4970 tree_view = PSPP_SHEET_VIEW (widget);
4972 if (tree_view->priv->prelight_node >= 0)
4973 _pspp_sheet_view_queue_draw_node (tree_view,
4974 tree_view->priv->prelight_node,
4977 tree_view->priv->event_last_x = -10000;
4978 tree_view->priv->event_last_y = -10000;
4980 prelight_or_select (tree_view,
4982 -1000, -1000); /* coords not possibly over an arrow */
4989 pspp_sheet_view_focus_out (GtkWidget *widget,
4990 GdkEventFocus *event)
4992 PsppSheetView *tree_view;
4994 tree_view = PSPP_SHEET_VIEW (widget);
4996 gtk_widget_queue_draw (widget);
4998 /* destroy interactive search dialog */
4999 if (tree_view->priv->search_window)
5000 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
5006 /* Incremental Reflow
5010 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
5013 GtkAllocation allocation;
5014 gint y = pspp_sheet_view_node_find_offset (tree_view, node)
5015 - gtk_adjustment_get_value (tree_view->priv->vadjustment)
5016 + TREE_VIEW_HEADER_HEIGHT (tree_view);
5018 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5020 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
5023 tree_view->priv->fixed_height);
5027 node_is_visible (PsppSheetView *tree_view,
5033 y = pspp_sheet_view_node_find_offset (tree_view, node);
5034 height = ROW_HEIGHT (tree_view);
5036 if (y >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5037 y + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5038 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5044 /* Returns the row height. */
5046 validate_row (PsppSheetView *tree_view,
5051 PsppSheetViewColumn *column;
5052 GList *list, *first_column, *last_column;
5054 gint horizontal_separator;
5055 gint vertical_separator;
5056 gint focus_line_width;
5057 gboolean draw_vgrid_lines, draw_hgrid_lines;
5059 gint grid_line_width;
5060 gboolean wide_separators;
5061 gint separator_height;
5063 gtk_widget_style_get (GTK_WIDGET (tree_view),
5064 "focus-padding", &focus_pad,
5065 "focus-line-width", &focus_line_width,
5066 "horizontal-separator", &horizontal_separator,
5067 "vertical-separator", &vertical_separator,
5068 "grid-line-width", &grid_line_width,
5069 "wide-separators", &wide_separators,
5070 "separator-height", &separator_height,
5074 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5075 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5077 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5078 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5080 for (last_column = g_list_last (tree_view->priv->columns);
5081 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5082 last_column = last_column->prev)
5085 for (first_column = g_list_first (tree_view->priv->columns);
5086 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5087 first_column = first_column->next)
5090 for (list = tree_view->priv->columns; list; list = list->next)
5095 column = list->data;
5097 if (! column->visible)
5100 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5101 pspp_sheet_view_column_cell_get_size (column,
5103 &tmp_width, &tmp_height);
5105 tmp_height += vertical_separator;
5106 height = MAX (height, tmp_height);
5108 tmp_width = tmp_width + horizontal_separator;
5110 if (draw_vgrid_lines)
5112 if (list->data == first_column || list->data == last_column)
5113 tmp_width += grid_line_width / 2.0;
5115 tmp_width += grid_line_width;
5118 if (tmp_width > column->requested_width)
5119 column->requested_width = tmp_width;
5122 if (draw_hgrid_lines)
5123 height += grid_line_width;
5125 tree_view->priv->post_validation_flag = TRUE;
5131 validate_visible_area (PsppSheetView *tree_view)
5133 GtkTreePath *path = NULL;
5134 GtkTreePath *above_path = NULL;
5138 gint area_above = 0;
5139 gint area_below = 0;
5140 GtkAllocation allocation;
5142 if (tree_view->priv->row_count == 0)
5145 if (tree_view->priv->scroll_to_path == NULL)
5148 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5150 total_height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5152 if (total_height == 0)
5155 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5158 /* we are going to scroll, and will update dy */
5159 _pspp_sheet_view_find_node (tree_view, path, &node);
5160 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5162 if (tree_view->priv->scroll_to_use_align)
5164 gint height = ROW_HEIGHT (tree_view);
5165 area_above = (total_height - height) *
5166 tree_view->priv->scroll_to_row_align;
5167 area_below = total_height - area_above - height;
5168 area_above = MAX (area_above, 0);
5169 area_below = MAX (area_below, 0);
5174 * 1) row not visible
5178 gint height = ROW_HEIGHT (tree_view);
5180 dy = pspp_sheet_view_node_find_offset (tree_view, node);
5182 if (dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5183 dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5184 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5186 /* row visible: keep the row at the same position */
5187 area_above = dy - gtk_adjustment_get_value (tree_view->priv->vadjustment);
5188 area_below = (gtk_adjustment_get_value (tree_view->priv->vadjustment) +
5189 gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5194 /* row not visible */
5196 && dy + height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5198 /* row at the beginning -- fixed */
5200 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment)
5201 - area_above - height;
5203 else if (dy >= (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5204 gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5206 /* row at the end -- fixed */
5207 area_above = dy - (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5208 gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
5209 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) -
5210 area_above - height;
5214 area_above = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - height;
5220 /* row somewhere in the middle, bring it to the top
5224 area_below = total_height - height;
5230 /* the scroll to isn't valid; ignore it.
5233 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5234 tree_view->priv->scroll_to_path = NULL;
5238 above_path = gtk_tree_path_copy (path);
5240 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5241 * backwards is much slower then forward, as there is no iter_prev function.
5242 * We go forwards first in case we run out of tree. Then we go backwards to
5245 while (node >= 0 && area_below > 0)
5247 gboolean done = FALSE;
5250 node = pspp_sheet_view_node_next (tree_view, node);
5253 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5255 gtk_tree_path_next (path);
5258 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5268 area_below -= ROW_HEIGHT (tree_view);
5270 gtk_tree_path_free (path);
5272 /* If we ran out of tree, and have extra area_below left, we need to add it
5275 area_above += area_below;
5277 _pspp_sheet_view_find_node (tree_view, above_path, &node);
5279 /* We walk backwards */
5280 while (area_above > 0)
5282 node = pspp_sheet_view_node_prev (tree_view, node);
5284 /* Always find the new path in the tree. We cannot just assume
5285 * a gtk_tree_path_prev() is enough here, as there might be children
5286 * in between this node and the previous sibling node. If this
5287 * appears to be a performance hotspot in profiles, we can look into
5288 * intrigate logic for keeping path, node and iter in sync like
5289 * we do for forward walks. (Which will be hard because of the lacking
5296 gtk_tree_path_free (above_path);
5297 above_path = _pspp_sheet_view_find_path (tree_view, node);
5299 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5301 area_above -= ROW_HEIGHT (tree_view);
5304 /* set the dy here to scroll to the path,
5305 * and sync the top row accordingly
5307 pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5308 pspp_sheet_view_top_row_to_dy (tree_view);
5310 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5311 tree_view->priv->scroll_to_path = NULL;
5314 gtk_tree_path_free (above_path);
5316 if (tree_view->priv->scroll_to_column)
5318 tree_view->priv->scroll_to_column = NULL;
5320 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5324 initialize_fixed_height_mode (PsppSheetView *tree_view)
5326 if (!tree_view->priv->row_count)
5329 if (tree_view->priv->fixed_height_set)
5332 if (tree_view->priv->fixed_height < 0)
5339 path = _pspp_sheet_view_find_path (tree_view, node);
5340 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5342 tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5344 gtk_tree_path_free (path);
5346 g_object_notify (G_OBJECT (tree_view), "fixed-height");
5350 /* Our strategy for finding nodes to validate is a little convoluted. We find
5351 * the left-most uninvalidated node. We then try walking right, validating
5352 * nodes. Once we find a valid node, we repeat the previous process of finding
5353 * the first invalid node.
5357 validate_rows_handler (PsppSheetView *tree_view)
5359 initialize_fixed_height_mode (tree_view);
5360 if (tree_view->priv->validate_rows_timer)
5362 g_source_remove (tree_view->priv->validate_rows_timer);
5363 tree_view->priv->validate_rows_timer = 0;
5370 do_presize_handler (PsppSheetView *tree_view)
5372 GtkRequisition requisition;
5374 validate_visible_area (tree_view);
5375 tree_view->priv->presize_handler_timer = 0;
5377 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5380 gtk_widget_get_preferred_size (GTK_WIDGET (tree_view), NULL, &requisition);
5382 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5383 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5384 gtk_adjustment_changed (tree_view->priv->hadjustment);
5385 gtk_adjustment_changed (tree_view->priv->vadjustment);
5386 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5392 presize_handler_callback (gpointer data)
5394 do_presize_handler (PSPP_SHEET_VIEW (data));
5400 install_presize_handler (PsppSheetView *tree_view)
5402 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5405 if (! tree_view->priv->presize_handler_timer)
5407 tree_view->priv->presize_handler_timer =
5408 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5410 if (! tree_view->priv->validate_rows_timer)
5412 tree_view->priv->validate_rows_timer =
5413 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5418 scroll_sync_handler (PsppSheetView *tree_view)
5420 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5421 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5422 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5423 pspp_sheet_view_top_row_to_dy (tree_view);
5425 pspp_sheet_view_dy_to_top_row (tree_view);
5427 tree_view->priv->scroll_sync_timer = 0;
5433 install_scroll_sync_handler (PsppSheetView *tree_view)
5435 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5438 if (!tree_view->priv->scroll_sync_timer)
5440 tree_view->priv->scroll_sync_timer =
5441 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5446 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5450 gtk_tree_row_reference_free (tree_view->priv->top_row);
5454 tree_view->priv->top_row = NULL;
5455 tree_view->priv->top_row_dy = 0;
5459 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5460 tree_view->priv->top_row_dy = offset;
5464 /* Always call this iff dy is in the visible range. If the tree is empty, then
5465 * it's set to be NULL, and top_row_dy is 0;
5468 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5474 if (tree_view->priv->row_count == 0)
5476 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5480 offset = pspp_sheet_view_find_offset (tree_view,
5481 tree_view->priv->dy,
5486 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5490 path = _pspp_sheet_view_find_path (tree_view, node);
5491 pspp_sheet_view_set_top_row (tree_view, path, offset);
5492 gtk_tree_path_free (path);
5498 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5504 /* Avoid recursive calls */
5505 if (tree_view->priv->in_top_row_to_dy)
5508 if (tree_view->priv->top_row)
5509 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5516 _pspp_sheet_view_find_node (tree_view, path, &node);
5519 gtk_tree_path_free (path);
5523 /* keep dy and set new toprow */
5524 gtk_tree_row_reference_free (tree_view->priv->top_row);
5525 tree_view->priv->top_row = NULL;
5526 tree_view->priv->top_row_dy = 0;
5527 /* DO NOT install the idle handler */
5528 pspp_sheet_view_dy_to_top_row (tree_view);
5532 if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5534 /* new top row -- do NOT install the idle handler */
5535 pspp_sheet_view_dy_to_top_row (tree_view);
5539 new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5540 new_dy += tree_view->priv->top_row_dy;
5542 if (new_dy + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
5543 new_dy = tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
5545 new_dy = MAX (0, new_dy);
5547 tree_view->priv->in_top_row_to_dy = TRUE;
5548 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5549 tree_view->priv->in_top_row_to_dy = FALSE;
5554 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5556 install_presize_handler (tree_view);
5562 set_source_row (GdkDragContext *context,
5563 GtkTreeModel *model,
5564 GtkTreePath *source_row)
5566 g_object_set_data_full (G_OBJECT (context),
5567 "gtk-tree-view-source-row",
5568 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5569 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5573 get_source_row (GdkDragContext *context)
5575 GtkTreeRowReference *ref =
5576 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5579 return gtk_tree_row_reference_get_path (ref);
5586 GtkTreeRowReference *dest_row;
5587 guint path_down_mode : 1;
5588 guint empty_view_drop : 1;
5589 guint drop_append_mode : 1;
5594 dest_row_free (gpointer data)
5596 DestRow *dr = (DestRow *)data;
5598 gtk_tree_row_reference_free (dr->dest_row);
5599 g_slice_free (DestRow, dr);
5603 set_dest_row (GdkDragContext *context,
5604 GtkTreeModel *model,
5605 GtkTreePath *dest_row,
5606 gboolean path_down_mode,
5607 gboolean empty_view_drop,
5608 gboolean drop_append_mode)
5614 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5619 dr = g_slice_new (DestRow);
5621 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5622 dr->path_down_mode = path_down_mode != FALSE;
5623 dr->empty_view_drop = empty_view_drop != FALSE;
5624 dr->drop_append_mode = drop_append_mode != FALSE;
5626 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5627 dr, (GDestroyNotify) dest_row_free);
5631 get_dest_row (GdkDragContext *context,
5632 gboolean *path_down_mode)
5635 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5639 GtkTreePath *path = NULL;
5642 *path_down_mode = dr->path_down_mode;
5645 path = gtk_tree_row_reference_get_path (dr->dest_row);
5646 else if (dr->empty_view_drop)
5647 path = gtk_tree_path_new_from_indices (0, -1);
5651 if (path && dr->drop_append_mode)
5652 gtk_tree_path_next (path);
5660 /* Get/set whether drag_motion requested the drag data and
5661 * drag_data_received should thus not actually insert the data,
5662 * since the data doesn't result from a drop.
5665 set_status_pending (GdkDragContext *context,
5666 GdkDragAction suggested_action)
5668 g_object_set_data (G_OBJECT (context),
5669 "gtk-tree-view-status-pending",
5670 GINT_TO_POINTER (suggested_action));
5673 static GdkDragAction
5674 get_status_pending (GdkDragContext *context)
5676 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5677 "gtk-tree-view-status-pending"));
5680 static TreeViewDragInfo*
5681 get_info (PsppSheetView *tree_view)
5683 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5687 destroy_info (TreeViewDragInfo *di)
5689 g_slice_free (TreeViewDragInfo, di);
5692 static TreeViewDragInfo*
5693 ensure_info (PsppSheetView *tree_view)
5695 TreeViewDragInfo *di;
5697 di = get_info (tree_view);
5701 di = g_slice_new0 (TreeViewDragInfo);
5703 g_object_set_data_full (G_OBJECT (tree_view),
5704 "gtk-tree-view-drag-info",
5706 (GDestroyNotify) destroy_info);
5713 remove_info (PsppSheetView *tree_view)
5715 g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5720 drag_scan_timeout (gpointer data)
5722 PsppSheetView *tree_view;
5724 GdkModifierType state;
5725 GtkTreePath *path = NULL;
5726 PsppSheetViewColumn *column = NULL;
5727 GdkRectangle visible_rect;
5729 GDK_THREADS_ENTER ();
5731 tree_view = PSPP_SHEET_VIEW (data);
5733 gdk_window_get_pointer (tree_view->priv->bin_window,
5736 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5738 /* See if we are near the edge. */
5739 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5740 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5741 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5742 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5744 pspp_sheet_view_get_path_at_pos (tree_view,
5745 tree_view->priv->bin_window,
5754 pspp_sheet_view_scroll_to_cell (tree_view,
5760 gtk_tree_path_free (path);
5764 GDK_THREADS_LEAVE ();
5771 add_scroll_timeout (PsppSheetView *tree_view)
5773 if (tree_view->priv->scroll_timeout == 0)
5775 tree_view->priv->scroll_timeout =
5776 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5781 remove_scroll_timeout (PsppSheetView *tree_view)
5783 if (tree_view->priv->scroll_timeout != 0)
5785 g_source_remove (tree_view->priv->scroll_timeout);
5786 tree_view->priv->scroll_timeout = 0;
5791 check_model_dnd (GtkTreeModel *model,
5792 GType required_iface,
5793 const gchar *signal)
5795 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5797 g_warning ("You must override the default '%s' handler "
5798 "on PsppSheetView when using models that don't support "
5799 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5800 "is to connect to '%s' and call "
5801 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5802 "the default handler from running. Look at the source code "
5803 "for the default handler in gtktreeview.c to get an idea what "
5804 "your handler should do. (gtktreeview.c is in the GTK source "
5805 "code.) If you're using GTK from a language other than C, "
5806 "there may be a more natural way to override default handlers, e.g. via derivation.",
5807 signal, g_type_name (required_iface), signal);
5815 scroll_row_timeout (gpointer data)
5817 PsppSheetView *tree_view = data;
5819 pspp_sheet_view_horizontal_autoscroll (tree_view);
5820 pspp_sheet_view_vertical_autoscroll (tree_view);
5822 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5823 pspp_sheet_view_update_rubber_band (tree_view);
5828 /* Returns TRUE if event should not be propagated to parent widgets */
5830 set_destination_row (PsppSheetView *tree_view,
5831 GdkDragContext *context,
5832 /* coordinates relative to the widget */
5835 GdkDragAction *suggested_action,
5838 GtkTreePath *path = NULL;
5839 PsppSheetViewDropPosition pos;
5840 PsppSheetViewDropPosition old_pos;
5841 TreeViewDragInfo *di;
5843 GtkTreePath *old_dest_path = NULL;
5844 gboolean can_drop = FALSE;
5846 *suggested_action = 0;
5849 widget = GTK_WIDGET (tree_view);
5851 di = get_info (tree_view);
5853 if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5855 /* someone unset us as a drag dest, note that if
5856 * we return FALSE drag_leave isn't called
5859 pspp_sheet_view_set_drag_dest_row (tree_view,
5861 PSPP_SHEET_VIEW_DROP_BEFORE);
5863 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5865 return FALSE; /* no longer a drop site */
5868 *target = gtk_drag_dest_find_target (widget, context,
5869 gtk_drag_dest_get_target_list (widget));
5870 if (*target == GDK_NONE)
5875 if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
5881 GtkTreeModel *model;
5883 /* the row got dropped on empty space, let's setup a special case
5887 gtk_tree_path_free (path);
5889 model = pspp_sheet_view_get_model (tree_view);
5891 n_children = gtk_tree_model_iter_n_children (model, NULL);
5894 pos = PSPP_SHEET_VIEW_DROP_AFTER;
5895 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
5899 pos = PSPP_SHEET_VIEW_DROP_BEFORE;
5900 path = gtk_tree_path_new_from_indices (0, -1);
5910 /* If we left the current row's "open" zone, unset the timeout for
5913 pspp_sheet_view_get_drag_dest_row (tree_view,
5918 gtk_tree_path_free (old_dest_path);
5920 if (TRUE /* FIXME if the location droppable predicate */)
5928 GtkWidget *source_widget;
5930 *suggested_action = gdk_drag_context_get_suggested_action (context);
5931 source_widget = gtk_drag_get_source_widget (context);
5933 if (source_widget == widget)
5935 /* Default to MOVE, unless the user has
5936 * pressed ctrl or shift to affect available actions
5938 if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
5939 *suggested_action = GDK_ACTION_MOVE;
5942 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5947 /* can't drop here */
5948 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5950 PSPP_SHEET_VIEW_DROP_BEFORE);
5954 gtk_tree_path_free (path);
5960 get_logical_dest_row (PsppSheetView *tree_view,
5961 gboolean *path_down_mode,
5962 gboolean *drop_append_mode)
5964 /* adjust path to point to the row the drop goes in front of */
5965 GtkTreePath *path = NULL;
5966 PsppSheetViewDropPosition pos;
5968 g_return_val_if_fail (path_down_mode != NULL, NULL);
5969 g_return_val_if_fail (drop_append_mode != NULL, NULL);
5971 *path_down_mode = FALSE;
5972 *drop_append_mode = 0;
5974 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
5979 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
5981 else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
5982 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
5983 *path_down_mode = TRUE;
5987 GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
5989 g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
5991 if (!gtk_tree_model_get_iter (model, &iter, path) ||
5992 !gtk_tree_model_iter_next (model, &iter))
5993 *drop_append_mode = 1;
5996 *drop_append_mode = 0;
5997 gtk_tree_path_next (path);
6005 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
6006 GdkEventMotion *event)
6008 GtkWidget *widget = GTK_WIDGET (tree_view);
6009 GdkDragContext *context;
6010 TreeViewDragInfo *di;
6011 GtkTreePath *path = NULL;
6013 gint cell_x, cell_y;
6014 GtkTreeModel *model;
6015 gboolean retval = FALSE;
6017 di = get_info (tree_view);
6019 if (di == NULL || !di->source_set)
6022 if (tree_view->priv->pressed_button < 0)
6025 if (!gtk_drag_check_threshold (widget,
6026 tree_view->priv->press_start_x,
6027 tree_view->priv->press_start_y,
6028 event->x, event->y))
6031 model = pspp_sheet_view_get_model (tree_view);
6036 button = tree_view->priv->pressed_button;
6037 tree_view->priv->pressed_button = -1;
6039 pspp_sheet_view_get_path_at_pos (tree_view,
6040 tree_view->priv->press_start_x,
6041 tree_view->priv->press_start_y,
6050 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6051 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6055 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6058 /* Now we can begin the drag */
6062 context = gtk_drag_begin (widget,
6063 gtk_drag_source_get_target_list (widget),
6068 set_source_row (context, model, path);
6072 gtk_tree_path_free (path);
6080 pspp_sheet_view_drag_begin (GtkWidget *widget,
6081 GdkDragContext *context)
6084 PsppSheetView *tree_view;
6085 GtkTreePath *path = NULL;
6086 gint cell_x, cell_y;
6088 TreeViewDragInfo *di;
6090 tree_view = PSPP_SHEET_VIEW (widget);
6092 /* if the user uses a custom DND source impl, we don't set the icon here */
6093 di = get_info (tree_view);
6095 if (di == NULL || !di->source_set)
6098 pspp_sheet_view_get_path_at_pos (tree_view,
6099 tree_view->priv->press_start_x,
6100 tree_view->priv->press_start_y,
6106 g_return_if_fail (path != NULL);
6108 row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6111 gtk_drag_set_icon_pixmap (context,
6112 gdk_drawable_get_colormap (row_pix),
6115 /* the + 1 is for the black border in the icon */
6116 tree_view->priv->press_start_x + 1,
6119 g_object_unref (row_pix);
6120 gtk_tree_path_free (path);
6126 pspp_sheet_view_drag_end (GtkWidget *widget,
6127 GdkDragContext *context)
6132 /* Default signal implementations for the drag signals */
6134 pspp_sheet_view_drag_data_get (GtkWidget *widget,
6135 GdkDragContext *context,
6136 GtkSelectionData *selection_data,
6140 PsppSheetView *tree_view;
6141 GtkTreeModel *model;
6142 TreeViewDragInfo *di;
6143 GtkTreePath *source_row;
6145 tree_view = PSPP_SHEET_VIEW (widget);
6147 model = pspp_sheet_view_get_model (tree_view);
6152 di = get_info (PSPP_SHEET_VIEW (widget));
6157 source_row = get_source_row (context);
6159 if (source_row == NULL)
6162 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6163 * any model; for DragSource models there are some other targets
6167 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6168 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6173 /* If drag_data_get does nothing, try providing row data. */
6174 if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6176 gtk_tree_set_row_drag_data (selection_data,
6182 gtk_tree_path_free (source_row);
6187 pspp_sheet_view_drag_data_delete (GtkWidget *widget,
6188 GdkDragContext *context)
6190 TreeViewDragInfo *di;
6191 GtkTreeModel *model;
6192 PsppSheetView *tree_view;
6193 GtkTreePath *source_row;
6195 tree_view = PSPP_SHEET_VIEW (widget);
6196 model = pspp_sheet_view_get_model (tree_view);
6198 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6201 di = get_info (tree_view);
6206 source_row = get_source_row (context);
6208 if (source_row == NULL)
6211 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6214 gtk_tree_path_free (source_row);
6216 set_source_row (context, NULL, NULL);
6220 pspp_sheet_view_drag_leave (GtkWidget *widget,
6221 GdkDragContext *context,
6224 /* unset any highlight row */
6225 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6227 PSPP_SHEET_VIEW_DROP_BEFORE);
6229 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6234 pspp_sheet_view_drag_motion (GtkWidget *widget,
6235 GdkDragContext *context,
6236 /* coordinates relative to the widget */
6242 GtkTreePath *path = NULL;
6243 PsppSheetViewDropPosition pos;
6244 PsppSheetView *tree_view;
6245 GdkDragAction suggested_action = 0;
6248 tree_view = PSPP_SHEET_VIEW (widget);
6250 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6253 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6255 /* we only know this *after* set_desination_row */
6256 empty = tree_view->priv->empty_view_drop;
6258 if (path == NULL && !empty)
6260 /* Can't drop here. */
6261 gdk_drag_status (context, 0, time);
6265 if (tree_view->priv->open_dest_timeout == 0 &&
6266 (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6267 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6273 add_scroll_timeout (tree_view);
6276 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6278 /* Request data so we can use the source row when
6279 * determining whether to accept the drop
6281 set_status_pending (context, suggested_action);
6282 gtk_drag_get_data (widget, context, target, time);
6286 set_status_pending (context, 0);
6287 gdk_drag_status (context, suggested_action, time);
6292 gtk_tree_path_free (path);
6299 pspp_sheet_view_drag_drop (GtkWidget *widget,
6300 GdkDragContext *context,
6301 /* coordinates relative to the widget */
6306 PsppSheetView *tree_view;
6308 GdkDragAction suggested_action = 0;
6309 GdkAtom target = GDK_NONE;
6310 TreeViewDragInfo *di;
6311 GtkTreeModel *model;
6312 gboolean path_down_mode;
6313 gboolean drop_append_mode;
6315 tree_view = PSPP_SHEET_VIEW (widget);
6317 model = pspp_sheet_view_get_model (tree_view);
6319 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6321 di = get_info (tree_view);
6326 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6329 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6332 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6334 if (target != GDK_NONE && path != NULL)
6336 /* in case a motion had requested drag data, change things so we
6337 * treat drag data receives as a drop.
6339 set_status_pending (context, 0);
6340 set_dest_row (context, model, path,
6341 path_down_mode, tree_view->priv->empty_view_drop,
6346 gtk_tree_path_free (path);
6348 /* Unset this thing */
6349 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6351 PSPP_SHEET_VIEW_DROP_BEFORE);
6353 if (target != GDK_NONE)
6355 gtk_drag_get_data (widget, context, target, time);
6363 pspp_sheet_view_drag_data_received (GtkWidget *widget,
6364 GdkDragContext *context,
6365 /* coordinates relative to the widget */
6368 GtkSelectionData *selection_data,
6373 TreeViewDragInfo *di;
6374 gboolean accepted = FALSE;
6375 GtkTreeModel *model;
6376 PsppSheetView *tree_view;
6377 GtkTreePath *dest_row;
6378 GdkDragAction suggested_action;
6379 gboolean path_down_mode;
6380 gboolean drop_append_mode;
6382 tree_view = PSPP_SHEET_VIEW (widget);
6384 model = pspp_sheet_view_get_model (tree_view);
6386 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6389 di = get_info (tree_view);
6394 suggested_action = get_status_pending (context);
6396 if (suggested_action)
6398 /* We are getting this data due to a request in drag_motion,
6399 * rather than due to a request in drag_drop, so we are just
6400 * supposed to call drag_status, not actually paste in the
6403 path = get_logical_dest_row (tree_view, &path_down_mode,
6407 suggested_action = 0;
6408 else if (path_down_mode)
6409 gtk_tree_path_down (path);
6411 if (suggested_action)
6413 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6419 path_down_mode = FALSE;
6420 gtk_tree_path_up (path);
6422 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6425 suggested_action = 0;
6428 suggested_action = 0;
6432 gdk_drag_status (context, suggested_action, time);
6435 gtk_tree_path_free (path);
6437 /* If you can't drop, remove user drop indicator until the next motion */
6438 if (suggested_action == 0)
6439 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6441 PSPP_SHEET_VIEW_DROP_BEFORE);
6446 dest_row = get_dest_row (context, &path_down_mode);
6448 if (dest_row == NULL)
6451 if (gtk_selection_data_get_length (selection_data) >= 0)
6455 gtk_tree_path_down (dest_row);
6456 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6457 dest_row, selection_data))
6458 gtk_tree_path_up (dest_row);
6462 if (gtk_selection_data_get_length (selection_data) >= 0)
6464 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6470 gtk_drag_finish (context,
6472 (gdk_drag_context_get_actions (context) == GDK_ACTION_MOVE),
6475 if (gtk_tree_path_get_depth (dest_row) == 1
6476 && gtk_tree_path_get_indices (dest_row)[0] == 0)
6478 /* special special case drag to "0", scroll to first item */
6479 if (!tree_view->priv->scroll_to_path)
6480 pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6483 gtk_tree_path_free (dest_row);
6486 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6491 /* GtkContainer Methods
6496 pspp_sheet_view_remove (GtkContainer *container,
6499 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6500 PsppSheetViewChild *child = NULL;
6503 tmp_list = tree_view->priv->children;
6506 child = tmp_list->data;
6507 if (child->widget == widget)
6509 gtk_widget_unparent (widget);
6511 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6512 g_list_free_1 (tmp_list);
6513 g_slice_free (PsppSheetViewChild, child);
6517 tmp_list = tmp_list->next;
6520 tmp_list = tree_view->priv->columns;
6524 PsppSheetViewColumn *column;
6525 column = tmp_list->data;
6526 if (column->button == widget)
6528 gtk_widget_unparent (widget);
6531 tmp_list = tmp_list->next;
6536 pspp_sheet_view_forall (GtkContainer *container,
6537 gboolean include_internals,
6538 GtkCallback callback,
6539 gpointer callback_data)
6541 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6542 PsppSheetViewChild *child = NULL;
6543 PsppSheetViewColumn *column;
6546 tmp_list = tree_view->priv->children;
6549 child = tmp_list->data;
6550 tmp_list = tmp_list->next;
6552 (* callback) (child->widget, callback_data);
6554 if (include_internals == FALSE)
6557 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6559 column = tmp_list->data;
6562 (* callback) (column->button, callback_data);
6566 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6567 * cells. If so we draw one big row-spanning focus rectangle.
6570 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6574 if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6575 return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6577 for (list = tree_view->priv->columns; list; list = list->next)
6579 if (!((PsppSheetViewColumn *)list->data)->visible)
6581 if (_pspp_sheet_view_column_count_special_cells (list->data))
6589 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6590 PsppSheetViewColumn *focus_column,
6591 gboolean clamp_column_visible)
6593 g_return_if_fail (focus_column != NULL);
6595 tree_view->priv->focus_column = focus_column;
6597 if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view)) != focus_column->button)
6598 gtk_widget_grab_focus (focus_column->button);
6600 if (clamp_column_visible)
6601 pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6604 /* Returns TRUE if the focus is within the headers, after the focus operation is
6608 pspp_sheet_view_header_focus (PsppSheetView *tree_view,
6609 GtkDirectionType dir,
6610 gboolean clamp_column_visible)
6612 GtkWidget *focus_child;
6613 PsppSheetViewColumn *focus_column;
6614 GList *last_column, *first_column;
6618 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6621 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
6623 first_column = tree_view->priv->columns;
6624 while (first_column)
6626 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6628 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6630 first_column = first_column->next;
6633 /* No headers are visible, or are focusable. We can't focus in or out.
6635 if (first_column == NULL)
6638 last_column = g_list_last (tree_view->priv->columns);
6641 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6643 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6645 last_column = last_column->prev;
6649 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6653 case GTK_DIR_TAB_BACKWARD:
6654 case GTK_DIR_TAB_FORWARD:
6657 if (focus_child == NULL)
6659 if (tree_view->priv->focus_column != NULL &&
6660 pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6661 focus_column = tree_view->priv->focus_column;
6663 focus_column = first_column->data;
6664 pspp_sheet_view_focus_column (tree_view, focus_column,
6665 clamp_column_visible);
6672 if (focus_child == NULL)
6674 if (tree_view->priv->focus_column != NULL)
6675 focus_column = tree_view->priv->focus_column;
6676 else if (dir == GTK_DIR_LEFT)
6677 focus_column = last_column->data;
6679 focus_column = first_column->data;
6680 pspp_sheet_view_focus_column (tree_view, focus_column,
6681 clamp_column_visible);
6685 if (gtk_widget_child_focus (focus_child, dir))
6687 /* The focus moves inside the button. */
6688 /* This is probably a great example of bad UI */
6689 if (clamp_column_visible)
6690 pspp_sheet_view_clamp_column_visible (tree_view,
6691 tree_view->priv->focus_column,
6696 /* We need to move the focus among the row of buttons. */
6697 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6698 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6701 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6702 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6704 gtk_widget_error_bell (GTK_WIDGET (tree_view));
6710 PsppSheetViewColumn *column;
6712 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6713 tmp_list = tmp_list->next;
6715 tmp_list = tmp_list->prev;
6717 if (tmp_list == NULL)
6719 g_warning ("Internal button not found");
6722 column = tmp_list->data;
6723 if (column->visible &&
6724 pspp_sheet_view_column_can_focus (column))
6728 pspp_sheet_view_focus_column (tree_view, column,
6729 clamp_column_visible);
6737 g_assert_not_reached ();
6744 /* This function returns in 'path' the first focusable path, if the given path
6745 * is already focusable, it's the returned one.
6749 search_first_focusable_path (PsppSheetView *tree_view,
6751 gboolean search_forward,
6754 /* XXX this function is trivial given that the sheetview doesn't support
6758 if (!path || !*path)
6761 _pspp_sheet_view_find_node (tree_view, *path, &node);
6769 return (*path != NULL);
6773 pspp_sheet_view_focus (GtkWidget *widget,
6774 GtkDirectionType direction)
6776 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6777 GtkContainer *container = GTK_CONTAINER (widget);
6778 GtkWidget *focus_child;
6780 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6783 focus_child = gtk_container_get_focus_child (container);
6785 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6786 /* Case 1. Headers currently have focus. */
6793 pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6795 case GTK_DIR_TAB_BACKWARD:
6798 case GTK_DIR_TAB_FORWARD:
6800 gtk_widget_grab_focus (widget);
6803 g_assert_not_reached ();
6808 /* Case 2. We don't have focus at all. */
6809 if (!gtk_widget_has_focus (widget))
6811 if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6812 gtk_widget_grab_focus (widget);
6816 /* Case 3. We have focus already. */
6817 if (direction == GTK_DIR_TAB_BACKWARD)
6818 return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6819 else if (direction == GTK_DIR_TAB_FORWARD)
6822 /* Other directions caught by the keybindings */
6823 gtk_widget_grab_focus (widget);
6828 pspp_sheet_view_grab_focus (GtkWidget *widget)
6830 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6832 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6836 pspp_sheet_view_style_updated (GtkWidget *widget)
6838 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6840 PsppSheetViewColumn *column;
6841 GtkStyleContext *context;
6843 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->style_updated (widget);
6845 if (gtk_widget_get_realized (widget))
6847 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
6848 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
6849 gtk_style_context_set_background (context, tree_view->priv->header_window);
6850 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6853 gtk_widget_style_get (widget,
6854 "expander-size", &tree_view->priv->expander_size,
6856 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6858 for (list = tree_view->priv->columns; list; list = list->next)
6860 column = list->data;
6861 _pspp_sheet_view_column_cell_set_dirty (column);
6864 tree_view->priv->fixed_height = -1;
6866 /* Invalidate cached button style. */
6867 if (tree_view->priv->button_style)
6869 g_object_unref (tree_view->priv->button_style);
6870 tree_view->priv->button_style = NULL;
6873 gtk_widget_queue_resize (widget);
6878 pspp_sheet_view_set_focus_child (GtkContainer *container,
6881 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6884 for (list = tree_view->priv->columns; list; list = list->next)
6886 if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
6888 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
6893 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
6897 pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
6898 GtkAdjustment *hadj,
6899 GtkAdjustment *vadj)
6901 gboolean need_adjust = FALSE;
6903 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
6906 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
6908 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6910 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
6912 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6914 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
6916 g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
6917 pspp_sheet_view_adjustment_changed,
6919 g_object_unref (tree_view->priv->hadjustment);
6922 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
6924 g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
6925 pspp_sheet_view_adjustment_changed,
6927 g_object_unref (tree_view->priv->vadjustment);
6930 if (tree_view->priv->hadjustment != hadj)
6932 tree_view->priv->hadjustment = hadj;
6933 g_object_ref_sink (tree_view->priv->hadjustment);
6935 g_signal_connect (tree_view->priv->hadjustment, "value-changed",
6936 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6941 if (tree_view->priv->vadjustment != vadj)
6943 tree_view->priv->vadjustment = vadj;
6944 g_object_ref_sink (tree_view->priv->vadjustment);
6946 g_signal_connect (tree_view->priv->vadjustment, "value-changed",
6947 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6953 pspp_sheet_view_adjustment_changed (NULL, tree_view);
6958 pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
6959 GtkMovementStep step,
6962 PsppSheetSelectMode mode;
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 ||
6971 step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
6973 if (tree_view->priv->row_count == 0)
6975 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
6978 pspp_sheet_view_stop_editing (tree_view, FALSE);
6979 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
6980 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
6983 if (gtk_get_current_event_state (&state))
6985 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
6986 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
6987 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
6988 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
6990 /* else we assume not pressed */
6994 case GTK_MOVEMENT_LOGICAL_POSITIONS:
6995 pspp_sheet_view_move_cursor_tab (tree_view, count);
6997 case GTK_MOVEMENT_VISUAL_POSITIONS:
6998 pspp_sheet_view_move_cursor_left_right (tree_view, count, mode);
7000 case GTK_MOVEMENT_DISPLAY_LINES:
7001 pspp_sheet_view_move_cursor_up_down (tree_view, count, mode);
7003 case GTK_MOVEMENT_PAGES:
7004 pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode);
7006 case GTK_MOVEMENT_BUFFER_ENDS:
7007 pspp_sheet_view_move_cursor_start_end (tree_view, count, mode);
7009 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7010 pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode);
7013 g_assert_not_reached ();
7020 pspp_sheet_view_put (PsppSheetView *tree_view,
7021 GtkWidget *child_widget,
7023 PsppSheetViewColumn *column)
7025 PsppSheetViewChild *child;
7027 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7028 g_return_if_fail (GTK_IS_WIDGET (child_widget));
7030 child = g_slice_new (PsppSheetViewChild);
7032 child->widget = child_widget;
7033 _pspp_sheet_view_find_node (tree_view, path, &child->node);
7034 if (child->node < 0)
7036 g_assert_not_reached ();
7038 child->column = column;
7040 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7042 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7043 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7045 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7048 /* TreeModel Callbacks
7052 pspp_sheet_view_row_changed (GtkTreeModel *model,
7057 PsppSheetView *tree_view = (PsppSheetView *)data;
7059 gboolean free_path = FALSE;
7060 GtkTreePath *cursor_path;
7062 g_return_if_fail (path != NULL || iter != NULL);
7064 if (tree_view->priv->cursor != NULL)
7065 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7069 if (tree_view->priv->edited_column &&
7070 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7071 pspp_sheet_view_stop_editing (tree_view, TRUE);
7073 if (cursor_path != NULL)
7074 gtk_tree_path_free (cursor_path);
7078 path = gtk_tree_model_get_path (model, iter);
7081 else if (iter == NULL)
7082 gtk_tree_model_get_iter (model, iter, path);
7084 _pspp_sheet_view_find_node (tree_view,
7090 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7091 pspp_sheet_view_node_queue_redraw (tree_view, node);
7095 gtk_tree_path_free (path);
7099 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7104 PsppSheetView *tree_view = (PsppSheetView *) data;
7107 gint height = tree_view->priv->fixed_height;
7108 gboolean free_path = FALSE;
7109 gboolean node_visible = TRUE;
7111 g_return_if_fail (path != NULL || iter != NULL);
7115 path = gtk_tree_model_get_path (model, iter);
7118 else if (iter == NULL)
7119 gtk_tree_model_get_iter (model, iter, path);
7121 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7123 /* Update all row-references */
7124 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7125 indices = gtk_tree_path_get_indices (path);
7126 tmpnode = indices[0];
7128 range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7132 if (node_visible && node_is_visible (tree_view, tmpnode))
7133 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7135 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7138 install_presize_handler (tree_view);
7140 gtk_tree_path_free (path);
7144 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7148 PsppSheetView *tree_view = (PsppSheetView *)data;
7151 g_return_if_fail (path != NULL);
7153 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7155 _pspp_sheet_view_find_node (tree_view, path, &node);
7160 range_tower_delete (tree_view->priv->selected, node, 1);
7162 /* Ensure we don't have a dangling pointer to a dead node */
7163 ensure_unprelighted (tree_view);
7165 /* Cancel editting if we've started */
7166 pspp_sheet_view_stop_editing (tree_view, TRUE);
7168 if (tree_view->priv->destroy_count_func)
7170 gint child_count = 0;
7171 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7174 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7176 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7178 gtk_tree_row_reference_free (tree_view->priv->top_row);
7179 tree_view->priv->top_row = NULL;
7182 install_scroll_sync_handler (tree_view);
7184 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7187 if (helper_data.changed)
7188 g_signal_emit_by_name (tree_view->priv->selection, "changed");
7193 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7194 GtkTreePath *parent,
7199 PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7202 /* XXX need to adjust selection */
7203 len = gtk_tree_model_iter_n_children (model, iter);
7208 gtk_tree_row_reference_reordered (G_OBJECT (data),
7213 if (gtk_tree_path_get_depth (parent) != 0)
7216 if (tree_view->priv->edited_column)
7217 pspp_sheet_view_stop_editing (tree_view, TRUE);
7219 /* we need to be unprelighted */
7220 ensure_unprelighted (tree_view);
7222 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7224 pspp_sheet_view_dy_to_top_row (tree_view);
7228 /* Internal tree functions
7233 pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view,
7234 PsppSheetViewColumn *column,
7238 PsppSheetViewColumn *tmp_column = NULL;
7249 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7252 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7254 list = (rtl ? list->prev : list->next))
7256 tmp_column = list->data;
7258 if (tmp_column == column)
7261 if (tmp_column->visible)
7262 total_width += tmp_column->width;
7265 if (tmp_column != column)
7267 g_warning (G_STRLOC": passed-in column isn't in the tree");
7276 if (column->visible)
7277 *x2 = total_width + column->width;
7279 *x2 = total_width; /* width of 0 */
7283 /* Make sure the node is visible vertically */
7285 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7288 gint node_dy, height;
7289 GtkTreePath *path = NULL;
7291 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7294 /* just return if the node is visible, avoiding a costly expose */
7295 node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7296 height = ROW_HEIGHT (tree_view);
7297 if (node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment)
7298 && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
7299 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
7302 path = _pspp_sheet_view_find_path (tree_view, node);
7305 /* We process updates because we want to clear old selected items when we scroll.
7306 * if this is removed, we get a "selection streak" at the bottom. */
7307 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7308 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7309 gtk_tree_path_free (path);
7314 pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
7315 PsppSheetViewColumn *column,
7316 gboolean focus_to_cell)
7323 x = column->allocation.x;
7324 width = column->allocation.width;
7326 if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7328 /* The column is larger than the horizontal page size. If the
7329 * column has cells which can be focussed individually, then we make
7330 * sure the cell which gets focus is fully visible (if even the
7331 * focus cell is bigger than the page size, we make sure the
7332 * left-hand side of the cell is visible).
7334 * If the column does not have those so-called special cells, we
7335 * make sure the left-hand side of the column is visible.
7338 if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7340 GtkTreePath *cursor_path;
7341 GdkRectangle background_area, cell_area, focus_area;
7343 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7345 pspp_sheet_view_get_cell_area (tree_view,
7346 cursor_path, column, &cell_area);
7347 pspp_sheet_view_get_background_area (tree_view,
7348 cursor_path, column,
7351 gtk_tree_path_free (cursor_path);
7353 _pspp_sheet_view_column_get_focus_area (column,
7359 width = focus_area.width;
7361 if (width < gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7363 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7364 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7365 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7366 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7367 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7371 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7373 gtk_adjustment_get_lower (tree_view->priv->hadjustment),
7374 gtk_adjustment_get_upper (tree_view->priv->hadjustment)
7375 - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)));
7379 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7380 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7381 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7382 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7383 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7388 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7393 path = gtk_tree_path_new ();
7395 gtk_tree_path_append_index (path, node);
7400 _pspp_sheet_view_find_node (PsppSheetView *tree_view,
7404 gint *indices = gtk_tree_path_get_indices (path);
7405 gint depth = gtk_tree_path_get_depth (path);
7408 if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7414 pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
7417 gboolean add_shifted_binding,
7418 GtkMovementStep step,
7422 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7427 if (add_shifted_binding)
7428 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7433 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7436 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7441 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7448 pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view,
7449 PsppSheetViewColumn *column)
7451 PsppSheetViewColumn *left_column;
7452 PsppSheetViewColumn *cur_column = NULL;
7453 PsppSheetViewColumnReorder *reorder;
7458 /* We want to precalculate the motion list such that we know what column slots
7462 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7464 /* First, identify all possible drop spots */
7466 tmp_list = g_list_last (tree_view->priv->columns);
7468 tmp_list = g_list_first (tree_view->priv->columns);
7472 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7473 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7475 if (cur_column->visible == FALSE)
7478 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7479 if (left_column != column && cur_column != column &&
7480 tree_view->priv->column_drop_func &&
7481 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7483 left_column = cur_column;
7486 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7487 reorder->left_column = left_column;
7488 left_column = reorder->right_column = cur_column;
7490 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7493 /* Add the last one */
7494 if (tree_view->priv->column_drop_func == NULL ||
7495 ((left_column != column) &&
7496 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7498 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7499 reorder->left_column = left_column;
7500 reorder->right_column = NULL;
7501 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7504 /* We quickly check to see if it even makes sense to reorder columns. */
7505 /* If there is nothing that can be moved, then we return */
7507 if (tree_view->priv->column_drag_info == NULL)
7510 /* We know there are always 2 slots possbile, as you can always return column. */
7511 /* If that's all there is, return */
7512 if (tree_view->priv->column_drag_info->next == NULL ||
7513 (tree_view->priv->column_drag_info->next->next == NULL &&
7514 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7515 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7517 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7518 g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7519 g_list_free (tree_view->priv->column_drag_info);
7520 tree_view->priv->column_drag_info = NULL;
7523 /* We fill in the ranges for the columns, now that we've isolated them */
7524 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7526 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7528 reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7530 reorder->left_align = left;
7531 if (tmp_list->next != NULL)
7533 g_assert (tmp_list->next->data);
7534 left = reorder->right_align = (reorder->right_column->allocation.x +
7535 reorder->right_column->allocation.width +
7536 ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7540 gint width = gdk_window_get_width (tree_view->priv->header_window);
7541 reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7547 _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view,
7548 PsppSheetViewColumn *column)
7550 GdkEvent *send_event;
7551 GtkAllocation allocation;
7553 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7554 GdkDisplay *display = gdk_screen_get_display (screen);
7556 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7557 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7558 g_return_if_fail (column->button);
7560 pspp_sheet_view_set_column_drag_info (tree_view, column);
7562 if (tree_view->priv->column_drag_info == NULL)
7565 if (tree_view->priv->drag_window == NULL)
7567 GdkWindowAttr attributes;
7568 guint attributes_mask;
7570 attributes.window_type = GDK_WINDOW_CHILD;
7571 attributes.wclass = GDK_INPUT_OUTPUT;
7572 attributes.x = column->allocation.x;
7574 attributes.width = column->allocation.width;
7575 attributes.height = column->allocation.height;
7576 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7577 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7578 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL ;
7580 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7583 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7586 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7587 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7589 gtk_grab_remove (column->button);
7591 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7592 send_event->crossing.send_event = TRUE;
7593 send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button)));
7594 send_event->crossing.subwindow = NULL;
7595 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7596 send_event->crossing.time = GDK_CURRENT_TIME;
7598 gtk_propagate_event (column->button, send_event);
7599 gdk_event_free (send_event);
7601 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7602 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7603 send_event->button.send_event = TRUE;
7604 send_event->button.time = GDK_CURRENT_TIME;
7605 send_event->button.x = -1;
7606 send_event->button.y = -1;
7607 send_event->button.axes = NULL;
7608 send_event->button.state = 0;
7609 send_event->button.button = 1;
7610 send_event->button.device =
7611 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
7613 send_event->button.x_root = 0;
7614 send_event->button.y_root = 0;
7616 gtk_propagate_event (column->button, send_event);
7617 gdk_event_free (send_event);
7619 /* Kids, don't try this at home */
7620 g_object_ref (column->button);
7621 gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7622 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7623 gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7624 g_object_unref (column->button);
7626 tree_view->priv->drag_column_x = column->allocation.x;
7627 allocation = column->allocation;
7629 gtk_widget_size_allocate (column->button, &allocation);
7630 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7632 tree_view->priv->drag_column = column;
7633 gdk_window_show (tree_view->priv->drag_window);
7635 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7637 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7638 while (gtk_events_pending ())
7639 gtk_main_iteration ();
7641 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7642 gdk_pointer_grab (tree_view->priv->drag_window,
7644 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7645 NULL, NULL, GDK_CURRENT_TIME);
7646 gdk_keyboard_grab (tree_view->priv->drag_window,
7652 _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view,
7654 const GdkRectangle *clip_rect)
7657 GtkAllocation allocation;
7659 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7662 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
7664 rect.width = MAX (tree_view->priv->width, allocation.width);
7666 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7667 rect.height = ROW_HEIGHT (tree_view);
7671 GdkRectangle new_rect;
7673 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7675 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7679 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7684 pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
7686 const GdkRectangle *clip_rect)
7690 _pspp_sheet_view_find_node (tree_view, path, &node);
7693 _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7697 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7700 GtkTreePath *cursor_path;
7702 if ((tree_view->priv->row_count == 0) ||
7703 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7707 if (tree_view->priv->cursor)
7708 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7710 if (cursor_path == NULL)
7712 /* There's no cursor. Move the cursor to the first selected row, if any
7713 * are selected, otherwise to the first row in the sheetview.
7715 GList *selected_rows;
7716 GtkTreeModel *model;
7717 PsppSheetSelection *selection;
7719 selection = pspp_sheet_view_get_selection (tree_view);
7720 selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7724 /* XXX we could avoid doing O(n) work to get this result */
7725 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7726 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7727 g_list_free (selected_rows);
7731 cursor_path = gtk_tree_path_new_first ();
7732 search_first_focusable_path (tree_view, &cursor_path,
7736 gtk_tree_row_reference_free (tree_view->priv->cursor);
7737 tree_view->priv->cursor = NULL;
7741 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7742 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7743 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0);
7745 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0);
7751 /* Now find a column for the cursor. */
7752 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7754 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7755 gtk_tree_path_free (cursor_path);
7757 if (tree_view->priv->focus_column == NULL)
7760 for (list = tree_view->priv->columns; list; list = list->next)
7762 if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7764 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7765 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7766 pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7776 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7778 PsppSheetSelectMode mode)
7780 gint selection_count;
7781 int cursor_node = -1;
7782 int new_cursor_node = -1;
7783 GtkTreePath *cursor_path = NULL;
7784 gboolean grab_focus = TRUE;
7786 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7790 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7791 /* FIXME: we lost the cursor; should we get the first? */
7794 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7795 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7797 if (cursor_node < 0)
7798 /* FIXME: we lost the cursor; should we get the first? */
7801 selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7803 if (selection_count == 0
7804 && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7805 && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
7807 /* Don't move the cursor, but just select the current node */
7808 new_cursor_node = cursor_node;
7813 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7815 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7818 gtk_tree_path_free (cursor_path);
7820 if (new_cursor_node)
7822 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7824 search_first_focusable_path (tree_view, &cursor_path,
7829 gtk_tree_path_free (cursor_path);
7833 * If the list has only one item and multi-selection is set then select
7834 * the row (if not yet selected).
7836 if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7837 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7838 new_cursor_node < 0)
7841 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7843 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7845 if (new_cursor_node < 0
7846 && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
7848 new_cursor_node = cursor_node;
7852 new_cursor_node = -1;
7856 if (new_cursor_node >= 0)
7858 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7859 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode);
7860 gtk_tree_path_free (cursor_path);
7864 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7866 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
7868 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
7870 GTK_DIR_UP : GTK_DIR_DOWN))
7872 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
7875 gtk_widget_child_focus (toplevel,
7877 GTK_DIR_TAB_BACKWARD :
7878 GTK_DIR_TAB_FORWARD);
7885 gtk_widget_error_bell (GTK_WIDGET (tree_view));
7890 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7892 return new_cursor_node >= 0;
7896 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
7898 PsppSheetSelectMode mode)
7900 int cursor_node = -1;
7901 GtkTreePath *old_cursor_path = NULL;
7902 GtkTreePath *cursor_path = NULL;
7903 int start_cursor_node = -1;
7906 gint vertical_separator;
7908 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7911 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
7912 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7914 /* This is sorta weird. Focus in should give us a cursor */
7917 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
7918 _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
7920 if (cursor_node < 0)
7922 /* FIXME: we lost the cursor. Should we try to get one? */
7923 gtk_tree_path_free (old_cursor_path);
7927 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7928 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
7929 y += tree_view->priv->cursor_offset;
7930 y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment);
7931 y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment), (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator);
7933 if (y >= tree_view->priv->height)
7934 y = tree_view->priv->height - 1;
7936 tree_view->priv->cursor_offset =
7937 pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
7939 if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
7941 cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7942 tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
7945 y -= tree_view->priv->cursor_offset;
7946 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7948 start_cursor_node = cursor_node;
7950 if (! search_first_focusable_path (tree_view, &cursor_path,
7954 /* It looks like we reached the end of the view without finding
7955 * a focusable row. We will step backwards to find the last
7958 cursor_node = start_cursor_node;
7959 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7961 search_first_focusable_path (tree_view, &cursor_path,
7970 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7972 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode);
7975 pspp_sheet_view_scroll_to_point (tree_view, -1, y);
7976 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7977 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
7979 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
7980 gtk_widget_error_bell (GTK_WIDGET (tree_view));
7982 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7985 gtk_tree_path_free (old_cursor_path);
7986 gtk_tree_path_free (cursor_path);
7990 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
7992 PsppSheetSelectMode mode)
7994 int cursor_node = -1;
7995 GtkTreePath *cursor_path = NULL;
7996 PsppSheetViewColumn *column;
7999 gboolean found_column = FALSE;
8002 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8004 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8007 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8008 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8012 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8013 if (cursor_node < 0)
8015 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8017 gtk_tree_path_free (cursor_path);
8020 gtk_tree_path_free (cursor_path);
8022 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8023 if (tree_view->priv->focus_column)
8025 for (; list; list = (rtl ? list->prev : list->next))
8027 if (list->data == tree_view->priv->focus_column)
8034 gboolean left, right;
8036 column = list->data;
8037 if (column->visible == FALSE || column->row_head)
8040 pspp_sheet_view_column_cell_set_cell_data (column,
8041 tree_view->priv->model,
8046 right = list->prev ? TRUE : FALSE;
8047 left = list->next ? TRUE : FALSE;
8051 left = list->prev ? TRUE : FALSE;
8052 right = list->next ? TRUE : FALSE;
8055 if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8057 tree_view->priv->focus_column = column;
8058 found_column = TRUE;
8063 list = rtl ? list->prev : list->next;
8065 list = rtl ? list->next : list->prev;
8070 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8071 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8072 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8076 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8079 pspp_sheet_view_clamp_column_visible (tree_view,
8080 tree_view->priv->focus_column, TRUE);
8084 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8086 PsppSheetSelectMode mode)
8088 int cursor_node = -1;
8089 GtkTreePath *cursor_path = NULL;
8090 PsppSheetViewColumn *column;
8091 PsppSheetViewColumn *found_column;
8096 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8098 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8101 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8102 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8106 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8107 if (cursor_node < 0)
8109 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8111 gtk_tree_path_free (cursor_path);
8114 gtk_tree_path_free (cursor_path);
8116 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8117 if (tree_view->priv->focus_column)
8119 for (; list; list = (rtl ? list->prev : list->next))
8121 if (list->data == tree_view->priv->focus_column)
8126 found_column = NULL;
8129 gboolean left, right;
8131 column = list->data;
8132 if (column->visible == FALSE || column->row_head)
8135 pspp_sheet_view_column_cell_set_cell_data (column,
8136 tree_view->priv->model,
8141 right = list->prev ? TRUE : FALSE;
8142 left = list->next ? TRUE : FALSE;
8146 left = list->prev ? TRUE : FALSE;
8147 right = list->next ? TRUE : FALSE;
8150 if (column->tabbable
8151 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8152 found_column = column;
8156 list = rtl ? list->prev : list->next;
8158 list = rtl ? list->next : list->prev;
8163 tree_view->priv->focus_column = found_column;
8164 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8165 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8166 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8169 pspp_sheet_view_clamp_column_visible (tree_view,
8170 tree_view->priv->focus_column, TRUE);
8174 try_move_cursor_tab (PsppSheetView *tree_view,
8175 gboolean start_at_focus_column,
8178 PsppSheetViewColumn *column;
8180 int cursor_node = -1;
8181 GtkTreePath *cursor_path = NULL;
8185 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8186 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8190 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8191 if (cursor_node < 0)
8193 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8195 gtk_tree_path_free (cursor_path);
8198 gtk_tree_path_free (cursor_path);
8200 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8201 if (start_at_focus_column)
8204 ? g_list_last (tree_view->priv->columns)
8205 : g_list_first (tree_view->priv->columns));
8206 if (tree_view->priv->focus_column)
8208 for (; list; list = (rtl ? list->prev : list->next))
8210 if (list->data == tree_view->priv->focus_column)
8217 list = (rtl ^ (count == 1)
8218 ? g_list_first (tree_view->priv->columns)
8219 : g_list_last (tree_view->priv->columns));
8224 gboolean left, right;
8226 column = list->data;
8227 if (column->visible == FALSE || !column->tabbable)
8230 pspp_sheet_view_column_cell_set_cell_data (column,
8231 tree_view->priv->model,
8236 right = list->prev ? TRUE : FALSE;
8237 left = list->next ? TRUE : FALSE;
8241 left = list->prev ? TRUE : FALSE;
8242 right = list->next ? TRUE : FALSE;
8245 if (column->tabbable
8246 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8248 tree_view->priv->focus_column = column;
8249 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8250 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8251 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8256 list = rtl ? list->prev : list->next;
8258 list = rtl ? list->next : list->prev;
8265 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8268 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8271 if (!try_move_cursor_tab (tree_view, TRUE, count))
8273 if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0)
8274 && !try_move_cursor_tab (tree_view, FALSE, count))
8275 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8278 pspp_sheet_view_clamp_column_visible (tree_view,
8279 tree_view->priv->focus_column, TRUE);
8283 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8285 PsppSheetSelectMode mode)
8289 GtkTreePath *old_path;
8291 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8294 g_return_if_fail (tree_view->priv->row_count > 0);
8296 pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8300 /* Now go forward to find the first focusable row. */
8301 path = _pspp_sheet_view_find_path (tree_view, 0);
8302 search_first_focusable_path (tree_view, &path,
8303 TRUE, &cursor_node);
8307 /* Now go backwards to find last focusable row. */
8308 path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8309 search_first_focusable_path (tree_view, &path,
8310 FALSE, &cursor_node);
8316 if (gtk_tree_path_compare (old_path, path))
8318 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
8319 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8323 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8327 gtk_tree_path_free (old_path);
8328 gtk_tree_path_free (path);
8332 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8334 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8337 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8338 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8341 pspp_sheet_selection_select_all (tree_view->priv->selection);
8347 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8349 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8352 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8353 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8356 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8362 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8363 gboolean start_editing,
8364 PsppSheetSelectMode mode)
8367 int cursor_node = -1;
8368 GtkTreePath *cursor_path = NULL;
8370 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8373 if (tree_view->priv->cursor)
8374 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8376 if (cursor_path == NULL)
8379 _pspp_sheet_view_find_node (tree_view, cursor_path,
8382 if (cursor_node < 0)
8384 gtk_tree_path_free (cursor_path);
8388 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing &&
8389 tree_view->priv->focus_column)
8391 if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8393 gtk_tree_path_free (cursor_path);
8398 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8404 /* We bail out if the original (tree, node) don't exist anymore after
8405 * handling the selection-changed callback. We do return TRUE because
8406 * the key press has been handled at this point.
8408 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8410 if (cursor_node != new_node)
8413 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8415 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8416 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8418 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8419 pspp_sheet_view_row_activated (tree_view, cursor_path,
8420 tree_view->priv->focus_column);
8422 gtk_tree_path_free (cursor_path);
8428 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8431 int cursor_node = -1;
8432 GtkTreePath *cursor_path = NULL;
8434 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8438 if (tree_view->priv->cursor)
8439 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8441 if (cursor_path == NULL)
8444 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8445 if (cursor_node < 0)
8447 gtk_tree_path_free (cursor_path);
8451 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8454 PSPP_SHEET_SELECT_MODE_TOGGLE,
8457 /* We bail out if the original (tree, node) don't exist anymore after
8458 * handling the selection-changed callback. We do return TRUE because
8459 * the key press has been handled at this point.
8461 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8463 if (cursor_node != new_node)
8466 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8468 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8469 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8470 gtk_tree_path_free (cursor_path);
8476 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8478 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8479 tree_view->priv->typeselect_flush_timeout = 0;
8484 /* Cut and paste from gtkwindow.c */
8486 send_focus_change (GtkWidget *widget,
8489 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8491 fevent->focus_change.type = GDK_FOCUS_CHANGE;
8492 fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
8493 fevent->focus_change.in = in;
8495 gtk_widget_send_focus_change (widget, fevent);
8496 gdk_event_free (fevent);
8500 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8502 GtkWidget *frame, *vbox, *toplevel;
8505 if (tree_view->priv->search_custom_entry_set)
8508 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8509 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8511 if (tree_view->priv->search_window != NULL)
8513 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8514 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8515 GTK_WINDOW (tree_view->priv->search_window));
8516 else if (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)))
8517 gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
8518 GTK_WINDOW (tree_view->priv->search_window));
8519 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8523 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8524 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8526 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8527 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8528 GTK_WINDOW (tree_view->priv->search_window));
8530 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8531 GDK_WINDOW_TYPE_HINT_UTILITY);
8532 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8533 g_signal_connect (tree_view->priv->search_window, "delete-event",
8534 G_CALLBACK (pspp_sheet_view_search_delete_event),
8536 g_signal_connect (tree_view->priv->search_window, "key-press-event",
8537 G_CALLBACK (pspp_sheet_view_search_key_press_event),
8539 g_signal_connect (tree_view->priv->search_window, "button-press-event",
8540 G_CALLBACK (pspp_sheet_view_search_button_press_event),
8542 g_signal_connect (tree_view->priv->search_window, "scroll-event",
8543 G_CALLBACK (pspp_sheet_view_search_scroll_event),
8546 frame = gtk_frame_new (NULL);
8547 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8548 gtk_widget_show (frame);
8549 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8551 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
8552 gtk_widget_show (vbox);
8553 gtk_container_add (GTK_CONTAINER (frame), vbox);
8554 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8557 tree_view->priv->search_entry = gtk_entry_new ();
8558 gtk_widget_show (tree_view->priv->search_entry);
8559 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8560 G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8562 g_signal_connect (tree_view->priv->search_entry,
8563 "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8567 g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8569 G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8573 gtk_container_add (GTK_CONTAINER (vbox),
8574 tree_view->priv->search_entry);
8576 gtk_widget_realize (tree_view->priv->search_entry);
8579 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8580 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8583 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8584 gboolean keybinding)
8586 /* We only start interactive search if we have focus or the columns
8587 * have focus. If one of our children have focus, we don't want to
8591 gboolean found_focus = FALSE;
8592 GtkWidgetClass *entry_parent_class;
8594 if (!tree_view->priv->enable_search && !keybinding)
8597 if (tree_view->priv->search_custom_entry_set)
8600 if (tree_view->priv->search_window != NULL &&
8601 gtk_widget_get_visible (tree_view->priv->search_window))
8604 for (list = tree_view->priv->columns; list; list = list->next)
8606 PsppSheetViewColumn *column;
8608 column = list->data;
8609 if (! column->visible)
8612 if (column->button && gtk_widget_has_focus (column->button))
8619 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8625 if (tree_view->priv->search_column < 0)
8628 pspp_sheet_view_ensure_interactive_directory (tree_view);
8631 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8634 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8635 gtk_widget_show (tree_view->priv->search_window);
8636 if (tree_view->priv->search_entry_changed_id == 0)
8638 tree_view->priv->search_entry_changed_id =
8639 g_signal_connect (tree_view->priv->search_entry, "changed",
8640 G_CALLBACK (pspp_sheet_view_search_init),
8644 tree_view->priv->typeselect_flush_timeout =
8645 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8646 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8649 /* Grab focus will select all the text. We don't want that to happen, so we
8650 * call the parent instance and bypass the selection change. This is probably
8651 * really non-kosher. */
8652 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8653 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8655 /* send focus-in event */
8656 send_focus_change (tree_view->priv->search_entry, TRUE);
8658 /* search first matching iter */
8659 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8665 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8667 return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8670 /* this function returns the new width of the column being resized given
8671 * the column and x position of the cursor; the x cursor position is passed
8672 * in as a pointer and automagicly corrected if it's beyond min/max limits
8675 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8679 PsppSheetViewColumn *column;
8683 /* first translate the x position from gtk_widget_get_window (widget)
8684 * to clist->clist_window
8686 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8687 column = g_list_nth (tree_view->priv->columns, i)->data;
8688 width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8690 /* Clamp down the value */
8691 if (column->min_width == -1)
8692 width = MAX (column->button_request, width);
8694 width = MAX (column->min_width, width);
8695 if (column->max_width != -1)
8696 width = MIN (width, column->max_width);
8698 *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8704 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8708 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8709 PsppSheetView *tree_view)
8711 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8715 gdk_window_move (tree_view->priv->bin_window,
8716 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8717 TREE_VIEW_HEADER_HEIGHT (tree_view));
8718 gdk_window_move (tree_view->priv->header_window,
8719 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8721 dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8723 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8727 /* update our dy and top_row */
8728 tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8730 update_prelight (tree_view,
8731 tree_view->priv->event_last_x,
8732 tree_view->priv->event_last_y);
8734 if (!tree_view->priv->in_top_row_to_dy)
8735 pspp_sheet_view_dy_to_top_row (tree_view);
8738 update_childrens_allocation(tree_view);
8746 * pspp_sheet_view_new:
8748 * Creates a new #PsppSheetView widget.
8750 * Return value: A newly created #PsppSheetView widget.
8753 pspp_sheet_view_new (void)
8755 return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
8759 * pspp_sheet_view_new_with_model:
8760 * @model: the model.
8762 * Creates a new #PsppSheetView widget with the model initialized to @model.
8764 * Return value: A newly created #PsppSheetView widget.
8767 pspp_sheet_view_new_with_model (GtkTreeModel *model)
8769 return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
8776 * pspp_sheet_view_get_model:
8777 * @tree_view: a #PsppSheetView
8779 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
8782 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
8785 pspp_sheet_view_get_model (PsppSheetView *tree_view)
8787 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8789 return tree_view->priv->model;
8793 * pspp_sheet_view_set_model:
8794 * @tree_view: A #GtkTreeNode.
8795 * @model: (allow-none): The model.
8797 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
8798 * set, it will remove it before setting the new model. If @model is %NULL,
8799 * then it will unset the old model.
8802 pspp_sheet_view_set_model (PsppSheetView *tree_view,
8803 GtkTreeModel *model)
8805 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8806 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
8808 if (model == tree_view->priv->model)
8811 if (tree_view->priv->scroll_to_path)
8813 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8814 tree_view->priv->scroll_to_path = NULL;
8817 if (tree_view->priv->model)
8819 GList *tmplist = tree_view->priv->columns;
8821 if (tree_view->priv->selected)
8822 range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
8823 pspp_sheet_view_stop_editing (tree_view, TRUE);
8825 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8826 pspp_sheet_view_row_changed,
8828 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8829 pspp_sheet_view_row_inserted,
8831 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8832 pspp_sheet_view_row_deleted,
8834 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8835 pspp_sheet_view_rows_reordered,
8838 for (; tmplist; tmplist = tmplist->next)
8839 _pspp_sheet_view_column_unset_model (tmplist->data,
8840 tree_view->priv->model);
8842 tree_view->priv->prelight_node = -1;
8844 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
8845 tree_view->priv->drag_dest_row = NULL;
8846 gtk_tree_row_reference_free (tree_view->priv->cursor);
8847 tree_view->priv->cursor = NULL;
8848 gtk_tree_row_reference_free (tree_view->priv->anchor);
8849 tree_view->priv->anchor = NULL;
8850 gtk_tree_row_reference_free (tree_view->priv->top_row);
8851 tree_view->priv->top_row = NULL;
8852 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8853 tree_view->priv->scroll_to_path = NULL;
8855 tree_view->priv->scroll_to_column = NULL;
8857 g_object_unref (tree_view->priv->model);
8859 tree_view->priv->search_column = -1;
8860 tree_view->priv->fixed_height = -1;
8861 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
8862 tree_view->priv->last_button_x = -1;
8863 tree_view->priv->last_button_y = -1;
8866 tree_view->priv->model = model;
8868 if (tree_view->priv->model)
8872 if (tree_view->priv->search_column == -1)
8874 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
8876 GType type = gtk_tree_model_get_column_type (model, i);
8878 if (g_value_type_transformable (type, G_TYPE_STRING))
8880 tree_view->priv->search_column = i;
8886 g_object_ref (tree_view->priv->model);
8887 g_signal_connect (tree_view->priv->model,
8889 G_CALLBACK (pspp_sheet_view_row_changed),
8891 g_signal_connect (tree_view->priv->model,
8893 G_CALLBACK (pspp_sheet_view_row_inserted),
8895 g_signal_connect (tree_view->priv->model,
8897 G_CALLBACK (pspp_sheet_view_row_deleted),
8899 g_signal_connect (tree_view->priv->model,
8901 G_CALLBACK (pspp_sheet_view_rows_reordered),
8904 tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
8906 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
8907 install_presize_handler (tree_view);
8910 g_object_notify (G_OBJECT (tree_view), "model");
8912 if (tree_view->priv->selection)
8913 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
8915 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8916 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8920 * pspp_sheet_view_get_selection:
8921 * @tree_view: A #PsppSheetView.
8923 * Gets the #PsppSheetSelection associated with @tree_view.
8925 * Return value: A #PsppSheetSelection object.
8927 PsppSheetSelection *
8928 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
8930 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8932 return tree_view->priv->selection;
8936 * pspp_sheet_view_get_hadjustment:
8937 * @tree_view: A #PsppSheetView
8939 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
8941 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
8945 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
8947 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8949 return pspp_sheet_view_do_get_hadjustment (tree_view);
8952 static GtkAdjustment *
8953 pspp_sheet_view_do_get_hadjustment (PsppSheetView *tree_view)
8955 return tree_view->priv->hadjustment;
8959 * pspp_sheet_view_set_hadjustment:
8960 * @tree_view: A #PsppSheetView
8961 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
8963 * Sets the #GtkAdjustment for the current horizontal aspect.
8966 pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view,
8967 GtkAdjustment *adjustment)
8969 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8971 pspp_sheet_view_set_adjustments (tree_view,
8973 tree_view->priv->vadjustment);
8975 g_object_notify (G_OBJECT (tree_view), "hadjustment");
8979 pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
8980 GtkAdjustment *adjustment)
8982 PsppSheetViewPrivate *priv = tree_view->priv;
8984 if (adjustment && priv->hadjustment == adjustment)
8987 if (priv->hadjustment != NULL)
8989 g_signal_handlers_disconnect_by_func (priv->hadjustment,
8990 pspp_sheet_view_adjustment_changed,
8992 g_object_unref (priv->hadjustment);
8995 if (adjustment == NULL)
8996 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
8999 g_signal_connect (adjustment, "value-changed",
9000 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9001 priv->hadjustment = g_object_ref_sink (adjustment);
9002 /* FIXME: Adjustment should probably be populated here with fresh values, but
9003 * internal details are too complicated for me to decipher right now.
9005 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9007 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9011 * pspp_sheet_view_get_vadjustment:
9012 * @tree_view: A #PsppSheetView
9014 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9016 * Return value: (transfer none): A #GtkAdjustment object, or %NULL
9017 * if none is currently being used.
9019 * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
9022 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9024 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9026 return pspp_sheet_view_do_get_vadjustment (tree_view);
9029 static GtkAdjustment *
9030 pspp_sheet_view_do_get_vadjustment (PsppSheetView *tree_view)
9032 return tree_view->priv->vadjustment;
9036 * pspp_sheet_view_set_vadjustment:
9037 * @tree_view: A #PsppSheetView
9038 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9040 * Sets the #GtkAdjustment for the current vertical aspect.
9042 * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
9045 pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view,
9046 GtkAdjustment *adjustment)
9048 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9049 g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
9051 pspp_sheet_view_do_set_vadjustment (tree_view, adjustment);
9055 pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
9056 GtkAdjustment *adjustment)
9058 PsppSheetViewPrivate *priv = tree_view->priv;
9060 if (adjustment && priv->vadjustment == adjustment)
9063 if (priv->vadjustment != NULL)
9065 g_signal_handlers_disconnect_by_func (priv->vadjustment,
9066 pspp_sheet_view_adjustment_changed,
9068 g_object_unref (priv->vadjustment);
9071 if (adjustment == NULL)
9072 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9075 g_signal_connect (adjustment, "value-changed",
9076 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9077 priv->vadjustment = g_object_ref_sink (adjustment);
9078 /* FIXME: Adjustment should probably be populated here with fresh values, but
9079 * internal details are too complicated for me to decipher right now.
9081 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9082 g_object_notify (G_OBJECT (tree_view), "vadjustment");
9085 /* Column and header operations */
9088 * pspp_sheet_view_get_headers_visible:
9089 * @tree_view: A #PsppSheetView.
9091 * Returns %TRUE if the headers on the @tree_view are visible.
9093 * Return value: Whether the headers are visible or not.
9096 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9098 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9100 return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9104 * pspp_sheet_view_set_headers_visible:
9105 * @tree_view: A #PsppSheetView.
9106 * @headers_visible: %TRUE if the headers are visible
9108 * Sets the visibility state of the headers.
9111 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9112 gboolean headers_visible)
9116 PsppSheetViewColumn *column;
9117 GtkAllocation allocation;
9119 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9121 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9123 headers_visible = !! headers_visible;
9125 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9128 if (headers_visible)
9129 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9131 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9133 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9135 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9136 if (headers_visible)
9138 gdk_window_move_resize (tree_view->priv->bin_window, x, y + TREE_VIEW_HEADER_HEIGHT (tree_view),
9139 tree_view->priv->width, allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view));
9141 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9142 pspp_sheet_view_map_buttons (tree_view);
9146 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9148 for (list = tree_view->priv->columns; list; list = list->next)
9150 column = list->data;
9152 gtk_widget_unmap (column->button);
9154 gdk_window_hide (tree_view->priv->header_window);
9158 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view));
9159 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2);
9160 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
9161 gtk_adjustment_set_upper (tree_view->priv->vadjustment, tree_view->priv->height);
9162 gtk_adjustment_changed (tree_view->priv->vadjustment);
9164 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9166 g_object_notify (G_OBJECT (tree_view), "headers-visible");
9170 * pspp_sheet_view_columns_autosize:
9171 * @tree_view: A #PsppSheetView.
9173 * Resizes all columns to their optimal width. Only works after the
9174 * treeview has been realized.
9177 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9179 gboolean dirty = FALSE;
9181 PsppSheetViewColumn *column;
9183 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9185 for (list = tree_view->priv->columns; list; list = list->next)
9187 column = list->data;
9188 _pspp_sheet_view_column_cell_set_dirty (column);
9193 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9197 * pspp_sheet_view_set_headers_clickable:
9198 * @tree_view: A #PsppSheetView.
9199 * @setting: %TRUE if the columns are clickable.
9201 * Allow the column title buttons to be clicked.
9204 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9209 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9211 for (list = tree_view->priv->columns; list; list = list->next)
9212 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9214 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9219 * pspp_sheet_view_get_headers_clickable:
9220 * @tree_view: A #PsppSheetView.
9222 * Returns whether all header columns are clickable.
9224 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9229 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9233 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9235 for (list = tree_view->priv->columns; list; list = list->next)
9236 if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9243 * pspp_sheet_view_set_rules_hint
9244 * @tree_view: a #PsppSheetView
9245 * @setting: %TRUE if the tree requires reading across rows
9247 * This function tells GTK+ that the user interface for your
9248 * application requires users to read across tree rows and associate
9249 * cells with one another. By default, GTK+ will then render the tree
9250 * with alternating row colors. Do <emphasis>not</emphasis> use it
9251 * just because you prefer the appearance of the ruled tree; that's a
9252 * question for the theme. Some themes will draw tree rows in
9253 * alternating colors even when rules are turned off, and users who
9254 * prefer that appearance all the time can choose those themes. You
9255 * should call this function only as a <emphasis>semantic</emphasis>
9256 * hint to the theme engine that your tree makes alternating colors
9257 * useful from a functional standpoint (since it has lots of columns,
9262 pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view,
9265 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9267 setting = setting != FALSE;
9269 if (tree_view->priv->has_rules != setting)
9271 tree_view->priv->has_rules = setting;
9272 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9275 g_object_notify (G_OBJECT (tree_view), "rules-hint");
9279 * pspp_sheet_view_get_rules_hint
9280 * @tree_view: a #PsppSheetView
9282 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9284 * Return value: %TRUE if rules are useful for the user of this tree
9287 pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view)
9289 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9291 return tree_view->priv->has_rules;
9294 /* Public Column functions
9298 * pspp_sheet_view_append_column:
9299 * @tree_view: A #PsppSheetView.
9300 * @column: The #PsppSheetViewColumn to add.
9302 * Appends @column to the list of columns.
9304 * Return value: The number of columns in @tree_view after appending.
9307 pspp_sheet_view_append_column (PsppSheetView *tree_view,
9308 PsppSheetViewColumn *column)
9310 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9311 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9312 g_return_val_if_fail (column->tree_view == NULL, -1);
9314 return pspp_sheet_view_insert_column (tree_view, column, -1);
9319 * pspp_sheet_view_remove_column:
9320 * @tree_view: A #PsppSheetView.
9321 * @column: The #PsppSheetViewColumn to remove.
9323 * Removes @column from @tree_view.
9325 * Return value: The number of columns in @tree_view after removing.
9328 pspp_sheet_view_remove_column (PsppSheetView *tree_view,
9329 PsppSheetViewColumn *column)
9331 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9332 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9333 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9335 if (tree_view->priv->focus_column == column)
9336 tree_view->priv->focus_column = NULL;
9338 if (tree_view->priv->edited_column == column)
9340 pspp_sheet_view_stop_editing (tree_view, TRUE);
9342 /* no need to, but just to be sure ... */
9343 tree_view->priv->edited_column = NULL;
9346 _pspp_sheet_view_column_unset_tree_view (column);
9348 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9349 tree_view->priv->n_columns--;
9351 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9355 _pspp_sheet_view_column_unrealize_button (column);
9356 for (list = tree_view->priv->columns; list; list = list->next)
9358 PsppSheetViewColumn *tmp_column;
9360 tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9361 if (tmp_column->visible)
9362 _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9365 if (tree_view->priv->n_columns == 0 &&
9366 pspp_sheet_view_get_headers_visible (tree_view) &&
9367 tree_view->priv->header_window)
9368 gdk_window_hide (tree_view->priv->header_window);
9370 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9373 g_object_unref (column);
9374 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9376 return tree_view->priv->n_columns;
9380 * pspp_sheet_view_insert_column:
9381 * @tree_view: A #PsppSheetView.
9382 * @column: The #PsppSheetViewColumn to be inserted.
9383 * @position: The position to insert @column in.
9385 * This inserts the @column into the @tree_view at @position. If @position is
9386 * -1, then the column is inserted at the end.
9388 * Return value: The number of columns in @tree_view after insertion.
9391 pspp_sheet_view_insert_column (PsppSheetView *tree_view,
9392 PsppSheetViewColumn *column,
9395 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9396 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9397 g_return_val_if_fail (column->tree_view == NULL, -1);
9399 g_object_ref_sink (column);
9401 if (tree_view->priv->n_columns == 0 &&
9402 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9403 pspp_sheet_view_get_headers_visible (tree_view))
9405 gdk_window_show (tree_view->priv->header_window);
9408 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9410 tree_view->priv->n_columns++;
9412 _pspp_sheet_view_column_set_tree_view (column, tree_view);
9414 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9418 _pspp_sheet_view_column_realize_button (column);
9420 for (list = tree_view->priv->columns; list; list = list->next)
9422 column = PSPP_SHEET_VIEW_COLUMN (list->data);
9423 if (column->visible)
9424 _pspp_sheet_view_column_cell_set_dirty (column);
9426 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9429 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9431 return tree_view->priv->n_columns;
9435 * pspp_sheet_view_insert_column_with_attributes:
9436 * @tree_view: A #PsppSheetView
9437 * @position: The position to insert the new column in.
9438 * @title: The title to set the header to.
9439 * @cell: The #GtkCellRenderer.
9440 * @Varargs: A %NULL-terminated list of attributes.
9442 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9443 * @position. If @position is -1, then the newly created column is inserted at
9444 * the end. The column is initialized with the attributes given.
9446 * Return value: The number of columns in @tree_view after insertion.
9449 pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view,
9452 GtkCellRenderer *cell,
9455 PsppSheetViewColumn *column;
9460 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9462 column = pspp_sheet_view_column_new ();
9463 pspp_sheet_view_column_set_title (column, title);
9464 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9466 va_start (args, cell);
9468 attribute = va_arg (args, gchar *);
9470 while (attribute != NULL)
9472 column_id = va_arg (args, gint);
9473 pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9474 attribute = va_arg (args, gchar *);
9479 pspp_sheet_view_insert_column (tree_view, column, position);
9481 return tree_view->priv->n_columns;
9485 * pspp_sheet_view_insert_column_with_data_func:
9486 * @tree_view: a #PsppSheetView
9487 * @position: Position to insert, -1 for append
9488 * @title: column title
9489 * @cell: cell renderer for column
9490 * @func: function to set attributes of cell renderer
9491 * @data: data for @func
9492 * @dnotify: destroy notifier for @data
9494 * Convenience function that inserts a new column into the #PsppSheetView
9495 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9496 * attributes (normally using data from the model). See also
9497 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9499 * Return value: number of columns in the tree view post-insert
9502 pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view,
9505 GtkCellRenderer *cell,
9506 PsppSheetCellDataFunc func,
9508 GDestroyNotify dnotify)
9510 PsppSheetViewColumn *column;
9512 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9514 column = pspp_sheet_view_column_new ();
9515 pspp_sheet_view_column_set_title (column, title);
9516 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9517 pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9519 pspp_sheet_view_insert_column (tree_view, column, position);
9521 return tree_view->priv->n_columns;
9525 * pspp_sheet_view_get_column:
9526 * @tree_view: A #PsppSheetView.
9527 * @n: The position of the column, counting from 0.
9529 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9531 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9534 PsppSheetViewColumn *
9535 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9538 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9540 if (n < 0 || n >= tree_view->priv->n_columns)
9543 if (tree_view->priv->columns == NULL)
9546 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9550 * pspp_sheet_view_get_columns:
9551 * @tree_view: A #PsppSheetView
9553 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9554 * The returned list must be freed with g_list_free ().
9556 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9559 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9561 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9563 return g_list_copy (tree_view->priv->columns);
9567 * pspp_sheet_view_move_column_after:
9568 * @tree_view: A #PsppSheetView
9569 * @column: The #PsppSheetViewColumn to be moved.
9570 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9572 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9573 * @column is placed in the first position.
9576 pspp_sheet_view_move_column_after (PsppSheetView *tree_view,
9577 PsppSheetViewColumn *column,
9578 PsppSheetViewColumn *base_column)
9580 GList *column_list_el, *base_el = NULL;
9582 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9584 column_list_el = g_list_find (tree_view->priv->columns, column);
9585 g_return_if_fail (column_list_el != NULL);
9589 base_el = g_list_find (tree_view->priv->columns, base_column);
9590 g_return_if_fail (base_el != NULL);
9593 if (column_list_el->prev == base_el)
9596 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9597 if (base_el == NULL)
9599 column_list_el->prev = NULL;
9600 column_list_el->next = tree_view->priv->columns;
9601 if (column_list_el->next)
9602 column_list_el->next->prev = column_list_el;
9603 tree_view->priv->columns = column_list_el;
9607 column_list_el->prev = base_el;
9608 column_list_el->next = base_el->next;
9609 if (column_list_el->next)
9610 column_list_el->next->prev = column_list_el;
9611 base_el->next = column_list_el;
9614 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9616 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9617 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9620 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9624 * pspp_sheet_view_set_column_drag_function:
9625 * @tree_view: A #PsppSheetView.
9626 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9627 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9628 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9630 * Sets a user function for determining where a column may be dropped when
9631 * dragged. This function is called on every column pair in turn at the
9632 * beginning of a column drag to determine where a drop can take place. The
9633 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9634 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9635 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9636 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9637 * @tree_view reverts to the default behavior of allowing all columns to be
9638 * dropped everywhere.
9641 pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view,
9642 PsppSheetViewColumnDropFunc func,
9644 GDestroyNotify destroy)
9646 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9648 if (tree_view->priv->column_drop_func_data_destroy)
9649 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9651 tree_view->priv->column_drop_func = func;
9652 tree_view->priv->column_drop_func_data = user_data;
9653 tree_view->priv->column_drop_func_data_destroy = destroy;
9657 * pspp_sheet_view_scroll_to_point:
9658 * @tree_view: a #PsppSheetView
9659 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9660 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9662 * Scrolls the tree view such that the top-left corner of the visible
9663 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9664 * in tree coordinates. The @tree_view must be realized before
9665 * this function is called. If it isn't, you probably want to be
9666 * using pspp_sheet_view_scroll_to_cell().
9668 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9671 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9675 GtkAdjustment *hadj;
9676 GtkAdjustment *vadj;
9678 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9679 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9681 hadj = tree_view->priv->hadjustment;
9682 vadj = tree_view->priv->vadjustment;
9685 gtk_adjustment_set_value (hadj, CLAMP (tree_x, gtk_adjustment_get_lower (hadj), gtk_adjustment_get_upper (hadj) - gtk_adjustment_get_page_size (hadj)));
9687 gtk_adjustment_set_value (vadj, CLAMP (tree_y, gtk_adjustment_get_lower (vadj), gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj)));
9691 * pspp_sheet_view_scroll_to_cell:
9692 * @tree_view: A #PsppSheetView.
9693 * @path: (allow-none): The path of the row to move to, or %NULL.
9694 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9695 * @use_align: whether to use alignment arguments, or %FALSE.
9696 * @row_align: The vertical alignment of the row specified by @path.
9697 * @col_align: The horizontal alignment of the column specified by @column.
9699 * Moves the alignments of @tree_view to the position specified by @column and
9700 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9701 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9702 * or @path need to be non-%NULL. @row_align determines where the row is
9703 * placed, and @col_align determines where @column is placed. Both are expected
9704 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9705 * right/bottom alignment, 0.5 means center.
9707 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9708 * tree does the minimum amount of work to scroll the cell onto the screen.
9709 * This means that the cell will be scrolled to the edge closest to its current
9710 * position. If the cell is currently visible on the screen, nothing is done.
9712 * This function only works if the model is set, and @path is a valid row on the
9713 * model. If the model changes before the @tree_view is realized, the centered
9714 * path will be modified to reflect this change.
9717 pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view,
9719 PsppSheetViewColumn *column,
9724 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9725 g_return_if_fail (tree_view->priv->model != NULL);
9726 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9727 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9728 g_return_if_fail (path != NULL || column != NULL);
9731 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9732 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9734 row_align = CLAMP (row_align, 0.0, 1.0);
9735 col_align = CLAMP (col_align, 0.0, 1.0);
9738 /* Note: Despite the benefits that come from having one code path for the
9739 * scrolling code, we short-circuit validate_visible_area's immplementation as
9740 * it is much slower than just going to the point.
9742 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9743 !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9744 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9746 if (tree_view->priv->scroll_to_path)
9747 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9749 tree_view->priv->scroll_to_path = NULL;
9750 tree_view->priv->scroll_to_column = NULL;
9753 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
9755 tree_view->priv->scroll_to_column = column;
9756 tree_view->priv->scroll_to_use_align = use_align;
9757 tree_view->priv->scroll_to_row_align = row_align;
9758 tree_view->priv->scroll_to_col_align = col_align;
9760 install_presize_handler (tree_view);
9764 GdkRectangle cell_rect;
9765 GdkRectangle vis_rect;
9766 gint dest_x, dest_y;
9768 pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
9769 pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
9771 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
9773 dest_x = vis_rect.x;
9774 dest_y = vis_rect.y;
9780 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
9784 if (cell_rect.x < vis_rect.x)
9785 dest_x = cell_rect.x;
9786 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
9787 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
9795 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
9796 dest_y = MAX (dest_y, 0);
9800 if (cell_rect.y < vis_rect.y)
9801 dest_y = cell_rect.y;
9802 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
9803 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
9807 pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
9812 * pspp_sheet_view_row_activated:
9813 * @tree_view: A #PsppSheetView
9814 * @path: The #GtkTreePath to be activated.
9815 * @column: The #PsppSheetViewColumn to be activated.
9817 * Activates the cell determined by @path and @column.
9820 pspp_sheet_view_row_activated (PsppSheetView *tree_view,
9822 PsppSheetViewColumn *column)
9824 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9826 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
9831 * pspp_sheet_view_get_reorderable:
9832 * @tree_view: a #PsppSheetView
9834 * Retrieves whether the user can reorder the tree via drag-and-drop. See
9835 * pspp_sheet_view_set_reorderable().
9837 * Return value: %TRUE if the tree can be reordered.
9840 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
9842 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9844 return tree_view->priv->reorderable;
9848 * pspp_sheet_view_set_reorderable:
9849 * @tree_view: A #PsppSheetView.
9850 * @reorderable: %TRUE, if the tree can be reordered.
9852 * This function is a convenience function to allow you to reorder
9853 * models that support the #GtkDragSourceIface and the
9854 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
9855 * these. If @reorderable is %TRUE, then the user can reorder the
9856 * model by dragging and dropping rows. The developer can listen to
9857 * these changes by connecting to the model's row_inserted and
9858 * row_deleted signals. The reordering is implemented by setting up
9859 * the tree view as a drag source and destination. Therefore, drag and
9860 * drop can not be used in a reorderable view for any other purpose.
9862 * This function does not give you any degree of control over the order -- any
9863 * reordering is allowed. If more control is needed, you should probably
9864 * handle drag and drop manually.
9867 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
9868 gboolean reorderable)
9870 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9872 reorderable = reorderable != FALSE;
9874 if (tree_view->priv->reorderable == reorderable)
9879 const GtkTargetEntry row_targets[] = {
9880 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
9883 pspp_sheet_view_enable_model_drag_source (tree_view,
9886 G_N_ELEMENTS (row_targets),
9888 pspp_sheet_view_enable_model_drag_dest (tree_view,
9890 G_N_ELEMENTS (row_targets),
9895 pspp_sheet_view_unset_rows_drag_source (tree_view);
9896 pspp_sheet_view_unset_rows_drag_dest (tree_view);
9899 tree_view->priv->reorderable = reorderable;
9901 g_object_notify (G_OBJECT (tree_view), "reorderable");
9904 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
9905 is pressed, other rows will be unselected.
9907 If CLAMP_NODE is true, then the sheetview will scroll to make the row
9910 pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
9912 gboolean clear_and_select,
9913 gboolean clamp_node,
9914 PsppSheetSelectMode mode)
9918 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9920 GtkTreePath *cursor_path;
9921 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9922 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
9923 gtk_tree_path_free (cursor_path);
9926 gtk_tree_row_reference_free (tree_view->priv->cursor);
9927 tree_view->priv->cursor = NULL;
9929 _pspp_sheet_view_find_node (tree_view, path, &node);
9930 tree_view->priv->cursor =
9931 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
9932 tree_view->priv->model,
9935 if (tree_view->priv->row_count > 0)
9939 if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
9940 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
9944 /* We have to re-find tree and node here again, somebody might have
9945 * cleared the node or the whole tree in the PsppSheetSelection::changed
9946 * callback. If the nodes differ we bail out here.
9948 _pspp_sheet_view_find_node (tree_view, path, &new_node);
9950 if (node != new_node)
9955 pspp_sheet_view_clamp_node_visible (tree_view, node);
9956 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
9960 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
9964 * pspp_sheet_view_get_cursor:
9965 * @tree_view: A #PsppSheetView
9966 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
9967 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
9969 * Fills in @path and @focus_column with the current path and focus column. If
9970 * the cursor isn't currently set, then *@path will be %NULL. If no column
9971 * currently has focus, then *@focus_column will be %NULL.
9973 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
9974 * you are done with it.
9977 pspp_sheet_view_get_cursor (PsppSheetView *tree_view,
9979 PsppSheetViewColumn **focus_column)
9981 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9985 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9986 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9993 *focus_column = tree_view->priv->focus_column;
9998 * pspp_sheet_view_set_cursor:
9999 * @tree_view: A #PsppSheetView
10000 * @path: A #GtkTreePath
10001 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10002 * @start_editing: %TRUE if the specified cell should start being edited.
10004 * Sets the current keyboard focus to be at @path, and selects it. This is
10005 * useful when you want to focus the user's attention on a particular row. If
10006 * @focus_column is not %NULL, then focus is given to the column specified by
10007 * it. Additionally, if @focus_column is specified, and @start_editing is
10008 * %TRUE, then editing should be started in the specified cell.
10009 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10010 * in order to give keyboard focus to the widget. Please note that editing
10011 * can only happen when the widget is realized.
10013 * If @path is invalid for @model, the current cursor (if any) will be unset
10014 * and the function will return without failing.
10017 pspp_sheet_view_set_cursor (PsppSheetView *tree_view,
10019 PsppSheetViewColumn *focus_column,
10020 gboolean start_editing)
10022 pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10023 NULL, start_editing);
10027 * pspp_sheet_view_set_cursor_on_cell:
10028 * @tree_view: A #PsppSheetView
10029 * @path: A #GtkTreePath
10030 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10031 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10032 * @start_editing: %TRUE if the specified cell should start being edited.
10034 * Sets the current keyboard focus to be at @path, and selects it. This is
10035 * useful when you want to focus the user's attention on a particular row. If
10036 * @focus_column is not %NULL, then focus is given to the column specified by
10037 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10038 * contains 2 or more editable or activatable cells, then focus is given to
10039 * the cell specified by @focus_cell. Additionally, if @focus_column is
10040 * specified, and @start_editing is %TRUE, then editing should be started in
10041 * the specified cell. This function is often followed by
10042 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10043 * widget. Please note that editing can only happen when the widget is
10046 * If @path is invalid for @model, the current cursor (if any) will be unset
10047 * and the function will return without failing.
10052 pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view,
10054 PsppSheetViewColumn *focus_column,
10055 GtkCellRenderer *focus_cell,
10056 gboolean start_editing)
10058 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10059 g_return_if_fail (path != NULL);
10060 g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10062 if (!tree_view->priv->model)
10067 g_return_if_fail (focus_column);
10068 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10071 /* cancel the current editing, if it exists */
10072 if (tree_view->priv->edited_column &&
10073 tree_view->priv->edited_column->editable_widget)
10074 pspp_sheet_view_stop_editing (tree_view, TRUE);
10076 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
10078 if (focus_column && focus_column->visible)
10081 gboolean column_in_tree = FALSE;
10083 for (list = tree_view->priv->columns; list; list = list->next)
10084 if (list->data == focus_column)
10086 column_in_tree = TRUE;
10089 g_return_if_fail (column_in_tree);
10090 tree_view->priv->focus_column = focus_column;
10092 pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10094 pspp_sheet_view_start_editing (tree_view, path);
10096 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10097 pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10103 * pspp_sheet_view_get_bin_window:
10104 * @tree_view: A #PsppSheetView
10106 * Returns the window that @tree_view renders to. This is used primarily to
10107 * compare to <literal>event->window</literal> to confirm that the event on
10108 * @tree_view is on the right window.
10110 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10113 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10115 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10117 return tree_view->priv->bin_window;
10121 * pspp_sheet_view_get_path_at_pos:
10122 * @tree_view: A #PsppSheetView.
10123 * @x: The x position to be identified (relative to bin_window).
10124 * @y: The y position to be identified (relative to bin_window).
10125 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10126 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10127 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10128 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10130 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10131 * (please see pspp_sheet_view_get_bin_window()).
10132 * That is, @x and @y are relative to an events coordinates. @x and @y must
10133 * come from an event on the @tree_view only where <literal>event->window ==
10134 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10135 * things like popup menus. If @path is non-%NULL, then it will be filled
10136 * with the #GtkTreePath at that point. This path should be freed with
10137 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10138 * with the column at that point. @cell_x and @cell_y return the coordinates
10139 * relative to the cell background (i.e. the @background_area passed to
10140 * gtk_cell_renderer_render()). This function is only meaningful if
10141 * @tree_view is realized. Therefore this function will always return %FALSE
10142 * if @tree_view is not realized or does not have a model.
10144 * For converting widget coordinates (eg. the ones you get from
10145 * GtkWidget::query-tooltip), please see
10146 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10148 * Return value: %TRUE if a row exists at that coordinate.
10151 pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view,
10154 GtkTreePath **path,
10155 PsppSheetViewColumn **column,
10162 g_return_val_if_fail (tree_view != NULL, FALSE);
10169 if (tree_view->priv->bin_window == NULL)
10172 if (tree_view->priv->row_count == 0)
10175 if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment))
10178 if (x < 0 || y < 0)
10181 if (column || cell_x)
10183 PsppSheetViewColumn *tmp_column;
10184 PsppSheetViewColumn *last_column = NULL;
10186 gint remaining_x = x;
10187 gboolean found = FALSE;
10190 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10191 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10193 list = (rtl ? list->prev : list->next))
10195 tmp_column = list->data;
10197 if (tmp_column->visible == FALSE)
10200 last_column = tmp_column;
10201 if (remaining_x <= tmp_column->width)
10206 *column = tmp_column;
10209 *cell_x = remaining_x;
10213 remaining_x -= tmp_column->width;
10216 /* If found is FALSE and there is a last_column, then it the remainder
10217 * space is in that area
10224 *column = last_column;
10227 *cell_x = last_column->width + remaining_x;
10236 y_offset = pspp_sheet_view_find_offset (tree_view,
10237 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10244 *cell_y = y_offset;
10247 *path = _pspp_sheet_view_find_path (tree_view, node);
10252 /* Computes 'cell_area' from 'background_area', which must be the background
10253 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10254 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10255 the cell area as passed to _pspp_sheet_view_column_cell_render().
10257 'column' is required to properly adjust 'cell_area->x' and
10258 'cell_area->width'. It may be set to NULL if these values are not of
10259 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10262 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
10263 PsppSheetViewColumn *column,
10264 const GdkRectangle *background_area,
10265 gboolean subtract_focus_rect,
10266 GdkRectangle *cell_area)
10268 gint vertical_separator;
10269 gint horizontal_separator;
10271 *cell_area = *background_area;
10273 gtk_widget_style_get (GTK_WIDGET (tree_view),
10274 "vertical-separator", &vertical_separator,
10275 "horizontal-separator", &horizontal_separator,
10277 cell_area->x += horizontal_separator / 2;
10278 cell_area->y += vertical_separator / 2;
10279 cell_area->width -= horizontal_separator;
10280 cell_area->height -= vertical_separator;
10282 if (subtract_focus_rect)
10284 int focus_line_width;
10286 gtk_widget_style_get (GTK_WIDGET (tree_view),
10287 "focus-line-width", &focus_line_width,
10289 cell_area->x += focus_line_width;
10290 cell_area->y += focus_line_width;
10291 cell_area->width -= 2 * focus_line_width;
10292 cell_area->height -= 2 * focus_line_width;
10295 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10297 gint grid_line_width;
10298 gtk_widget_style_get (GTK_WIDGET (tree_view),
10299 "grid-line-width", &grid_line_width,
10302 if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10303 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10306 PsppSheetViewColumn *first_column, *last_column;
10309 /* Find the last visible column. */
10310 last_column = NULL;
10311 for (list = g_list_last (tree_view->priv->columns);
10315 PsppSheetViewColumn *c = list->data;
10323 /* Find the first visible column. */
10324 first_column = NULL;
10325 for (list = g_list_first (tree_view->priv->columns);
10329 PsppSheetViewColumn *c = list->data;
10337 if (column == first_column)
10339 cell_area->width -= grid_line_width / 2;
10341 else if (column == last_column)
10343 cell_area->x += grid_line_width / 2;
10344 cell_area->width -= grid_line_width / 2;
10348 cell_area->x += grid_line_width / 2;
10349 cell_area->width -= grid_line_width;
10353 if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10354 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10356 cell_area->y += grid_line_width / 2;
10357 cell_area->height -= grid_line_width;
10361 if (column == NULL)
10364 cell_area->width = 0;
10369 * pspp_sheet_view_get_cell_area:
10370 * @tree_view: a #PsppSheetView
10371 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10372 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10373 * @rect: rectangle to fill with cell rect
10375 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10376 * row specified by @path and the column specified by @column. If @path is
10377 * %NULL, or points to a path not currently displayed, the @y and @height fields
10378 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10379 * fields will be filled with 0. The sum of all cell rects does not cover the
10380 * entire tree; there are extra pixels in between rows, for example. The
10381 * returned rectangle is equivalent to the @cell_area passed to
10382 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10386 pspp_sheet_view_get_cell_area (PsppSheetView *tree_view,
10388 PsppSheetViewColumn *column,
10389 GdkRectangle *rect)
10391 GdkRectangle background_area;
10393 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10394 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10395 g_return_if_fail (rect != NULL);
10396 g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10397 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10399 pspp_sheet_view_get_background_area (tree_view, path, column,
10401 pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10406 * pspp_sheet_view_get_background_area:
10407 * @tree_view: a #PsppSheetView
10408 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10409 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10410 * @rect: rectangle to fill with cell background rect
10412 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10413 * row specified by @path and the column specified by @column. If @path is
10414 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10415 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10416 * fields will be filled with 0. The returned rectangle is equivalent to the
10417 * @background_area passed to gtk_cell_renderer_render(). These background
10418 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10419 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10420 * itself, excluding surrounding borders.
10424 pspp_sheet_view_get_background_area (PsppSheetView *tree_view,
10426 PsppSheetViewColumn *column,
10427 GdkRectangle *rect)
10431 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10432 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10433 g_return_if_fail (rect != NULL);
10442 /* Get vertical coords */
10444 _pspp_sheet_view_find_node (tree_view, path, &node);
10448 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10450 rect->height = ROW_HEIGHT (tree_view);
10457 pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10458 rect->width = x2 - rect->x;
10463 * pspp_sheet_view_get_visible_rect:
10464 * @tree_view: a #PsppSheetView
10465 * @visible_rect: rectangle to fill
10467 * Fills @visible_rect with the currently-visible region of the
10468 * buffer, in tree coordinates. Convert to bin_window coordinates with
10469 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10470 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10471 * scrollable area of the tree.
10474 pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view,
10475 GdkRectangle *visible_rect)
10479 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10481 widget = GTK_WIDGET (tree_view);
10485 GtkAllocation allocation;
10486 gtk_widget_get_allocation (widget, &allocation);
10487 visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
10488 visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
10489 visible_rect->width = allocation.width;
10490 visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10495 * pspp_sheet_view_widget_to_tree_coords:
10496 * @tree_view: a #PsppSheetView
10497 * @wx: X coordinate relative to bin_window
10498 * @wy: Y coordinate relative to bin_window
10499 * @tx: return location for tree X coordinate
10500 * @ty: return location for tree Y coordinate
10502 * Converts bin_window coordinates to coordinates for the
10503 * tree (the full scrollable area of the tree).
10505 * Deprecated: 2.12: Due to historial reasons the name of this function is
10506 * incorrect. For converting coordinates relative to the widget to
10507 * bin_window coordinates, please see
10508 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10512 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10518 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10521 *tx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10523 *ty = wy + tree_view->priv->dy;
10527 * pspp_sheet_view_tree_to_widget_coords:
10528 * @tree_view: a #PsppSheetView
10529 * @tx: tree X coordinate
10530 * @ty: tree Y coordinate
10531 * @wx: return location for X coordinate relative to bin_window
10532 * @wy: return location for Y coordinate relative to bin_window
10534 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10535 * to bin_window coordinates.
10537 * Deprecated: 2.12: Due to historial reasons the name of this function is
10538 * incorrect. For converting bin_window coordinates to coordinates relative
10539 * to bin_window, please see
10540 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10544 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10550 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10553 *wx = tx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10555 *wy = ty - tree_view->priv->dy;
10560 * pspp_sheet_view_convert_widget_to_tree_coords:
10561 * @tree_view: a #PsppSheetView
10562 * @wx: X coordinate relative to the widget
10563 * @wy: Y coordinate relative to the widget
10564 * @tx: return location for tree X coordinate
10565 * @ty: return location for tree Y coordinate
10567 * Converts widget coordinates to coordinates for the
10568 * tree (the full scrollable area of the tree).
10573 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10581 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10583 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10586 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10592 * pspp_sheet_view_convert_tree_to_widget_coords:
10593 * @tree_view: a #PsppSheetView
10594 * @tx: X coordinate relative to the tree
10595 * @ty: Y coordinate relative to the tree
10596 * @wx: return location for widget X coordinate
10597 * @wy: return location for widget Y coordinate
10599 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10600 * to widget coordinates.
10605 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10613 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10615 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10618 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10624 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10625 * @tree_view: a #PsppSheetView
10626 * @wx: X coordinate relative to the widget
10627 * @wy: Y coordinate relative to the widget
10628 * @bx: return location for bin_window X coordinate
10629 * @by: return location for bin_window Y coordinate
10631 * Converts widget coordinates to coordinates for the bin_window
10632 * (see pspp_sheet_view_get_bin_window()).
10637 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10643 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10646 *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10648 *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10652 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10653 * @tree_view: a #PsppSheetView
10654 * @bx: bin_window X coordinate
10655 * @by: bin_window Y coordinate
10656 * @wx: return location for widget X coordinate
10657 * @wy: return location for widget Y coordinate
10659 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10660 * to widget relative coordinates.
10665 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10671 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10674 *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10676 *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10680 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10681 * @tree_view: a #PsppSheetView
10682 * @tx: tree X coordinate
10683 * @ty: tree Y coordinate
10684 * @bx: return location for X coordinate relative to bin_window
10685 * @by: return location for Y coordinate relative to bin_window
10687 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10688 * to bin_window coordinates.
10693 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10699 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10704 *by = ty - tree_view->priv->dy;
10708 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10709 * @tree_view: a #PsppSheetView
10710 * @bx: X coordinate relative to bin_window
10711 * @by: Y coordinate relative to bin_window
10712 * @tx: return location for tree X coordinate
10713 * @ty: return location for tree Y coordinate
10715 * Converts bin_window coordinates to coordinates for the
10716 * tree (the full scrollable area of the tree).
10721 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10727 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10732 *ty = by + tree_view->priv->dy;
10738 * pspp_sheet_view_get_visible_range:
10739 * @tree_view: A #PsppSheetView
10740 * @start_path: (allow-none): Return location for start of region, or %NULL.
10741 * @end_path: (allow-none): Return location for end of region, or %NULL.
10743 * Sets @start_path and @end_path to be the first and last visible path.
10744 * Note that there may be invisible paths in between.
10746 * The paths should be freed with gtk_tree_path_free() after use.
10748 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
10753 pspp_sheet_view_get_visible_range (PsppSheetView *tree_view,
10754 GtkTreePath **start_path,
10755 GtkTreePath **end_path)
10760 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10762 if (!tree_view->priv->row_count)
10769 pspp_sheet_view_find_offset (tree_view,
10770 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
10773 *start_path = _pspp_sheet_view_find_path (tree_view, node);
10782 if (tree_view->priv->height < gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
10783 y = tree_view->priv->height - 1;
10785 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1;
10787 pspp_sheet_view_find_offset (tree_view, y, &node);
10789 *end_path = _pspp_sheet_view_find_path (tree_view, node);
10798 unset_reorderable (PsppSheetView *tree_view)
10800 if (tree_view->priv->reorderable)
10802 tree_view->priv->reorderable = FALSE;
10803 g_object_notify (G_OBJECT (tree_view), "reorderable");
10808 * pspp_sheet_view_enable_model_drag_source:
10809 * @tree_view: a #PsppSheetView
10810 * @start_button_mask: Mask of allowed buttons to start drag
10811 * @targets: the table of targets that the drag will support
10812 * @n_targets: the number of items in @targets
10813 * @actions: the bitmask of possible actions for a drag from this
10816 * Turns @tree_view into a drag source for automatic DND. Calling this
10817 * method sets #PsppSheetView:reorderable to %FALSE.
10820 pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view,
10821 GdkModifierType start_button_mask,
10822 const GtkTargetEntry *targets,
10824 GdkDragAction actions)
10826 TreeViewDragInfo *di;
10828 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10830 gtk_drag_source_set (GTK_WIDGET (tree_view),
10836 di = ensure_info (tree_view);
10838 di->start_button_mask = start_button_mask;
10839 di->source_actions = actions;
10840 di->source_set = TRUE;
10842 unset_reorderable (tree_view);
10846 * pspp_sheet_view_enable_model_drag_dest:
10847 * @tree_view: a #PsppSheetView
10848 * @targets: the table of targets that the drag will support
10849 * @n_targets: the number of items in @targets
10850 * @actions: the bitmask of possible actions for a drag from this
10853 * Turns @tree_view into a drop destination for automatic DND. Calling
10854 * this method sets #PsppSheetView:reorderable to %FALSE.
10857 pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view,
10858 const GtkTargetEntry *targets,
10860 GdkDragAction actions)
10862 TreeViewDragInfo *di;
10864 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10866 gtk_drag_dest_set (GTK_WIDGET (tree_view),
10872 di = ensure_info (tree_view);
10873 di->dest_set = TRUE;
10875 unset_reorderable (tree_view);
10879 * pspp_sheet_view_unset_rows_drag_source:
10880 * @tree_view: a #PsppSheetView
10882 * Undoes the effect of
10883 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
10884 * #PsppSheetView:reorderable to %FALSE.
10887 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
10889 TreeViewDragInfo *di;
10891 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10893 di = get_info (tree_view);
10897 if (di->source_set)
10899 gtk_drag_source_unset (GTK_WIDGET (tree_view));
10900 di->source_set = FALSE;
10903 if (!di->dest_set && !di->source_set)
10904 remove_info (tree_view);
10907 unset_reorderable (tree_view);
10911 * pspp_sheet_view_unset_rows_drag_dest:
10912 * @tree_view: a #PsppSheetView
10914 * Undoes the effect of
10915 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
10916 * #PsppSheetView:reorderable to %FALSE.
10919 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
10921 TreeViewDragInfo *di;
10923 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10925 di = get_info (tree_view);
10931 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
10932 di->dest_set = FALSE;
10935 if (!di->dest_set && !di->source_set)
10936 remove_info (tree_view);
10939 unset_reorderable (tree_view);
10943 * pspp_sheet_view_set_drag_dest_row:
10944 * @tree_view: a #PsppSheetView
10945 * @path: (allow-none): The path of the row to highlight, or %NULL.
10946 * @pos: Specifies whether to drop before, after or into the row
10948 * Sets the row that is highlighted for feedback.
10951 pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view,
10953 PsppSheetViewDropPosition pos)
10955 GtkTreePath *current_dest;
10957 /* Note; this function is exported to allow a custom DND
10958 * implementation, so it can't touch TreeViewDragInfo
10961 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10963 current_dest = NULL;
10965 if (tree_view->priv->drag_dest_row)
10967 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
10968 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
10971 /* special case a drop on an empty model */
10972 tree_view->priv->empty_view_drop = 0;
10974 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
10975 && gtk_tree_path_get_depth (path) == 1
10976 && gtk_tree_path_get_indices (path)[0] == 0)
10980 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
10984 tree_view->priv->empty_view_drop = 1;
10987 tree_view->priv->drag_dest_pos = pos;
10991 tree_view->priv->drag_dest_row =
10992 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
10993 pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
10996 tree_view->priv->drag_dest_row = NULL;
11000 int node, new_node;
11002 _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11003 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11007 new_node = pspp_sheet_view_node_next (tree_view, node);
11009 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11011 new_node = pspp_sheet_view_node_prev (tree_view, node);
11013 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11015 gtk_tree_path_free (current_dest);
11020 * pspp_sheet_view_get_drag_dest_row:
11021 * @tree_view: a #PsppSheetView
11022 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11023 * @pos: (allow-none): Return location for the drop position, or %NULL
11025 * Gets information about the row that is highlighted for feedback.
11028 pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view,
11029 GtkTreePath **path,
11030 PsppSheetViewDropPosition *pos)
11032 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11036 if (tree_view->priv->drag_dest_row)
11037 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11040 if (tree_view->priv->empty_view_drop)
11041 *path = gtk_tree_path_new_from_indices (0, -1);
11048 *pos = tree_view->priv->drag_dest_pos;
11052 * pspp_sheet_view_get_dest_row_at_pos:
11053 * @tree_view: a #PsppSheetView
11054 * @drag_x: the position to determine the destination row for
11055 * @drag_y: the position to determine the destination row for
11056 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11057 * @pos: (allow-none): Return location for the drop position, or %NULL
11059 * Determines the destination row for a given position. @drag_x and
11060 * @drag_y are expected to be in widget coordinates. This function is only
11061 * meaningful if @tree_view is realized. Therefore this function will always
11062 * return %FALSE if @tree_view is not realized or does not have a model.
11064 * Return value: whether there is a row at the given position, %TRUE if this
11065 * is indeed the case.
11068 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view,
11071 GtkTreePath **path,
11072 PsppSheetViewDropPosition *pos)
11076 gdouble offset_into_row;
11079 PsppSheetViewColumn *column = NULL;
11080 GtkTreePath *tmp_path = NULL;
11082 /* Note; this function is exported to allow a custom DND
11083 * implementation, so it can't touch TreeViewDragInfo
11086 g_return_val_if_fail (tree_view != NULL, FALSE);
11087 g_return_val_if_fail (drag_x >= 0, FALSE);
11088 g_return_val_if_fail (drag_y >= 0, FALSE);
11093 if (tree_view->priv->bin_window == NULL)
11096 if (tree_view->priv->row_count == 0)
11099 /* If in the top third of a row, we drop before that row; if
11100 * in the bottom third, drop after that row; if in the middle,
11101 * and the row has children, drop into the row.
11103 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11106 if (!pspp_sheet_view_get_path_at_pos (tree_view,
11115 pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11118 offset_into_row = cell_y;
11123 gtk_tree_path_free (tmp_path);
11127 third = cell.height / 3.0;
11131 if (offset_into_row < third)
11133 *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11135 else if (offset_into_row < (cell.height / 2.0))
11137 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11139 else if (offset_into_row < third * 2.0)
11141 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11145 *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11153 #if GTK3_TRANSITION
11154 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11156 * pspp_sheet_view_create_row_drag_icon:
11157 * @tree_view: a #PsppSheetView
11158 * @path: a #GtkTreePath in @tree_view
11160 * Creates a #GdkPixmap representation of the row at @path.
11161 * This image is used for a drag icon.
11163 * Return value: a newly-allocated pixmap of the drag icon.
11166 pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view,
11173 GdkRectangle background_area;
11174 GdkRectangle expose_area;
11176 /* start drawing inside the black outline */
11178 GdkDrawable *drawable;
11179 gint bin_window_width;
11182 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11183 g_return_val_if_fail (path != NULL, NULL);
11185 widget = GTK_WIDGET (tree_view);
11187 if (!gtk_widget_get_realized (widget))
11190 _pspp_sheet_view_find_node (tree_view,
11197 if (!gtk_tree_model_get_iter (tree_view->priv->model,
11204 background_area.y = y;
11205 background_area.height = ROW_HEIGHT (tree_view);
11207 bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
11209 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11210 bin_window_width + 2,
11211 background_area.height + 2,
11216 expose_area.width = bin_window_width + 2;
11217 expose_area.height = background_area.height + 2;
11219 #if GTK3_TRANSITION
11220 gdk_draw_rectangle (drawable,
11221 widget->style->base_gc [gtk_widget_get_state (widget)],
11224 bin_window_width + 2,
11225 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 #if GTK3_TRANSITION
11266 gdk_draw_rectangle (drawable,
11267 widget->style->black_gc,
11270 bin_window_width + 1,
11271 background_area.height + 1);
11279 * pspp_sheet_view_set_destroy_count_func:
11280 * @tree_view: A #PsppSheetView
11281 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11282 * @data: (allow-none): User data to be passed to @func, or %NULL
11283 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11285 * This function should almost never be used. It is meant for private use by
11286 * ATK for determining the number of visible children that are removed when a row is deleted.
11289 pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view,
11290 PsppSheetDestroyCountFunc func,
11292 GDestroyNotify destroy)
11294 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11296 if (tree_view->priv->destroy_count_destroy)
11297 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11299 tree_view->priv->destroy_count_func = func;
11300 tree_view->priv->destroy_count_data = data;
11301 tree_view->priv->destroy_count_destroy = destroy;
11306 * Interactive search
11310 * pspp_sheet_view_set_enable_search:
11311 * @tree_view: A #PsppSheetView
11312 * @enable_search: %TRUE, if the user can search interactively
11314 * If @enable_search is set, then the user can type in text to search through
11315 * the tree interactively (this is sometimes called "typeahead find").
11317 * Note that even if this is %FALSE, the user can still initiate a search
11318 * using the "start-interactive-search" key binding.
11321 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11322 gboolean enable_search)
11324 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11326 enable_search = !!enable_search;
11328 if (tree_view->priv->enable_search != enable_search)
11330 tree_view->priv->enable_search = enable_search;
11331 g_object_notify (G_OBJECT (tree_view), "enable-search");
11336 * pspp_sheet_view_get_enable_search:
11337 * @tree_view: A #PsppSheetView
11339 * Returns whether or not the tree allows to start interactive searching
11340 * by typing in text.
11342 * Return value: whether or not to let the user search interactively
11345 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11347 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11349 return tree_view->priv->enable_search;
11354 * pspp_sheet_view_get_search_column:
11355 * @tree_view: A #PsppSheetView
11357 * Gets the column searched on by the interactive search code.
11359 * Return value: the column the interactive search code searches in.
11362 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11364 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11366 return (tree_view->priv->search_column);
11370 * pspp_sheet_view_set_search_column:
11371 * @tree_view: A #PsppSheetView
11372 * @column: the column of the model to search in, or -1 to disable searching
11374 * Sets @column as the column where the interactive search code should
11375 * search in for the current model.
11377 * If the search column is set, users can use the "start-interactive-search"
11378 * key binding to bring up search popup. The enable-search property controls
11379 * whether simply typing text will also start an interactive search.
11381 * Note that @column refers to a column of the current model. The search
11382 * column is reset to -1 when the model is changed.
11385 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11388 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11389 g_return_if_fail (column >= -1);
11391 if (tree_view->priv->search_column == column)
11394 tree_view->priv->search_column = column;
11395 g_object_notify (G_OBJECT (tree_view), "search-column");
11399 * pspp_sheet_view_get_search_equal_func:
11400 * @tree_view: A #PsppSheetView
11402 * Returns the compare function currently in use.
11404 * Return value: the currently used compare function for the search code.
11407 PsppSheetViewSearchEqualFunc
11408 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11410 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11412 return tree_view->priv->search_equal_func;
11416 * pspp_sheet_view_set_search_equal_func:
11417 * @tree_view: A #PsppSheetView
11418 * @search_equal_func: the compare function to use during the search
11419 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11420 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11422 * Sets the compare function for the interactive search capabilities; note
11423 * that somewhat like strcmp() returning 0 for equality
11424 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11427 pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view,
11428 PsppSheetViewSearchEqualFunc search_equal_func,
11429 gpointer search_user_data,
11430 GDestroyNotify search_destroy)
11432 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11433 g_return_if_fail (search_equal_func != NULL);
11435 if (tree_view->priv->search_destroy)
11436 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11438 tree_view->priv->search_equal_func = search_equal_func;
11439 tree_view->priv->search_user_data = search_user_data;
11440 tree_view->priv->search_destroy = search_destroy;
11441 if (tree_view->priv->search_equal_func == NULL)
11442 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11446 * pspp_sheet_view_get_search_entry:
11447 * @tree_view: A #PsppSheetView
11449 * Returns the #GtkEntry which is currently in use as interactive search
11450 * entry for @tree_view. In case the built-in entry is being used, %NULL
11451 * will be returned.
11453 * Return value: the entry currently in use as search entry.
11458 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11460 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11462 if (tree_view->priv->search_custom_entry_set)
11463 return GTK_ENTRY (tree_view->priv->search_entry);
11469 * pspp_sheet_view_set_search_entry:
11470 * @tree_view: A #PsppSheetView
11471 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11473 * Sets the entry which the interactive search code will use for this
11474 * @tree_view. This is useful when you want to provide a search entry
11475 * in our interface at all time at a fixed position. Passing %NULL for
11476 * @entry will make the interactive search code use the built-in popup
11482 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11485 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11486 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11488 if (tree_view->priv->search_custom_entry_set)
11490 if (tree_view->priv->search_entry_changed_id)
11492 g_signal_handler_disconnect (tree_view->priv->search_entry,
11493 tree_view->priv->search_entry_changed_id);
11494 tree_view->priv->search_entry_changed_id = 0;
11496 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11497 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11500 g_object_unref (tree_view->priv->search_entry);
11502 else if (tree_view->priv->search_window)
11504 gtk_widget_destroy (tree_view->priv->search_window);
11506 tree_view->priv->search_window = NULL;
11511 tree_view->priv->search_entry = g_object_ref (entry);
11512 tree_view->priv->search_custom_entry_set = TRUE;
11514 if (tree_view->priv->search_entry_changed_id == 0)
11516 tree_view->priv->search_entry_changed_id =
11517 g_signal_connect (tree_view->priv->search_entry, "changed",
11518 G_CALLBACK (pspp_sheet_view_search_init),
11522 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11523 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11526 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11530 tree_view->priv->search_entry = NULL;
11531 tree_view->priv->search_custom_entry_set = FALSE;
11536 * pspp_sheet_view_set_search_position_func:
11537 * @tree_view: A #PsppSheetView
11538 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11539 * to use the default search position function
11540 * @data: (allow-none): user data to pass to @func, or %NULL
11541 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11543 * Sets the function to use when positioning the search dialog.
11548 pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view,
11549 PsppSheetViewSearchPositionFunc func,
11550 gpointer user_data,
11551 GDestroyNotify destroy)
11553 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11555 if (tree_view->priv->search_position_destroy)
11556 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11558 tree_view->priv->search_position_func = func;
11559 tree_view->priv->search_position_user_data = user_data;
11560 tree_view->priv->search_position_destroy = destroy;
11561 if (tree_view->priv->search_position_func == NULL)
11562 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11566 * pspp_sheet_view_get_search_position_func:
11567 * @tree_view: A #PsppSheetView
11569 * Returns the positioning function currently in use.
11571 * Return value: the currently used function for positioning the search dialog.
11575 PsppSheetViewSearchPositionFunc
11576 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11578 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11580 return tree_view->priv->search_position_func;
11585 pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
11586 PsppSheetView *tree_view)
11588 if (tree_view->priv->disable_popdown)
11591 if (tree_view->priv->search_entry_changed_id)
11593 g_signal_handler_disconnect (tree_view->priv->search_entry,
11594 tree_view->priv->search_entry_changed_id);
11595 tree_view->priv->search_entry_changed_id = 0;
11597 if (tree_view->priv->typeselect_flush_timeout)
11599 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11600 tree_view->priv->typeselect_flush_timeout = 0;
11603 if (gtk_widget_get_visible (search_dialog))
11605 /* send focus-in event */
11606 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11607 gtk_widget_hide (search_dialog);
11608 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11609 send_focus_change (GTK_WIDGET (tree_view), TRUE);
11614 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11615 GtkWidget *search_dialog,
11616 gpointer user_data)
11619 gint tree_x, tree_y;
11620 gint tree_width, tree_height;
11621 GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
11622 GdkScreen *screen = gdk_window_get_screen (tree_window);
11623 GtkRequisition requisition;
11625 GdkRectangle monitor;
11627 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11628 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11630 gtk_widget_realize (search_dialog);
11632 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11633 tree_width = gdk_window_get_width (tree_window);
11634 tree_height = gdk_window_get_height (tree_window);
11636 gtk_widget_size_request (search_dialog, &requisition);
11638 if (tree_x + tree_width > gdk_screen_get_width (screen))
11639 x = gdk_screen_get_width (screen) - requisition.width;
11640 else if (tree_x + tree_width - requisition.width < 0)
11643 x = tree_x + tree_width - requisition.width;
11645 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11646 y = gdk_screen_get_height (screen) - requisition.height;
11647 else if (tree_y + tree_height < 0) /* isn't really possible ... */
11650 y = tree_y + tree_height;
11652 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11656 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11660 PsppSheetView *tree_view = (PsppSheetView *)data;
11662 tree_view->priv->disable_popdown = 1;
11663 g_signal_connect (menu, "hide",
11664 G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11667 #if GTK3_TRANSITION
11668 /* Because we're visible but offscreen, we just set a flag in the preedit
11672 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11673 PsppSheetView *tree_view)
11675 tree_view->priv->imcontext_changed = 1;
11676 if (tree_view->priv->typeselect_flush_timeout)
11678 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11679 tree_view->priv->typeselect_flush_timeout =
11680 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11681 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11689 pspp_sheet_view_search_activate (GtkEntry *entry,
11690 PsppSheetView *tree_view)
11695 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11698 /* If we have a row selected and it's the cursor row, we activate
11700 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11702 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11704 _pspp_sheet_view_find_node (tree_view, path, &node);
11706 if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11707 pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11709 gtk_tree_path_free (path);
11714 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11716 PsppSheetView *tree_view = (PsppSheetView *)data;
11718 tree_view->priv->disable_popdown = 0;
11724 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11727 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11731 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11732 GdkEventAny *event,
11733 PsppSheetView *tree_view)
11735 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11737 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11743 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11744 GdkEventButton *event,
11745 PsppSheetView *tree_view)
11747 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11749 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11751 if (event->window == tree_view->priv->bin_window)
11752 pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
11758 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
11759 GdkEventScroll *event,
11760 PsppSheetView *tree_view)
11762 gboolean retval = FALSE;
11764 if (event->direction == GDK_SCROLL_UP)
11766 pspp_sheet_view_search_move (widget, tree_view, TRUE);
11769 else if (event->direction == GDK_SCROLL_DOWN)
11771 pspp_sheet_view_search_move (widget, tree_view, FALSE);
11775 /* renew the flush timeout */
11776 if (retval && tree_view->priv->typeselect_flush_timeout
11777 && !tree_view->priv->search_custom_entry_set)
11779 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11780 tree_view->priv->typeselect_flush_timeout =
11781 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11782 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11790 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
11791 GdkEventKey *event,
11792 PsppSheetView *tree_view)
11794 gboolean retval = FALSE;
11796 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11797 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11799 /* close window and cancel the search */
11800 if (!tree_view->priv->search_custom_entry_set
11801 && (event->keyval == GDK_Escape ||
11802 event->keyval == GDK_Tab ||
11803 event->keyval == GDK_KP_Tab ||
11804 event->keyval == GDK_ISO_Left_Tab))
11806 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11810 /* select previous matching iter */
11811 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
11813 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11814 gtk_widget_error_bell (widget);
11819 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
11820 && (event->keyval == GDK_g || event->keyval == GDK_G))
11822 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11823 gtk_widget_error_bell (widget);
11828 /* select next matching iter */
11829 if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
11831 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11832 gtk_widget_error_bell (widget);
11837 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
11838 && (event->keyval == GDK_g || event->keyval == GDK_G))
11840 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11841 gtk_widget_error_bell (widget);
11846 /* renew the flush timeout */
11847 if (retval && tree_view->priv->typeselect_flush_timeout
11848 && !tree_view->priv->search_custom_entry_set)
11850 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11851 tree_view->priv->typeselect_flush_timeout =
11852 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11853 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11860 /* this function returns FALSE if there is a search string but
11861 * nothing was found, and TRUE otherwise.
11864 pspp_sheet_view_search_move (GtkWidget *window,
11865 PsppSheetView *tree_view,
11873 GtkTreeModel *model;
11874 PsppSheetSelection *selection;
11876 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
11878 g_return_val_if_fail (text != NULL, FALSE);
11880 len = strlen (text);
11882 if (up && tree_view->priv->selected_iter == 1)
11883 return strlen (text) < 1;
11885 len = strlen (text);
11890 model = pspp_sheet_view_get_model (tree_view);
11891 selection = pspp_sheet_view_get_selection (tree_view);
11894 pspp_sheet_selection_unselect_all (selection);
11895 if (!gtk_tree_model_get_iter_first (model, &iter))
11898 ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
11899 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
11904 tree_view->priv->selected_iter += up?(-1):(1);
11909 /* return to old iter */
11911 gtk_tree_model_get_iter_first (model, &iter);
11912 pspp_sheet_view_search_iter (model, selection,
11914 &count, tree_view->priv->selected_iter);
11920 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
11924 gpointer search_data)
11926 gboolean retval = TRUE;
11928 gchar *normalized_string;
11929 gchar *normalized_key;
11930 gchar *case_normalized_string = NULL;
11931 gchar *case_normalized_key = NULL;
11932 GValue value = {0,};
11933 GValue transformed = {0,};
11935 gtk_tree_model_get_value (model, iter, column, &value);
11937 g_value_init (&transformed, G_TYPE_STRING);
11939 if (!g_value_transform (&value, &transformed))
11941 g_value_unset (&value);
11945 g_value_unset (&value);
11947 str = g_value_get_string (&transformed);
11950 g_value_unset (&transformed);
11954 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
11955 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
11957 if (normalized_string && normalized_key)
11959 case_normalized_string = g_utf8_casefold (normalized_string, -1);
11960 case_normalized_key = g_utf8_casefold (normalized_key, -1);
11962 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
11966 g_value_unset (&transformed);
11967 g_free (normalized_key);
11968 g_free (normalized_string);
11969 g_free (case_normalized_key);
11970 g_free (case_normalized_string);
11976 pspp_sheet_view_search_iter (GtkTreeModel *model,
11977 PsppSheetSelection *selection,
11986 PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
11988 path = gtk_tree_model_get_path (model, iter);
11989 _pspp_sheet_view_find_node (tree_view, path, &node);
11993 gboolean done = FALSE;
11995 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
12000 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
12002 pspp_sheet_selection_select_iter (selection, iter);
12003 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12006 gtk_tree_path_free (path);
12015 node = pspp_sheet_view_node_next (tree_view, node);
12021 has_next = gtk_tree_model_iter_next (model, iter);
12024 gtk_tree_path_next (path);
12027 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12032 gtk_tree_path_free (path);
12034 /* we've run out of tree, done with this func */
12046 pspp_sheet_view_search_init (GtkWidget *entry,
12047 PsppSheetView *tree_view)
12053 GtkTreeModel *model;
12054 PsppSheetSelection *selection;
12056 g_return_if_fail (GTK_IS_ENTRY (entry));
12057 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12059 text = gtk_entry_get_text (GTK_ENTRY (entry));
12061 model = pspp_sheet_view_get_model (tree_view);
12062 selection = pspp_sheet_view_get_selection (tree_view);
12065 pspp_sheet_selection_unselect_all (selection);
12066 if (tree_view->priv->typeselect_flush_timeout
12067 && !tree_view->priv->search_custom_entry_set)
12069 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12070 tree_view->priv->typeselect_flush_timeout =
12071 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12072 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12079 if (!gtk_tree_model_get_iter_first (model, &iter))
12082 ret = pspp_sheet_view_search_iter (model, selection,
12087 tree_view->priv->selected_iter = 1;
12091 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12092 PsppSheetView *tree_view)
12094 if (tree_view->priv->edited_column == NULL)
12097 _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12098 tree_view->priv->edited_column = NULL;
12100 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12101 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12103 g_signal_handlers_disconnect_by_func (cell_editable,
12104 pspp_sheet_view_remove_widget,
12106 g_signal_handlers_disconnect_by_func (cell_editable,
12107 pspp_sheet_view_editable_button_press_event,
12109 g_signal_handlers_disconnect_by_func (cell_editable,
12110 pspp_sheet_view_editable_clicked,
12113 gtk_container_remove (GTK_CONTAINER (tree_view),
12114 GTK_WIDGET (cell_editable));
12116 /* FIXME should only redraw a single node */
12117 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12121 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12122 GtkTreePath *cursor_path)
12125 GdkRectangle background_area;
12126 GdkRectangle cell_area;
12127 GtkCellEditable *editable_widget = NULL;
12128 gchar *path_string;
12129 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12130 gint retval = FALSE;
12133 g_assert (tree_view->priv->focus_column);
12135 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12138 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12139 if (cursor_node < 0)
12142 path_string = gtk_tree_path_to_string (cursor_path);
12143 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12145 pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12146 tree_view->priv->model,
12148 pspp_sheet_view_get_background_area (tree_view,
12150 tree_view->priv->focus_column,
12152 pspp_sheet_view_get_cell_area (tree_view,
12154 tree_view->priv->focus_column,
12157 if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12166 if (editable_widget != NULL)
12170 GtkCellRenderer *cell;
12173 cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12175 _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12178 area.width -= right + left;
12180 pspp_sheet_view_real_start_editing (tree_view,
12181 tree_view->priv->focus_column,
12190 g_free (path_string);
12195 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12196 GdkEventButton *event,
12197 PsppSheetView *sheet_view)
12201 node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12202 "pspp-sheet-view-node"));
12203 return pspp_sheet_view_row_head_clicked (sheet_view,
12205 sheet_view->priv->edited_column,
12210 pspp_sheet_view_editable_clicked (GtkButton *button,
12211 PsppSheetView *sheet_view)
12213 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12218 is_all_selected (GtkWidget *widget)
12220 GtkEntryBuffer *buffer;
12221 gint start_pos, end_pos;
12223 if (!GTK_IS_ENTRY (widget))
12226 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12227 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12228 &start_pos, &end_pos)
12230 && end_pos == gtk_entry_buffer_get_length (buffer));
12234 is_at_left (GtkWidget *widget)
12236 return (GTK_IS_ENTRY (widget)
12237 && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12241 is_at_right (GtkWidget *widget)
12243 GtkEntryBuffer *buffer;
12246 if (!GTK_IS_ENTRY (widget))
12249 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12250 length = gtk_entry_buffer_get_length (buffer);
12251 return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12255 pspp_sheet_view_event (GtkWidget *widget,
12256 GdkEventKey *event,
12257 PsppSheetView *tree_view)
12259 PsppSheetViewColumn *column;
12266 /* Intercept only key press events.
12267 It would make sense to use "key-press-event" instead of "event", but
12268 GtkEntry attaches its own signal handler to "key-press-event" that runs
12269 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12271 if (event->type != GDK_KEY_PRESS)
12274 keyval = event->keyval;
12276 switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12279 switch (event->keyval)
12281 case GDK_Left: case GDK_KP_Left:
12282 case GDK_Home: case GDK_KP_Home:
12283 if (!is_all_selected (widget) && !is_at_left (widget))
12287 case GDK_Right: case GDK_KP_Right:
12288 case GDK_End: case GDK_KP_End:
12289 if (!is_all_selected (widget) && !is_at_right (widget))
12293 case GDK_Up: case GDK_KP_Up:
12294 case GDK_Down: case GDK_KP_Down:
12297 case GDK_Page_Up: case GDK_KP_Page_Up:
12298 case GDK_Page_Down: case GDK_KP_Page_Down:
12309 case GDK_Tab: case GDK_KP_Tab:
12310 case GDK_ISO_Left_Tab:
12319 case GDK_SHIFT_MASK:
12320 switch (event->keyval)
12323 case GDK_ISO_Left_Tab:
12332 case GDK_CONTROL_MASK:
12333 switch (event->keyval)
12335 case GDK_Left: case GDK_KP_Left:
12336 if (!is_all_selected (widget) && !is_at_left (widget))
12340 case GDK_Right: case GDK_KP_Right:
12341 if (!is_all_selected (widget) && !is_at_right (widget))
12345 case GDK_Up: case GDK_KP_Up:
12346 case GDK_Down: case GDK_KP_Down:
12358 row = tree_view->priv->edited_row;
12359 column = tree_view->priv->edited_column;
12360 path = gtk_tree_path_new_from_indices (row, -1);
12362 pspp_sheet_view_stop_editing (tree_view, cancel);
12363 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12365 pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12366 gtk_tree_path_free (path);
12368 handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12369 G_OBJECT (tree_view));
12371 g_signal_stop_emission_by_name (widget, "event");
12373 pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12374 pspp_sheet_view_start_editing (tree_view, path);
12375 gtk_tree_path_free (path);
12381 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12384 PsppSheetView *sheet_view = data;
12386 g_signal_connect (widget, "event",
12387 G_CALLBACK (pspp_sheet_view_event),
12390 if (GTK_IS_CONTAINER (widget))
12391 gtk_container_foreach (GTK_CONTAINER (widget),
12392 pspp_sheet_view_override_cell_keypresses,
12397 pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
12398 PsppSheetViewColumn *column,
12400 GtkCellEditable *cell_editable,
12401 GdkRectangle *cell_area,
12405 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12406 gint pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
12409 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12411 tree_view->priv->edited_column = column;
12412 _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12414 row = gtk_tree_path_get_indices (path)[0];
12415 tree_view->priv->edited_row = row;
12416 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12417 cell_area->y += pre_val - (int)gtk_adjustment_get_value (tree_view->priv->vadjustment);
12419 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12420 pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12421 tree_view->priv->anchor_column = column;
12423 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12425 pspp_sheet_view_put (tree_view,
12426 GTK_WIDGET (cell_editable),
12430 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12431 (GdkEvent *)event);
12433 gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12434 g_signal_connect (cell_editable, "remove-widget",
12435 G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12436 if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12437 GTK_IS_BUTTON (cell_editable))
12439 g_signal_connect (cell_editable, "button-press-event",
12440 G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12442 g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12443 GINT_TO_POINTER (row));
12444 g_signal_connect (cell_editable, "clicked",
12445 G_CALLBACK (pspp_sheet_view_editable_clicked),
12449 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12454 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12455 gboolean cancel_editing)
12457 PsppSheetViewColumn *column;
12458 GtkCellRenderer *cell;
12460 if (tree_view->priv->edited_column == NULL)
12464 * This is very evil. We need to do this, because
12465 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12466 * later on. If pspp_sheet_view_row_changed notices
12467 * tree_view->priv->edited_column != NULL, it'll call
12468 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12470 * Please read that again if you intend to modify anything here.
12473 column = tree_view->priv->edited_column;
12474 tree_view->priv->edited_column = NULL;
12476 cell = _pspp_sheet_view_column_get_edited_cell (column);
12477 gtk_cell_renderer_stop_editing (cell, cancel_editing);
12479 if (!cancel_editing)
12480 gtk_cell_editable_editing_done (column->editable_widget);
12482 tree_view->priv->edited_column = column;
12484 gtk_cell_editable_remove_widget (column->editable_widget);
12489 * pspp_sheet_view_set_hover_selection:
12490 * @tree_view: a #PsppSheetView
12491 * @hover: %TRUE to enable hover selection mode
12493 * Enables of disables the hover selection mode of @tree_view.
12494 * Hover selection makes the selected row follow the pointer.
12495 * Currently, this works only for the selection modes
12496 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12501 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12504 hover = hover != FALSE;
12506 if (hover != tree_view->priv->hover_selection)
12508 tree_view->priv->hover_selection = hover;
12510 g_object_notify (G_OBJECT (tree_view), "hover-selection");
12515 * pspp_sheet_view_get_hover_selection:
12516 * @tree_view: a #PsppSheetView
12518 * Returns whether hover selection mode is turned on for @tree_view.
12520 * Return value: %TRUE if @tree_view is in hover selection mode
12525 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12527 return tree_view->priv->hover_selection;
12531 * pspp_sheet_view_set_rubber_banding:
12532 * @tree_view: a #PsppSheetView
12533 * @enable: %TRUE to enable rubber banding
12535 * Enables or disables rubber banding in @tree_view. If the selection mode is
12536 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12537 * banding will allow the user to select multiple rows by dragging the mouse.
12542 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12545 enable = enable != FALSE;
12547 if (enable != tree_view->priv->rubber_banding_enable)
12549 tree_view->priv->rubber_banding_enable = enable;
12551 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12556 * pspp_sheet_view_get_rubber_banding:
12557 * @tree_view: a #PsppSheetView
12559 * Returns whether rubber banding is turned on for @tree_view. If the
12560 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12561 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12562 * select multiple rows by dragging the mouse.
12564 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12569 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12571 return tree_view->priv->rubber_banding_enable;
12575 * pspp_sheet_view_is_rubber_banding_active:
12576 * @tree_view: a #PsppSheetView
12578 * Returns whether a rubber banding operation is currently being done
12581 * Return value: %TRUE if a rubber banding operation is currently being
12582 * done in @tree_view.
12587 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12589 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12591 if (tree_view->priv->rubber_banding_enable
12592 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12599 pspp_sheet_view_grab_notify (GtkWidget *widget,
12600 gboolean was_grabbed)
12602 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12604 tree_view->priv->in_grab = !was_grabbed;
12608 tree_view->priv->pressed_button = -1;
12610 if (tree_view->priv->rubber_band_status)
12611 pspp_sheet_view_stop_rubber_band (tree_view);
12616 pspp_sheet_view_state_changed (GtkWidget *widget,
12617 GtkStateType previous_state)
12619 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12621 if (gtk_widget_get_realized (widget))
12623 GtkStyle *style = gtk_widget_get_style (widget);
12624 gdk_window_set_background (tree_view->priv->bin_window, &style->base[gtk_widget_get_state (widget)]);
12627 gtk_widget_queue_draw (widget);
12631 * pspp_sheet_view_get_grid_lines:
12632 * @tree_view: a #PsppSheetView
12634 * Returns which grid lines are enabled in @tree_view.
12636 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12641 PsppSheetViewGridLines
12642 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12644 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12646 return tree_view->priv->grid_lines;
12650 * pspp_sheet_view_set_grid_lines:
12651 * @tree_view: a #PsppSheetView
12652 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12655 * Sets which grid lines to draw in @tree_view.
12660 pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view,
12661 PsppSheetViewGridLines grid_lines)
12663 PsppSheetViewPrivate *priv;
12664 PsppSheetViewGridLines old_grid_lines;
12666 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12668 priv = tree_view->priv;
12670 old_grid_lines = priv->grid_lines;
12671 priv->grid_lines = grid_lines;
12673 if (old_grid_lines != grid_lines)
12675 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12677 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12682 * pspp_sheet_view_get_special_cells:
12683 * @tree_view: a #PsppSheetView
12685 * Returns which grid lines are enabled in @tree_view.
12687 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12688 * the sheet view contain special cells.
12690 PsppSheetViewSpecialCells
12691 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12693 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12695 return tree_view->priv->special_cells;
12699 * pspp_sheet_view_set_special_cells:
12700 * @tree_view: a #PsppSheetView
12701 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12702 * the sheet view contain special cells.
12704 * Sets whether rows in the sheet view contain special cells, controlling the
12705 * rendering of row selections.
12708 pspp_sheet_view_set_special_cells (PsppSheetView *tree_view,
12709 PsppSheetViewSpecialCells special_cells)
12711 PsppSheetViewPrivate *priv;
12713 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12715 priv = tree_view->priv;
12717 if (priv->special_cells != special_cells)
12719 priv->special_cells = special_cells;
12720 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12721 g_object_notify (G_OBJECT (tree_view), "special-cells");
12726 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12728 /* XXX (re)calculate fixed_height if necessary */
12729 return tree_view->priv->fixed_height;
12733 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
12736 g_return_if_fail (fixed_height > 0);
12738 if (tree_view->priv->fixed_height != fixed_height)
12740 tree_view->priv->fixed_height = fixed_height;
12741 g_object_notify (G_OBJECT (tree_view), "fixed-height");
12743 if (!tree_view->priv->fixed_height_set)
12745 tree_view->priv->fixed_height_set = TRUE;
12746 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
12751 * pspp_sheet_view_set_tooltip_row:
12752 * @tree_view: a #PsppSheetView
12753 * @tooltip: a #GtkTooltip
12754 * @path: a #GtkTreePath
12756 * Sets the tip area of @tooltip to be the area covered by the row at @path.
12757 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12758 * See also gtk_tooltip_set_tip_area().
12763 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
12764 GtkTooltip *tooltip,
12767 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12768 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12770 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
12774 * pspp_sheet_view_set_tooltip_cell:
12775 * @tree_view: a #PsppSheetView
12776 * @tooltip: a #GtkTooltip
12777 * @path: (allow-none): a #GtkTreePath or %NULL
12778 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
12779 * @cell: (allow-none): a #GtkCellRenderer or %NULL
12781 * Sets the tip area of @tooltip to the area @path, @column and @cell have
12782 * in common. For example if @path is %NULL and @column is set, the tip
12783 * area will be set to the full area covered by @column. See also
12784 * gtk_tooltip_set_tip_area().
12786 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12791 pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view,
12792 GtkTooltip *tooltip,
12794 PsppSheetViewColumn *column,
12795 GtkCellRenderer *cell)
12799 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12800 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12801 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
12802 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
12804 /* Determine x values. */
12805 if (column && cell)
12810 pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
12811 pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
12813 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12816 rect.width = width;
12822 pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
12823 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12826 rect.width = tmp.width;
12830 GtkAllocation allocation;
12831 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
12833 rect.width = allocation.width;
12836 /* Determine y values. */
12841 pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
12842 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12845 rect.height = tmp.height;
12850 rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
12853 gtk_tooltip_set_tip_area (tooltip, &rect);
12857 * pspp_sheet_view_get_tooltip_context:
12858 * @tree_view: a #PsppSheetView
12859 * @x: the x coordinate (relative to widget coordinates)
12860 * @y: the y coordinate (relative to widget coordinates)
12861 * @keyboard_tip: whether this is a keyboard tooltip or not
12862 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
12863 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
12864 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
12866 * This function is supposed to be used in a #GtkWidget::query-tooltip
12867 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
12868 * which are received in the signal handler, should be passed to this
12869 * function without modification.
12871 * The return value indicates whether there is a tree view row at the given
12872 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
12873 * tooltips the row returned will be the cursor row. When %TRUE, then any of
12874 * @model, @path and @iter which have been provided will be set to point to
12875 * that row and the corresponding model. @x and @y will always be converted
12876 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
12878 * Return value: whether or not the given tooltip context points to a row.
12883 pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view,
12886 gboolean keyboard_tip,
12887 GtkTreeModel **model,
12888 GtkTreePath **path,
12891 GtkTreePath *tmppath = NULL;
12893 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12894 g_return_val_if_fail (x != NULL, FALSE);
12895 g_return_val_if_fail (y != NULL, FALSE);
12899 pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
12906 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
12909 if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
12910 &tmppath, NULL, NULL, NULL))
12915 *model = pspp_sheet_view_get_model (tree_view);
12918 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
12924 gtk_tree_path_free (tmppath);
12930 pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget,
12933 gboolean keyboard_tip,
12934 GtkTooltip *tooltip,
12937 GValue value = { 0, };
12938 GValue transformed = { 0, };
12941 GtkTreeModel *model;
12942 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12944 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
12947 &model, &path, &iter))
12950 gtk_tree_model_get_value (model, &iter,
12951 tree_view->priv->tooltip_column, &value);
12953 g_value_init (&transformed, G_TYPE_STRING);
12955 if (!g_value_transform (&value, &transformed))
12957 g_value_unset (&value);
12958 gtk_tree_path_free (path);
12963 g_value_unset (&value);
12965 if (!g_value_get_string (&transformed))
12967 g_value_unset (&transformed);
12968 gtk_tree_path_free (path);
12973 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
12974 pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
12976 gtk_tree_path_free (path);
12977 g_value_unset (&transformed);
12983 * pspp_sheet_view_set_tooltip_column:
12984 * @tree_view: a #PsppSheetView
12985 * @column: an integer, which is a valid column number for @tree_view's model
12987 * If you only plan to have simple (text-only) tooltips on full rows, you
12988 * can use this function to have #PsppSheetView handle these automatically
12989 * for you. @column should be set to the column in @tree_view's model
12990 * containing the tooltip texts, or -1 to disable this feature.
12992 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
12993 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
12995 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
12996 * so &, <, etc have to be escaped in the text.
13001 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
13004 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13006 if (column == tree_view->priv->tooltip_column)
13011 g_signal_handlers_disconnect_by_func (tree_view,
13012 pspp_sheet_view_set_tooltip_query_cb,
13014 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13018 if (tree_view->priv->tooltip_column == -1)
13020 g_signal_connect (tree_view, "query-tooltip",
13021 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13022 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13026 tree_view->priv->tooltip_column = column;
13027 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13031 * pspp_sheet_view_get_tooltip_column:
13032 * @tree_view: a #PsppSheetView
13034 * Returns the column of @tree_view's model which is being used for
13035 * displaying tooltips on @tree_view's rows.
13037 * Return value: the index of the tooltip column that is currently being
13038 * used, or -1 if this is disabled.
13043 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13045 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13047 return tree_view->priv->tooltip_column;
13051 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13052 GValue *return_accu,
13053 const GValue *handler_return,
13056 gboolean continue_emission;
13057 gboolean signal_handled;
13059 signal_handled = g_value_get_boolean (handler_return);
13060 g_value_set_boolean (return_accu, signal_handled);
13061 continue_emission = !signal_handled;
13063 return continue_emission;
13068 pspp_sheet_view_grid_lines_get_type (void)
13070 static GType etype = 0;
13071 if (G_UNLIKELY(etype == 0)) {
13072 static const GEnumValue values[] = {
13073 { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13074 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13075 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13076 { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13079 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13085 pspp_sheet_view_special_cells_get_type (void)
13087 static GType etype = 0;
13088 if (G_UNLIKELY(etype == 0)) {
13089 static const GEnumValue values[] = {
13090 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13091 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13092 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13095 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);