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))
1773 gtk_widget_size_request (child->widget, &child_requisition);
1778 invalidate_column (PsppSheetView *tree_view,
1779 PsppSheetViewColumn *column)
1781 gint column_offset = 0;
1783 GtkWidget *widget = GTK_WIDGET (tree_view);
1786 if (!gtk_widget_get_realized (widget))
1789 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1790 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
1792 list = (rtl ? list->prev : list->next))
1794 PsppSheetViewColumn *tmpcolumn = list->data;
1795 if (tmpcolumn == column)
1797 GdkRectangle invalid_rect;
1798 GtkAllocation allocation;
1800 gtk_widget_get_allocation (widget, &allocation);
1801 invalid_rect.x = column_offset;
1803 invalid_rect.width = column->width;
1804 invalid_rect.height = allocation.height;
1806 gdk_window_invalidate_rect (gtk_widget_get_window (widget), &invalid_rect, TRUE);
1810 column_offset += tmpcolumn->width;
1815 invalidate_last_column (PsppSheetView *tree_view)
1820 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1822 for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
1824 last_column = (rtl ? last_column->next : last_column->prev))
1826 if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible)
1828 invalidate_column (tree_view, last_column->data);
1835 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_view,
1836 PsppSheetViewColumn *column)
1838 gint real_requested_width;
1840 if (column->use_resized_width)
1842 real_requested_width = column->resized_width;
1846 real_requested_width = column->fixed_width;
1849 if (column->min_width != -1)
1850 real_requested_width = MAX (real_requested_width, column->min_width);
1851 if (column->max_width != -1)
1852 real_requested_width = MIN (real_requested_width, column->max_width);
1854 return real_requested_width;
1858 span_intersects (int a0, int a_width,
1859 int b0, int b_width)
1861 int a1 = a0 + a_width;
1862 int b1 = b0 + b_width;
1863 return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1);
1866 /* GtkWidget::size_allocate helper */
1868 pspp_sheet_view_size_allocate_columns (GtkWidget *widget,
1869 gboolean *width_changed)
1871 PsppSheetView *tree_view;
1872 GList *list, *first_column, *last_column;
1873 PsppSheetViewColumn *column;
1874 GtkAllocation col_allocation;
1875 GtkAllocation allocation;
1877 gint extra, extra_per_column;
1878 gint full_requested_width = 0;
1879 gint number_of_expand_columns = 0;
1880 gboolean column_changed = FALSE;
1883 tree_view = PSPP_SHEET_VIEW (widget);
1885 for (last_column = g_list_last (tree_view->priv->columns);
1886 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
1887 last_column = last_column->prev)
1890 if (last_column == NULL)
1893 for (first_column = g_list_first (tree_view->priv->columns);
1894 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
1895 first_column = first_column->next)
1898 col_allocation.y = 0;
1899 col_allocation.height = tree_view->priv->header_height;
1901 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1903 /* find out how many extra space and expandable columns we have */
1904 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
1906 column = (PsppSheetViewColumn *)list->data;
1908 if (!column->visible)
1911 full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1914 number_of_expand_columns++;
1917 gtk_widget_get_allocation (widget, &allocation);
1918 extra = MAX (allocation.width - full_requested_width, 0);
1919 if (number_of_expand_columns > 0)
1920 extra_per_column = extra/number_of_expand_columns;
1922 extra_per_column = 0;
1924 for (list = (rtl ? last_column : first_column);
1925 list != (rtl ? first_column->prev : last_column->next);
1926 list = (rtl ? list->prev : list->next))
1928 gint real_requested_width = 0;
1931 column = list->data;
1932 old_width = column->width;
1934 if (!column->visible)
1937 /* We need to handle the dragged button specially.
1939 if (column == tree_view->priv->drag_column)
1941 GtkAllocation drag_allocation;
1942 drag_allocation.width = gdk_window_get_width (tree_view->priv->drag_window);
1943 drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window);
1944 drag_allocation.x = 0;
1945 drag_allocation.y = 0;
1946 pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column,
1948 width += drag_allocation.width;
1952 real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1954 col_allocation.x = width;
1955 column->width = real_requested_width;
1959 if (number_of_expand_columns == 1)
1961 /* We add the remander to the last column as
1963 column->width += extra;
1967 column->width += extra_per_column;
1968 extra -= extra_per_column;
1969 number_of_expand_columns --;
1973 if (column->width != old_width)
1974 g_object_notify (G_OBJECT (column), "width");
1976 col_allocation.width = column->width;
1977 width += column->width;
1979 if (column->width > old_width)
1980 column_changed = TRUE;
1982 pspp_sheet_view_column_size_allocate (column, &col_allocation);
1985 gdk_window_move_resize (column->window,
1986 col_allocation.x + (rtl ? 0 : col_allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
1988 TREE_VIEW_DRAG_WIDTH, col_allocation.height);
1991 /* We change the width here. The user might have been resizing columns,
1992 * so the total width of the tree view changes.
1994 tree_view->priv->width = width;
1996 *width_changed = TRUE;
1999 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2003 update_childrens_allocation (PsppSheetView *tree_view)
2006 for (tmp_list = tree_view->priv->children; tmp_list; tmp_list = tmp_list->next)
2008 PsppSheetViewChild *child = tmp_list->data;
2009 GtkAllocation allocation;
2012 /* totally ignore our child's requisition */
2013 path = _pspp_sheet_view_find_path (tree_view, child->node);
2014 pspp_sheet_view_get_cell_area (tree_view, path, child->column, &allocation);
2015 gtk_tree_path_free (path);
2016 gtk_widget_size_allocate (child->widget, &allocation);
2021 pspp_sheet_view_size_allocate (GtkWidget *widget,
2022 GtkAllocation *allocation)
2024 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2026 gboolean width_changed = FALSE;
2027 GtkAllocation old_allocation;
2028 gtk_widget_get_allocation (widget, &old_allocation);
2030 if (allocation->width != old_allocation.width)
2031 width_changed = TRUE;
2033 gtk_widget_set_allocation (widget, allocation);
2035 /* We size-allocate the columns first because the width of the
2036 * tree view (used in updating the adjustments below) might change.
2038 pspp_sheet_view_size_allocate_columns (widget, &width_changed);
2040 gtk_adjustment_set_page_size (tree_view->priv->hadjustment, allocation->width);
2041 gtk_adjustment_set_page_increment (tree_view->priv->hadjustment, allocation->width * 0.9);
2042 gtk_adjustment_set_step_increment (tree_view->priv->hadjustment, allocation->width * 0.1);
2043 gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0);
2044 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->hadjustment), tree_view->priv->width));
2046 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2048 if (allocation->width < tree_view->priv->width)
2050 if (tree_view->priv->init_hadjust_value)
2052 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2053 tree_view->priv->init_hadjust_value = FALSE;
2055 else if (allocation->width != old_allocation.width)
2057 gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) - allocation->width + old_allocation.width, 0, tree_view->priv->width - allocation->width));
2060 gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (tree_view->priv->width - (tree_view->priv->prev_width - gtk_adjustment_get_value (tree_view->priv->hadjustment)), 0, tree_view->priv->width - allocation->width));
2064 gtk_adjustment_set_value (tree_view->priv->hadjustment, 0);
2065 tree_view->priv->init_hadjust_value = TRUE;
2069 if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + allocation->width > tree_view->priv->width)
2070 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2072 gtk_adjustment_changed (tree_view->priv->hadjustment);
2074 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2075 gtk_adjustment_set_step_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.1);
2076 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.9);
2077 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
2078 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->vadjustment), tree_view->priv->height));
2080 gtk_adjustment_changed (tree_view->priv->vadjustment);
2082 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2083 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
2084 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2085 else if (gtk_adjustment_get_value (tree_view->priv->vadjustment) + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
2086 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2087 tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
2088 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2089 pspp_sheet_view_top_row_to_dy (tree_view);
2091 pspp_sheet_view_dy_to_top_row (tree_view);
2093 if (gtk_widget_get_realized (widget))
2095 gdk_window_move_resize (gtk_widget_get_window (widget),
2096 allocation->x, allocation->y,
2097 allocation->width, allocation->height);
2098 gdk_window_move_resize (tree_view->priv->header_window,
2099 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2101 MAX (tree_view->priv->width, allocation->width),
2102 tree_view->priv->header_height);
2103 gdk_window_move_resize (tree_view->priv->bin_window,
2104 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2105 TREE_VIEW_HEADER_HEIGHT (tree_view),
2106 MAX (tree_view->priv->width, allocation->width),
2107 allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2110 if (tree_view->priv->row_count == 0)
2111 invalidate_empty_focus (tree_view);
2113 if (gtk_widget_get_realized (widget))
2115 gboolean has_expand_column = FALSE;
2116 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2118 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2120 has_expand_column = TRUE;
2125 /* This little hack only works if we have an LTR locale, and no column has the */
2128 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2129 ! has_expand_column)
2130 invalidate_last_column (tree_view);
2132 gtk_widget_queue_draw (widget);
2134 update_childrens_allocation(tree_view);
2138 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2140 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2142 GtkWidget *widget = GTK_WIDGET (tree_view);
2144 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2145 gtk_widget_grab_focus (widget);
2146 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2150 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2153 return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2157 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2160 range_tower_set1 (tree_view->priv->selected, node, 1);
2164 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2167 range_tower_set0 (tree_view->priv->selected, node, 1);
2171 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2174 return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2178 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2181 return node > 0 ? node - 1 : -1;
2185 all_columns_selected (PsppSheetView *tree_view)
2189 for (list = tree_view->priv->columns; list; list = list->next)
2191 PsppSheetViewColumn *column = list->data;
2192 if (column->selectable && !column->selected)
2200 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2202 PsppSheetViewColumn *column,
2203 GdkEventButton *event)
2205 PsppSheetSelection *selection;
2206 PsppSheetSelectionMode mode;
2208 gboolean update_anchor;
2212 g_return_val_if_fail (tree_view != NULL, FALSE);
2213 g_return_val_if_fail (column != NULL, FALSE);
2215 selection = tree_view->priv->selection;
2216 mode = pspp_sheet_selection_get_mode (selection);
2217 if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2220 if (!column->row_head)
2225 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2226 if (event->type != GDK_BUTTON_PRESS
2227 || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2233 path = gtk_tree_path_new_from_indices (node, -1);
2236 pspp_sheet_selection_unselect_all (selection);
2237 pspp_sheet_selection_select_path (selection, path);
2238 pspp_sheet_selection_select_all_columns (selection);
2239 update_anchor = TRUE;
2242 else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2244 if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2245 || !all_columns_selected (tree_view))
2247 pspp_sheet_selection_unselect_all (selection);
2248 pspp_sheet_selection_select_path (selection, path);
2249 pspp_sheet_selection_select_all_columns (selection);
2250 update_anchor = TRUE;
2254 update_anchor = handled = FALSE;
2256 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2257 && modifiers == GDK_CONTROL_MASK)
2259 if (!all_columns_selected (tree_view))
2261 pspp_sheet_selection_unselect_all (selection);
2262 pspp_sheet_selection_select_all_columns (selection);
2265 if (pspp_sheet_selection_path_is_selected (selection, path))
2266 pspp_sheet_selection_unselect_path (selection, path);
2268 pspp_sheet_selection_select_path (selection, path);
2269 update_anchor = TRUE;
2272 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2273 && modifiers == GDK_SHIFT_MASK)
2275 GtkTreeRowReference *anchor = tree_view->priv->anchor;
2276 GtkTreePath *anchor_path;
2278 if (all_columns_selected (tree_view)
2279 && gtk_tree_row_reference_valid (anchor))
2281 update_anchor = FALSE;
2282 anchor_path = gtk_tree_row_reference_get_path (anchor);
2286 update_anchor = TRUE;
2287 anchor_path = gtk_tree_path_copy (path);
2290 pspp_sheet_selection_unselect_all (selection);
2291 pspp_sheet_selection_select_range (selection, anchor_path, path);
2292 pspp_sheet_selection_select_all_columns (selection);
2294 gtk_tree_path_free (anchor_path);
2299 update_anchor = handled = FALSE;
2303 if (tree_view->priv->anchor)
2304 gtk_tree_row_reference_free (tree_view->priv->anchor);
2305 tree_view->priv->anchor =
2306 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2307 tree_view->priv->model,
2311 gtk_tree_path_free (path);
2316 find_click (PsppSheetView *tree_view,
2319 PsppSheetViewColumn **column,
2320 GdkRectangle *background_area,
2321 GdkRectangle *cell_area)
2328 /* find the node that was clicked */
2329 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2332 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2337 background_area->y = y_offset + y;
2338 background_area->height = ROW_HEIGHT (tree_view);
2339 background_area->x = 0;
2341 /* Let the column have a chance at selecting it. */
2342 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2343 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2344 list; list = (rtl ? list->prev : list->next))
2346 PsppSheetViewColumn *candidate = list->data;
2348 if (!candidate->visible)
2351 background_area->width = candidate->width;
2352 if ((background_area->x > x) ||
2353 (background_area->x + background_area->width <= x))
2355 background_area->x += background_area->width;
2359 /* we found the focus column */
2361 pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2363 *column = candidate;
2371 pspp_sheet_view_button_press (GtkWidget *widget,
2372 GdkEventButton *event)
2374 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2376 PsppSheetViewColumn *column = NULL;
2378 GdkRectangle background_area;
2379 GdkRectangle cell_area;
2382 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2383 pspp_sheet_view_stop_editing (tree_view, FALSE);
2386 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2387 * we're done handling the button press.
2390 if (event->window == tree_view->priv->bin_window)
2395 gint pre_val, aft_val;
2396 PsppSheetViewColumn *column = NULL;
2397 GtkCellRenderer *focus_cell = NULL;
2398 gboolean row_double_click = FALSE;
2401 if (tree_view->priv->row_count == 0)
2403 grab_focus_and_unset_draw_keyfocus (tree_view);
2407 if (!find_click (tree_view, event->x, event->y, &node, &column,
2408 &background_area, &cell_area))
2410 grab_focus_and_unset_draw_keyfocus (tree_view);
2414 tree_view->priv->focus_column = column;
2416 if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2420 pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2422 path = _pspp_sheet_view_find_path (tree_view, node);
2424 /* we only handle selection modifications on the first button press
2426 if (event->type == GDK_BUTTON_PRESS)
2428 PsppSheetSelectionMode mode = 0;
2430 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2431 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
2432 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2433 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
2435 focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2437 pspp_sheet_view_column_focus_cell (column, focus_cell);
2439 if (event->state & GDK_CONTROL_MASK)
2441 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, mode);
2442 pspp_sheet_view_real_toggle_cursor_row (tree_view);
2444 else if (event->state & GDK_SHIFT_MASK)
2446 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
2447 pspp_sheet_view_real_select_cursor_row (tree_view, FALSE, mode);
2451 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
2454 if (tree_view->priv->anchor_column == NULL ||
2455 !(event->state & GDK_SHIFT_MASK))
2456 tree_view->priv->anchor_column = column;
2457 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2458 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2459 tree_view->priv->anchor_column,
2463 /* the treeview may have been scrolled because of _set_cursor,
2467 aft_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2468 dval = pre_val - aft_val;
2470 cell_area.y += dval;
2471 background_area.y += dval;
2473 /* Save press to possibly begin a drag
2475 if (!tree_view->priv->in_grab &&
2476 tree_view->priv->pressed_button < 0)
2478 tree_view->priv->pressed_button = event->button;
2479 tree_view->priv->press_start_x = event->x;
2480 tree_view->priv->press_start_y = event->y;
2481 tree_view->priv->press_start_node = node;
2483 if (tree_view->priv->rubber_banding_enable
2484 && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2485 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2487 tree_view->priv->press_start_y += tree_view->priv->dy;
2488 tree_view->priv->rubber_band_x = event->x;
2489 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2490 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2492 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2493 tree_view->priv->rubber_band_ctrl = TRUE;
2494 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2495 tree_view->priv->rubber_band_shift = TRUE;
2500 /* Test if a double click happened on the same row. */
2501 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2503 int double_click_time, double_click_distance;
2505 g_object_get (gtk_settings_get_for_screen (
2506 gtk_widget_get_screen (widget)),
2507 "gtk-double-click-time", &double_click_time,
2508 "gtk-double-click-distance", &double_click_distance,
2511 /* Same conditions as _gdk_event_button_generate */
2512 if (tree_view->priv->last_button_x != -1 &&
2513 (event->time < tree_view->priv->last_button_time + double_click_time) &&
2514 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2515 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2517 /* We do no longer compare paths of this row and the
2518 * row clicked previously. We use the double click
2519 * distance to decide whether this is a valid click,
2520 * allowing the mouse to slightly move over another row.
2522 row_double_click = TRUE;
2524 tree_view->priv->last_button_time = 0;
2525 tree_view->priv->last_button_x = -1;
2526 tree_view->priv->last_button_y = -1;
2530 tree_view->priv->last_button_time = event->time;
2531 tree_view->priv->last_button_x = event->x;
2532 tree_view->priv->last_button_y = event->y;
2536 if (row_double_click)
2538 gtk_grab_remove (widget);
2539 pspp_sheet_view_row_activated (tree_view, path, column);
2541 if (tree_view->priv->pressed_button == event->button)
2542 tree_view->priv->pressed_button = -1;
2545 gtk_tree_path_free (path);
2547 /* If we activated the row through a double click we don't want to grab
2548 * focus back, as moving focus to another widget is pretty common.
2550 if (!row_double_click)
2551 grab_focus_and_unset_draw_keyfocus (tree_view);
2556 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2558 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2560 column = list->data;
2561 if (event->window == column->window &&
2562 column->resizable &&
2567 if (gdk_pointer_grab (column->window, FALSE,
2568 GDK_POINTER_MOTION_HINT_MASK |
2569 GDK_BUTTON1_MOTION_MASK |
2570 GDK_BUTTON_RELEASE_MASK,
2571 NULL, NULL, event->time))
2574 gtk_grab_add (widget);
2575 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2576 column->resized_width = column->width;
2578 /* block attached dnd signal handler */
2579 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2581 g_signal_handlers_block_matched (widget,
2582 G_SIGNAL_MATCH_DATA,
2586 tree_view->priv->drag_pos = i;
2587 tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2589 if (!gtk_widget_has_focus (widget))
2590 gtk_widget_grab_focus (widget);
2598 /* GtkWidget::button_release_event helper */
2600 pspp_sheet_view_button_release_drag_column (GtkWidget *widget,
2601 GdkEventButton *event)
2603 PsppSheetView *tree_view;
2607 tree_view = PSPP_SHEET_VIEW (widget);
2609 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2610 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2611 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2613 /* Move the button back */
2614 g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2616 g_object_ref (tree_view->priv->drag_column->button);
2617 gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2618 gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2619 gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2620 g_object_unref (tree_view->priv->drag_column->button);
2621 gtk_widget_queue_resize (widget);
2622 if (tree_view->priv->drag_column->resizable)
2624 gdk_window_raise (tree_view->priv->drag_column->window);
2625 gdk_window_show (tree_view->priv->drag_column->window);
2628 gdk_window_hide (tree_view->priv->drag_column->window);
2630 gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2634 if (tree_view->priv->cur_reorder &&
2635 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2636 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2637 tree_view->priv->cur_reorder->right_column);
2641 if (tree_view->priv->cur_reorder &&
2642 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2643 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2644 tree_view->priv->cur_reorder->left_column);
2646 tree_view->priv->drag_column = NULL;
2647 gdk_window_hide (tree_view->priv->drag_window);
2649 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2650 g_slice_free (PsppSheetViewColumnReorder, l->data);
2651 g_list_free (tree_view->priv->column_drag_info);
2652 tree_view->priv->column_drag_info = NULL;
2653 tree_view->priv->cur_reorder = NULL;
2655 if (tree_view->priv->drag_highlight_window)
2656 gdk_window_hide (tree_view->priv->drag_highlight_window);
2658 /* Reset our flags */
2659 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2660 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2665 /* GtkWidget::button_release_event helper */
2667 pspp_sheet_view_button_release_column_resize (GtkWidget *widget,
2668 GdkEventButton *event)
2670 PsppSheetView *tree_view;
2673 tree_view = PSPP_SHEET_VIEW (widget);
2675 tree_view->priv->drag_pos = -1;
2677 /* unblock attached dnd signal handler */
2678 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2680 g_signal_handlers_unblock_matched (widget,
2681 G_SIGNAL_MATCH_DATA,
2685 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2686 gtk_grab_remove (widget);
2687 gdk_display_pointer_ungrab (gdk_window_get_display (event->window),
2693 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2694 GdkEventButton *event)
2696 GtkCellEditable *cell_editable;
2701 PsppSheetViewColumn *column;
2702 GdkRectangle background_area;
2703 GdkRectangle cell_area;
2709 if (event->window != tree_view->priv->bin_window)
2712 /* Ignore a released button, if that button wasn't depressed */
2713 if (tree_view->priv->pressed_button != event->button)
2716 if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2720 /* decide if we edit */
2721 path = _pspp_sheet_view_find_path (tree_view, node);
2722 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2723 if (event->button != 1 || modifiers)
2726 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2727 pspp_sheet_view_column_cell_set_cell_data (column,
2728 tree_view->priv->model,
2731 if (!pspp_sheet_view_column_get_quick_edit (column)
2732 && _pspp_sheet_view_column_has_editable_cell (column))
2735 flags = 0; /* FIXME: get the right flags */
2736 path_string = gtk_tree_path_to_string (path);
2738 if (!_pspp_sheet_view_column_cell_event (column,
2746 if (cell_editable == NULL)
2749 pspp_sheet_view_real_set_cursor (tree_view, path,
2750 TRUE, TRUE, 0); /* XXX mode? */
2751 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2754 _pspp_sheet_view_column_get_neighbor_sizes (
2755 column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2758 area.width -= right + left;
2760 pspp_sheet_view_real_start_editing (tree_view,
2767 g_free (path_string);
2768 gtk_tree_path_free (path);
2773 pspp_sheet_view_button_release (GtkWidget *widget,
2774 GdkEventButton *event)
2776 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2778 pspp_sheet_view_stop_editing (tree_view, FALSE);
2779 if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2780 && pspp_sheet_view_button_release_edit (tree_view, event))
2782 if (tree_view->priv->pressed_button == event->button)
2783 tree_view->priv->pressed_button = -1;
2785 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2789 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2790 return pspp_sheet_view_button_release_drag_column (widget, event);
2792 if (tree_view->priv->rubber_band_status)
2793 pspp_sheet_view_stop_rubber_band (tree_view);
2795 if (tree_view->priv->pressed_button == event->button)
2796 tree_view->priv->pressed_button = -1;
2798 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2799 return pspp_sheet_view_button_release_column_resize (widget, event);
2805 pspp_sheet_view_grab_broken (GtkWidget *widget,
2806 GdkEventGrabBroken *event)
2808 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2810 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2811 pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2813 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2814 pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2819 /* GtkWidget::motion_event function set.
2823 do_prelight (PsppSheetView *tree_view,
2825 /* these are in bin_window coords */
2829 int prev_node = tree_view->priv->prelight_node;
2831 if (prev_node != node)
2833 tree_view->priv->prelight_node = node;
2836 _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2839 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2845 prelight_or_select (PsppSheetView *tree_view,
2847 /* these are in bin_window coords */
2851 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2853 if (tree_view->priv->hover_selection &&
2854 (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2855 !(tree_view->priv->edited_column &&
2856 tree_view->priv->edited_column->editable_widget))
2860 if (!pspp_sheet_view_node_is_selected (tree_view, node))
2864 path = _pspp_sheet_view_find_path (tree_view, node);
2865 pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2866 if (pspp_sheet_view_node_is_selected (tree_view, node))
2868 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2869 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE, 0); /* XXX mode? */
2871 gtk_tree_path_free (path);
2875 else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2876 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2879 do_prelight (tree_view, node, x, y);
2883 ensure_unprelighted (PsppSheetView *tree_view)
2885 do_prelight (tree_view,
2887 -1000, -1000); /* coords not possibly over an arrow */
2889 g_assert (tree_view->priv->prelight_node < 0);
2893 update_prelight (PsppSheetView *tree_view,
2900 if (tree_view->priv->row_count == 0)
2905 ensure_unprelighted (tree_view);
2909 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2913 pspp_sheet_view_find_offset (tree_view, new_y, &node);
2916 prelight_or_select (tree_view, node, x, y);
2922 /* Our motion arrow is either a box (in the case of the original spot)
2923 * or an arrow. It is expander_size wide.
2946 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2949 PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2950 GtkWidget *widget = GTK_WIDGET (tree_view);
2951 GdkBitmap *mask = NULL;
2956 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2957 GdkWindowAttr attributes;
2958 guint attributes_mask;
2961 reorder->left_column == tree_view->priv->drag_column ||
2962 reorder->right_column == tree_view->priv->drag_column)
2963 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2964 else if (reorder->left_column || reorder->right_column)
2966 GdkRectangle visible_rect;
2967 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2968 if (reorder->left_column)
2969 x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2971 x = reorder->right_column->allocation.x;
2973 if (x < visible_rect.x)
2974 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2975 else if (x > visible_rect.x + visible_rect.width)
2976 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2978 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2981 /* We want to draw the rectangle over the initial location. */
2982 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2987 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2989 if (tree_view->priv->drag_highlight_window)
2991 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2993 gdk_window_destroy (tree_view->priv->drag_highlight_window);
2996 attributes.window_type = GDK_WINDOW_CHILD;
2997 attributes.wclass = GDK_INPUT_OUTPUT;
2998 attributes.x = tree_view->priv->drag_column_x;
3000 width = attributes.width = tree_view->priv->drag_column->allocation.width;
3001 height = attributes.height = tree_view->priv->drag_column->allocation.height;
3002 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3003 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3004 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3005 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3006 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
3007 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3009 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3010 gc = gdk_gc_new (mask);
3012 gdk_gc_set_foreground (gc, &col);
3013 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3015 gdk_gc_set_foreground(gc, &col);
3016 gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
3017 g_object_unref (gc);
3019 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3021 if (mask) g_object_unref (mask);
3022 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3025 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
3031 width = tree_view->priv->expander_size;
3033 /* Get x, y, width, height of arrow */
3034 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
3035 if (reorder->left_column)
3037 x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
3038 height = reorder->left_column->allocation.height;
3042 x += reorder->right_column->allocation.x - width/2;
3043 height = reorder->right_column->allocation.height;
3045 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3046 height += tree_view->priv->expander_size;
3048 /* Create the new window */
3049 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3051 if (tree_view->priv->drag_highlight_window)
3053 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3055 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3058 attributes.window_type = GDK_WINDOW_TEMP;
3059 attributes.wclass = GDK_INPUT_OUTPUT;
3060 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3061 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3062 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3063 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3066 attributes.width = width;
3067 attributes.height = height;
3068 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3069 &attributes, attributes_mask);
3070 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3072 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3073 gc = gdk_gc_new (mask);
3075 gdk_gc_set_foreground (gc, &col);
3076 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3078 /* Draw the 2 arrows as per above */
3080 gdk_gc_set_foreground (gc, &col);
3081 for (i = 0; i < width; i ++)
3083 if (i == (width/2 - 1))
3085 gdk_draw_line (mask, gc, i, j, i, height - j);
3086 if (i < (width/2 - 1))
3091 g_object_unref (gc);
3092 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3094 if (mask) g_object_unref (mask);
3097 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3098 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3100 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3101 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3107 width = tree_view->priv->expander_size;
3109 /* Get x, y, width, height of arrow */
3110 width = width/2; /* remember, the arrow only takes half the available width */
3111 gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
3112 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3113 x += widget->allocation.width - width;
3115 if (reorder->left_column)
3116 height = reorder->left_column->allocation.height;
3118 height = reorder->right_column->allocation.height;
3120 y -= tree_view->priv->expander_size;
3121 height += 2*tree_view->priv->expander_size;
3123 /* Create the new window */
3124 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3125 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3127 if (tree_view->priv->drag_highlight_window)
3129 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3131 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3134 attributes.window_type = GDK_WINDOW_TEMP;
3135 attributes.wclass = GDK_INPUT_OUTPUT;
3136 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3137 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3138 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3139 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3142 attributes.width = width;
3143 attributes.height = height;
3144 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3145 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3147 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3148 gc = gdk_gc_new (mask);
3150 gdk_gc_set_foreground (gc, &col);
3151 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3153 /* Draw the 2 arrows as per above */
3155 gdk_gc_set_foreground (gc, &col);
3156 j = tree_view->priv->expander_size;
3157 for (i = 0; i < width; i ++)
3160 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3164 gdk_draw_line (mask, gc, k, j, k, height - j);
3165 gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3166 gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3169 g_object_unref (gc);
3170 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3172 if (mask) g_object_unref (mask);
3175 tree_view->priv->drag_column_window_state = arrow_type;
3176 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3180 g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3181 gdk_window_hide (tree_view->priv->drag_highlight_window);
3185 gdk_window_show (tree_view->priv->drag_highlight_window);
3186 gdk_window_raise (tree_view->priv->drag_highlight_window);
3191 pspp_sheet_view_motion_resize_column (GtkWidget *widget,
3192 GdkEventMotion *event)
3196 PsppSheetViewColumn *column;
3197 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3199 column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3201 if (event->is_hint || event->window != gtk_widget_get_window (widget))
3202 gtk_widget_get_pointer (widget, &x, NULL);
3206 if (tree_view->priv->hadjustment)
3207 x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
3209 new_width = pspp_sheet_view_new_column_width (tree_view,
3210 tree_view->priv->drag_pos, &x);
3211 if (x != tree_view->priv->x_drag &&
3212 (new_width != column->fixed_width))
3214 column->use_resized_width = TRUE;
3215 column->resized_width = new_width;
3218 column->resized_width -= tree_view->priv->last_extra_space_per_column;
3220 gtk_widget_queue_resize (widget);
3228 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3230 PsppSheetViewColumnReorder *reorder = NULL;
3234 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3235 for (list = tree_view->priv->column_drag_info; list; list = list->next)
3237 reorder = (PsppSheetViewColumnReorder *) list->data;
3238 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3243 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3246 tree_view->priv->cur_reorder = reorder;
3247 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3251 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3253 GdkRectangle visible_rect;
3258 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3259 y += tree_view->priv->dy;
3261 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3263 /* see if we are near the edge. */
3264 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3267 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3272 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->vadjustment) + offset, 0.0,
3273 gtk_adjustment_get_upper (tree_view->priv->vadjustment) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
3274 gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3278 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3280 GdkRectangle visible_rect;
3285 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3287 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3289 /* See if we are near the edge. */
3290 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3293 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3299 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) + offset,
3300 0.0, gtk_adjustment_get_upper (tree_view->priv->hadjustment) - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
3301 gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3308 pspp_sheet_view_motion_drag_column (GtkWidget *widget,
3309 GdkEventMotion *event)
3311 PsppSheetView *tree_view = (PsppSheetView *) widget;
3312 PsppSheetViewColumn *column = tree_view->priv->drag_column;
3314 GtkAllocation allocation;
3317 if ((column == NULL) ||
3318 (event->window != tree_view->priv->drag_window))
3321 /* Handle moving the header */
3322 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3323 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
3324 x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3325 MAX (tree_view->priv->width, allocation.width) - column->allocation.width);
3326 gdk_window_move (tree_view->priv->drag_window, x, y);
3328 /* autoscroll, if needed */
3329 pspp_sheet_view_horizontal_autoscroll (tree_view);
3330 /* Update the current reorder position and arrow; */
3331 pspp_sheet_view_update_current_reorder (tree_view);
3337 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3339 remove_scroll_timeout (tree_view);
3340 gtk_grab_remove (GTK_WIDGET (tree_view));
3342 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3344 GtkTreePath *tmp_path;
3346 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3348 /* The anchor path should be set to the start path */
3349 tmp_path = _pspp_sheet_view_find_path (tree_view,
3350 tree_view->priv->rubber_band_start_node);
3352 if (tree_view->priv->anchor)
3353 gtk_tree_row_reference_free (tree_view->priv->anchor);
3355 tree_view->priv->anchor =
3356 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3357 tree_view->priv->model,
3360 gtk_tree_path_free (tmp_path);
3362 /* ... and the cursor to the end path */
3363 tmp_path = _pspp_sheet_view_find_path (tree_view,
3364 tree_view->priv->rubber_band_end_node);
3365 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE, 0); /* XXX mode? */
3366 gtk_tree_path_free (tmp_path);
3368 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3371 /* Clear status variables */
3372 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3373 tree_view->priv->rubber_band_shift = 0;
3374 tree_view->priv->rubber_band_ctrl = 0;
3376 tree_view->priv->rubber_band_start_node = -1;
3377 tree_view->priv->rubber_band_end_node = -1;
3381 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3385 gboolean skip_start,
3388 if (start_node == end_node)
3391 /* We skip the first node and jump inside the loop */
3397 /* Small optimization by assuming insensitive nodes are never
3402 if (tree_view->priv->rubber_band_shift)
3403 pspp_sheet_view_node_select (tree_view, start_node);
3404 else if (tree_view->priv->rubber_band_ctrl)
3406 /* Toggle the selection state */
3407 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3408 pspp_sheet_view_node_unselect (tree_view, start_node);
3410 pspp_sheet_view_node_select (tree_view, start_node);
3413 pspp_sheet_view_node_select (tree_view, start_node);
3417 /* Mirror the above */
3418 if (tree_view->priv->rubber_band_shift)
3419 pspp_sheet_view_node_unselect (tree_view, start_node);
3420 else if (tree_view->priv->rubber_band_ctrl)
3422 /* Toggle the selection state */
3423 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3424 pspp_sheet_view_node_unselect (tree_view, start_node);
3426 pspp_sheet_view_node_select (tree_view, start_node);
3429 pspp_sheet_view_node_unselect (tree_view, start_node);
3432 _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3434 if (start_node == end_node)
3439 start_node = pspp_sheet_view_node_next (tree_view, start_node);
3442 /* Ran out of tree */
3445 if (skip_end && start_node == end_node)
3452 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3455 return node * tree_view->priv->fixed_height;
3459 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3463 int fixed_height = tree_view->priv->fixed_height;
3464 if (fixed_height <= 0
3466 || height >= tree_view->priv->row_count * fixed_height)
3473 *new_node = height / fixed_height;
3474 return height % fixed_height;
3479 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3484 pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3485 pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3487 /* Handle the start area first */
3488 if (tree_view->priv->rubber_band_start_node < 0)
3490 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3497 else if (start_node < tree_view->priv->rubber_band_start_node)
3499 /* New node is above the old one; selection became bigger */
3500 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3502 tree_view->priv->rubber_band_start_node,
3507 else if (start_node > tree_view->priv->rubber_band_start_node)
3509 /* New node is below the old one; selection became smaller */
3510 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3511 tree_view->priv->rubber_band_start_node,
3518 tree_view->priv->rubber_band_start_node = start_node;
3520 /* Next, handle the end area */
3521 if (tree_view->priv->rubber_band_end_node < 0)
3523 /* In the event this happens, start_node was also -1; this case is
3527 else if (end_node < 0)
3529 /* Find the last node in the tree */
3530 pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3533 /* Selection reached end of the tree */
3534 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3535 tree_view->priv->rubber_band_end_node,
3541 else if (end_node > tree_view->priv->rubber_band_end_node)
3543 /* New node is below the old one; selection became bigger */
3544 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3545 tree_view->priv->rubber_band_end_node,
3551 else if (end_node < tree_view->priv->rubber_band_end_node)
3553 /* New node is above the old one; selection became smaller */
3554 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3556 tree_view->priv->rubber_band_end_node,
3562 tree_view->priv->rubber_band_end_node = end_node;
3565 #define GDK_RECTANGLE_PTR(X) ((GdkRectangle *)(X))
3568 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3571 cairo_rectangle_int_t old_area;
3572 cairo_rectangle_int_t new_area;
3573 cairo_rectangle_int_t common;
3574 cairo_region_t *invalid_region;
3575 PsppSheetViewColumn *column;
3577 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3578 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3579 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3580 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3582 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3585 y = MAX (y, 0) + tree_view->priv->dy;
3587 new_area.x = MIN (tree_view->priv->press_start_x, x);
3588 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3589 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3590 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3592 invalid_region = cairo_region_create_rectangle (&old_area);
3593 cairo_region_union_rectangle (invalid_region, &new_area);
3595 gdk_rectangle_intersect (GDK_RECTANGLE_PTR (&old_area),
3596 GDK_RECTANGLE_PTR (&new_area), GDK_RECTANGLE_PTR (&common));
3597 if (common.width > 2 && common.height > 2)
3599 cairo_region_t *common_region;
3601 /* make sure the border is invalidated */
3607 common_region = cairo_region_create_rectangle (&common);
3609 cairo_region_subtract (invalid_region, common_region);
3610 cairo_region_destroy (common_region);
3613 #if GTK_MAJOR_VERSION == 3
3614 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
3617 cairo_rectangle_int_t extents;
3619 cairo_region_get_extents (invalid_region, &extents);
3620 ereg = gdk_region_rectangle (GDK_RECTANGLE_PTR (&extents));
3621 gdk_window_invalidate_region (tree_view->priv->bin_window, ereg, TRUE);
3622 gdk_region_destroy (ereg);
3626 cairo_region_destroy (invalid_region);
3628 tree_view->priv->rubber_band_x = x;
3629 tree_view->priv->rubber_band_y = y;
3630 pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3632 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3633 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3634 tree_view->priv->anchor_column,
3637 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3639 pspp_sheet_view_update_rubber_band_selection (tree_view);
3644 pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view,
3649 GdkRectangle rubber_rect;
3653 rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3654 rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3655 rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3656 rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3658 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3661 cr = gdk_cairo_create (tree_view->priv->bin_window);
3662 cairo_set_line_width (cr, 1.0);
3664 style = gtk_widget_get_style (GTK_WIDGET (tree_view));
3665 cairo_set_source_rgba (cr,
3666 style->fg[GTK_STATE_NORMAL].red / 65535.,
3667 style->fg[GTK_STATE_NORMAL].green / 65535.,
3668 style->fg[GTK_STATE_NORMAL].blue / 65535.,
3671 gdk_cairo_rectangle (cr, &rect);
3675 cairo_set_source_rgb (cr,
3676 style->fg[GTK_STATE_NORMAL].red / 65535.,
3677 style->fg[GTK_STATE_NORMAL].green / 65535.,
3678 style->fg[GTK_STATE_NORMAL].blue / 65535.);
3680 cairo_rectangle (cr,
3681 rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3682 rubber_rect.width - 1, rubber_rect.height - 1);
3691 pspp_sheet_view_motion_bin_window (GtkWidget *widget,
3692 GdkEventMotion *event)
3694 PsppSheetView *tree_view;
3698 tree_view = (PsppSheetView *) widget;
3700 if (tree_view->priv->row_count == 0)
3703 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3705 GdkRectangle background_area, cell_area;
3706 PsppSheetViewColumn *column;
3708 if (find_click (tree_view, event->x, event->y, &node, &column,
3709 &background_area, &cell_area)
3710 && tree_view->priv->focus_column == column
3711 && tree_view->priv->press_start_node == node)
3714 gtk_grab_add (GTK_WIDGET (tree_view));
3715 pspp_sheet_view_update_rubber_band (tree_view);
3717 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3719 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3721 pspp_sheet_view_update_rubber_band (tree_view);
3723 add_scroll_timeout (tree_view);
3726 /* only check for an initiated drag when a button is pressed */
3727 if (tree_view->priv->pressed_button >= 0
3728 && !tree_view->priv->rubber_band_status)
3729 pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3731 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3735 pspp_sheet_view_find_offset (tree_view, new_y, &node);
3737 tree_view->priv->event_last_x = event->x;
3738 tree_view->priv->event_last_y = event->y;
3740 prelight_or_select (tree_view, node, event->x, event->y);
3746 pspp_sheet_view_motion (GtkWidget *widget,
3747 GdkEventMotion *event)
3749 PsppSheetView *tree_view;
3751 tree_view = (PsppSheetView *) widget;
3753 /* Resizing a column */
3754 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3755 return pspp_sheet_view_motion_resize_column (widget, event);
3758 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3759 return pspp_sheet_view_motion_drag_column (widget, event);
3761 /* Sanity check it */
3762 if (event->window == tree_view->priv->bin_window)
3763 return pspp_sheet_view_motion_bin_window (widget, event);
3768 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3769 * the tree is empty.
3772 invalidate_empty_focus (PsppSheetView *tree_view)
3776 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3781 area.width = gdk_window_get_width (tree_view->priv->bin_window);
3782 area.height = gdk_window_get_height (tree_view->priv->bin_window);
3783 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3786 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3790 draw_empty_focus (PsppSheetView *tree_view)
3792 GtkWidget *widget = GTK_WIDGET (tree_view);
3794 cairo_t *cr = gdk_cairo_create (tree_view->priv->bin_window);
3796 if (!gtk_widget_has_focus (widget))
3799 w = gdk_window_get_width (tree_view->priv->bin_window);
3800 h = gdk_window_get_height (tree_view->priv->bin_window);
3806 gtk_paint_focus (gtk_widget_get_style (widget),
3808 gtk_widget_get_state (widget),
3816 pspp_sheet_view_draw_vertical_grid_lines (PsppSheetView *tree_view,
3818 gint n_visible_columns,
3822 GList *list = tree_view->priv->columns;
3826 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3827 && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3830 /* Only draw the lines for visible rows and columns */
3831 for (list = tree_view->priv->columns; list; list = list->next, i++)
3833 PsppSheetViewColumn *column = list->data;
3836 if (! column->visible)
3839 current_x += column->width;
3841 /* Generally the grid lines should fit within the column, but for the
3842 last visible column we put it just past the end of the column.
3843 (Otherwise horizontal grid lines sometimes stick out by one pixel.) */
3845 if (i != n_visible_columns - 1)
3848 cairo_set_line_width (cr, 1.0);
3849 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
3850 cairo_move_to (cr, x + 0.5, min_y);
3851 cairo_line_to (cr, x + 0.5, max_y - min_y);
3856 /* Warning: Very scary function.
3857 * Modify at your own risk
3859 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3860 * FIXME: It's not...
3863 pspp_sheet_view_draw_bin (GtkWidget *widget,
3866 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3871 int drag_highlight = -1;
3874 gint y_offset, cell_offset;
3876 GdkRectangle background_area;
3877 GdkRectangle cell_area;
3879 gint bin_window_width;
3880 gint bin_window_height;
3881 GtkTreePath *cursor_path;
3882 GtkTreePath *drag_dest_path;
3883 GList *first_column, *last_column;
3884 gint vertical_separator;
3885 gint horizontal_separator;
3886 gint focus_line_width;
3887 gboolean allow_rules;
3889 gint n_visible_columns;
3890 gint grid_line_width;
3891 gboolean row_ending_details;
3892 gboolean draw_vgrid_lines, draw_hgrid_lines;
3894 GtkStyleContext *context;
3895 context = gtk_widget_get_style_context (widget);
3898 GtkAllocation allocation;
3899 gtk_widget_get_allocation (widget, &allocation);
3901 GdkRectangle exposed_rect;
3902 gdk_cairo_get_clip_rectangle (cr, &exposed_rect);
3906 Zarea.height = allocation.height;
3908 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3910 gtk_widget_style_get (widget,
3911 "horizontal-separator", &horizontal_separator,
3912 "vertical-separator", &vertical_separator,
3913 "allow-rules", &allow_rules,
3914 "focus-line-width", &focus_line_width,
3915 "row-ending-details", &row_ending_details,
3918 if (tree_view->priv->row_count == 0)
3920 draw_empty_focus (tree_view);
3925 /* clip event->area to the visible area */
3926 if (Zarea.height < 0.5)
3930 validate_visible_area (tree_view);
3932 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, Zarea.y);
3936 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3938 gdk_window_get_width (tree_view->priv->bin_window);
3941 gdk_window_get_height (tree_view->priv->bin_window);
3944 if (tree_view->priv->height < bin_window_height)
3946 gtk_paint_flat_box (gtk_widget_get_style (widget),
3948 gtk_widget_get_state (widget),
3952 0, tree_view->priv->height,
3954 bin_window_height - tree_view->priv->height);
3960 /* find the path for the node */
3961 path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3962 gtk_tree_model_get_iter (tree_view->priv->model,
3965 gtk_tree_path_free (path);
3968 drag_dest_path = NULL;
3970 if (tree_view->priv->cursor)
3971 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3974 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3976 if (tree_view->priv->drag_dest_row)
3977 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3980 _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3984 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3985 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3987 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
3988 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3990 if (draw_vgrid_lines || draw_hgrid_lines)
3991 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
3993 n_visible_columns = 0;
3994 for (list = tree_view->priv->columns; list; list = list->next)
3996 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
3998 n_visible_columns ++;
4001 /* Find the last column */
4002 for (last_column = g_list_last (tree_view->priv->columns);
4003 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
4004 last_column = last_column->prev)
4008 for (first_column = g_list_first (tree_view->priv->columns);
4009 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
4010 first_column = first_column->next)
4013 /* Actually process the expose event. To do this, we want to
4014 * start at the first node of the event, and walk the tree in
4015 * order, drawing each successive node.
4022 gboolean is_first = FALSE;
4023 gboolean is_last = FALSE;
4024 gboolean done = FALSE;
4027 max_height = ROW_HEIGHT (tree_view);
4031 background_area.y = y_offset + Zarea.y;
4032 background_area.height = max_height;
4033 max_y = background_area.y + max_height;
4037 if (node == tree_view->priv->prelight_node)
4038 flags |= GTK_CELL_RENDERER_PRELIT;
4040 selected = pspp_sheet_view_node_is_selected (tree_view, node);
4044 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4046 list = (rtl ? list->prev : list->next))
4048 PsppSheetViewColumn *column = list->data;
4049 const gchar *detail = NULL;
4050 gboolean selected_column;
4053 if (!column->visible)
4056 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
4057 selected_column = column->selected && column->selectable;
4059 selected_column = TRUE;
4062 if (cell_offset > Zarea.x + Zarea.width ||
4063 cell_offset + column->width < Zarea.x)
4065 cell_offset += column->width;
4070 if (selected && selected_column)
4071 flags |= GTK_CELL_RENDERER_SELECTED;
4073 flags &= ~GTK_CELL_RENDERER_SELECTED;
4075 if (column->show_sort_indicator)
4076 flags |= GTK_CELL_RENDERER_SORTED;
4078 flags &= ~GTK_CELL_RENDERER_SORTED;
4081 flags |= GTK_CELL_RENDERER_FOCUSED;
4083 flags &= ~GTK_CELL_RENDERER_FOCUSED;
4085 background_area.x = cell_offset;
4086 background_area.width = column->width;
4088 cell_area = background_area;
4089 cell_area.y += vertical_separator / 2;
4090 cell_area.x += horizontal_separator / 2;
4091 cell_area.height -= vertical_separator;
4092 cell_area.width -= horizontal_separator;
4094 if (draw_vgrid_lines)
4096 if (list == first_column)
4098 cell_area.width -= grid_line_width / 2;
4100 else if (list == last_column)
4102 cell_area.x += grid_line_width / 2;
4103 cell_area.width -= grid_line_width / 2;
4107 cell_area.x += grid_line_width / 2;
4108 cell_area.width -= grid_line_width;
4112 if (draw_hgrid_lines)
4114 cell_area.y += grid_line_width / 2;
4115 cell_area.height -= grid_line_width;
4119 if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4121 if (!gdk_rectangle_intersect (&background_area, &exposed_rect, NULL))
4124 cell_offset += column->width;
4129 pspp_sheet_view_column_cell_set_cell_data (column,
4130 tree_view->priv->model,
4133 /* Select the detail for drawing the cell. relevant
4134 * factors are parity, sortedness, and whether to
4137 if (allow_rules && tree_view->priv->has_rules)
4139 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4140 n_visible_columns >= 3)
4143 detail = "cell_odd_ruled_sorted";
4145 detail = "cell_even_ruled_sorted";
4150 detail = "cell_odd_ruled";
4152 detail = "cell_even_ruled";
4157 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4158 n_visible_columns >= 3)
4161 detail = "cell_odd_sorted";
4163 detail = "cell_even_sorted";
4168 detail = "cell_odd";
4170 detail = "cell_even";
4176 gtk_style_context_save (context);
4177 state = gtk_cell_renderer_get_state (NULL, widget, flags);
4178 gtk_style_context_set_state (context, state);
4179 gtk_style_context_add_class (context, GTK_STYLE_CLASS_CELL);
4181 /* Draw background */
4182 gtk_render_background (context, cr,
4185 background_area.width,
4186 background_area.height);
4189 gtk_render_frame (context, cr,
4192 background_area.width,
4193 background_area.height);
4195 if (draw_hgrid_lines)
4197 cairo_set_line_width (cr, 1.0);
4198 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
4200 if (background_area.y >= 0)
4203 gdk_draw_line (event->window,
4204 tree_view->priv->grid_line_gc[widget->state],
4205 background_area.x, background_area.y,
4206 background_area.x + background_area.width,
4209 cairo_move_to (cr, background_area.x, background_area.y - 0.5);
4210 cairo_line_to (cr, background_area.x + background_area.width,
4211 background_area.y - 0.5);
4215 if (y_offset + max_height <= Zarea.height - 0.5)
4218 gdk_draw_line (event->window,
4219 tree_view->priv->grid_line_gc[widget->state],
4220 background_area.x, background_area.y + max_height,
4221 background_area.x + background_area.width,
4222 background_area.y + max_height);
4225 cairo_move_to (cr, background_area.x, background_area.y + max_height - 0.5);
4226 cairo_line_to (cr, background_area.x + background_area.width,
4227 background_area.y + max_height - 0.5);
4233 _pspp_sheet_view_column_cell_render (column,
4240 cell_offset += column->width;
4241 gtk_style_context_restore (context);
4244 if (node == drag_highlight)
4246 /* Draw indicator for the drop
4248 gint highlight_y = -1;
4252 switch (tree_view->priv->drag_dest_pos)
4254 case PSPP_SHEET_VIEW_DROP_BEFORE:
4255 highlight_y = background_area.y - 1;
4256 if (highlight_y < 0)
4260 case PSPP_SHEET_VIEW_DROP_AFTER:
4261 highlight_y = background_area.y + background_area.height - 1;
4264 case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE:
4265 case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER:
4266 _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node);
4270 width = gdk_window_get_width (tree_view->priv->bin_window);
4272 if (row_ending_details)
4273 gtk_paint_focus (gtk_widget_get_style (widget),
4275 gtk_widget_get_state (widget),
4278 ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4279 : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4280 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4281 - focus_line_width / 2,
4282 width, ROW_HEIGHT (tree_view)
4283 - focus_line_width + 1);
4285 gtk_paint_focus (gtk_widget_get_style (widget),
4287 gtk_widget_get_state (widget),
4289 "treeview-drop-indicator",
4290 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4291 - focus_line_width / 2,
4292 width, ROW_HEIGHT (tree_view)
4293 - focus_line_width + 1);
4298 if (highlight_y >= 0)
4300 gdk_draw_line (event->window,
4301 widget->style->fg_gc[gtk_widget_get_state (widget)],
4304 rtl ? 0 : bin_window_width,
4310 y_offset += max_height;
4314 node = pspp_sheet_view_node_next (tree_view, node);
4317 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
4321 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
4328 while (y_offset < Zarea.height);
4331 pspp_sheet_view_draw_vertical_grid_lines (tree_view, cr, n_visible_columns,
4335 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4337 GdkRectangle *rectangles;
4340 gdk_region_get_rectangles (event->region,
4344 while (n_rectangles--)
4345 pspp_sheet_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
4347 g_free (rectangles);
4352 gtk_tree_path_free (cursor_path);
4355 gtk_tree_path_free (drag_dest_path);
4362 pspp_sheet_view_draw (GtkWidget *widget,
4365 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4366 GtkStyleContext *context;
4368 context = gtk_widget_get_style_context (widget);
4370 if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window))
4375 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->bin_window);
4376 pspp_sheet_view_draw_bin (widget, cr);
4379 /* We can't just chain up to Container::expose as it will try to send the
4380 * event to the headers, so we handle propagating it to our children
4381 * (eg. widgets being edited) ourselves.
4383 tmp_list = tree_view->priv->children;
4386 PsppSheetViewChild *child = tmp_list->data;
4387 tmp_list = tmp_list->next;
4389 gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
4394 gtk_render_background (context, cr,
4396 gtk_widget_get_allocated_width (widget),
4397 gtk_widget_get_allocated_height (widget));
4400 gtk_style_context_save (context);
4401 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_VIEW);
4403 if (gtk_cairo_should_draw_window (cr, tree_view->priv->header_window))
4405 gint n_visible_columns;
4408 for (list = tree_view->priv->columns; list != NULL; list = list->next)
4410 PsppSheetViewColumn *column = list->data;
4412 if (column == tree_view->priv->drag_column || !column->visible)
4415 if (span_intersects (column->allocation.x, column->allocation.width,
4416 (int) gtk_adjustment_get_value (tree_view->priv->hadjustment),
4417 (int) gtk_widget_get_allocated_width (widget))
4418 && column->button != NULL)
4419 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4420 column->button, cr);
4423 n_visible_columns = 0;
4424 for (list = tree_view->priv->columns; list; list = list->next)
4426 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4428 n_visible_columns ++;
4431 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->header_window);
4432 pspp_sheet_view_draw_vertical_grid_lines (tree_view,
4436 TREE_VIEW_HEADER_HEIGHT (tree_view));
4439 if (tree_view->priv->drag_window &&
4440 gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window))
4442 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4443 tree_view->priv->drag_column->button,
4447 gtk_style_context_restore (context);
4459 /* returns 0x1 when no column has been found -- yes it's hackish */
4460 static PsppSheetViewColumn *
4461 pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
4462 PsppSheetViewColumn *column,
4465 PsppSheetViewColumn *left_column = NULL;
4466 PsppSheetViewColumn *cur_column = NULL;
4469 if (!column->reorderable)
4470 return (PsppSheetViewColumn *)0x1;
4472 switch (drop_position)
4475 /* find first column where we can drop */
4476 tmp_list = tree_view->priv->columns;
4477 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4478 return (PsppSheetViewColumn *)0x1;
4482 g_assert (tmp_list);
4484 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4485 tmp_list = tmp_list->next;
4487 if (left_column && left_column->visible == FALSE)
4490 if (!tree_view->priv->column_drop_func)
4493 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4495 left_column = cur_column;
4502 if (!tree_view->priv->column_drop_func)
4505 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4508 return (PsppSheetViewColumn *)0x1;
4512 /* find first column after column where we can drop */
4513 tmp_list = tree_view->priv->columns;
4515 for (; tmp_list; tmp_list = tmp_list->next)
4516 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4519 if (!tmp_list || !tmp_list->next)
4520 return (PsppSheetViewColumn *)0x1;
4522 tmp_list = tmp_list->next;
4523 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4524 tmp_list = tmp_list->next;
4528 g_assert (tmp_list);
4530 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4531 tmp_list = tmp_list->next;
4533 if (left_column && left_column->visible == FALSE)
4535 left_column = cur_column;
4537 tmp_list = tmp_list->next;
4541 if (!tree_view->priv->column_drop_func)
4544 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4546 left_column = cur_column;
4553 if (!tree_view->priv->column_drop_func)
4556 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4559 return (PsppSheetViewColumn *)0x1;
4563 /* find first column before column where we can drop */
4564 tmp_list = tree_view->priv->columns;
4566 for (; tmp_list; tmp_list = tmp_list->next)
4567 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4570 if (!tmp_list || !tmp_list->prev)
4571 return (PsppSheetViewColumn *)0x1;
4573 tmp_list = tmp_list->prev;
4574 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4575 tmp_list = tmp_list->prev;
4579 g_assert (tmp_list);
4581 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4583 if (left_column && !left_column->visible)
4585 /*if (!tmp_list->prev)
4586 return (PsppSheetViewColumn *)0x1;
4589 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4590 tmp_list = tmp_list->prev->prev;
4593 cur_column = left_column;
4595 tmp_list = tmp_list->prev;
4599 if (!tree_view->priv->column_drop_func)
4602 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4605 cur_column = left_column;
4606 tmp_list = tmp_list->prev;
4609 if (!tree_view->priv->column_drop_func)
4612 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4615 return (PsppSheetViewColumn *)0x1;
4619 /* same as DROP_HOME case, but doing it backwards */
4620 tmp_list = g_list_last (tree_view->priv->columns);
4623 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4624 return (PsppSheetViewColumn *)0x1;
4628 g_assert (tmp_list);
4630 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4632 if (left_column && !left_column->visible)
4634 cur_column = left_column;
4635 tmp_list = tmp_list->prev;
4638 if (!tree_view->priv->column_drop_func)
4641 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4644 cur_column = left_column;
4645 tmp_list = tmp_list->prev;
4648 if (!tree_view->priv->column_drop_func)
4651 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4654 return (PsppSheetViewColumn *)0x1;
4658 return (PsppSheetViewColumn *)0x1;
4662 pspp_sheet_view_key_press (GtkWidget *widget,
4665 PsppSheetView *tree_view = (PsppSheetView *) widget;
4667 if (tree_view->priv->rubber_band_status)
4669 if (event->keyval == GDK_Escape)
4670 pspp_sheet_view_stop_rubber_band (tree_view);
4675 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4677 if (event->keyval == GDK_Escape)
4679 tree_view->priv->cur_reorder = NULL;
4680 pspp_sheet_view_button_release_drag_column (widget, NULL);
4685 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4687 GList *focus_column;
4690 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4692 for (focus_column = tree_view->priv->columns;
4694 focus_column = focus_column->next)
4696 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4698 if (column->button && gtk_widget_has_focus (column->button))
4703 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4704 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4705 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4707 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4709 if (!column->resizable)
4711 gtk_widget_error_bell (widget);
4715 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4716 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4718 gint old_width = column->resized_width;
4720 column->resized_width = MAX (column->resized_width,
4722 column->resized_width -= 2;
4723 if (column->resized_width < 0)
4724 column->resized_width = 0;
4726 if (column->min_width == -1)
4727 column->resized_width = MAX (column->button_request,
4728 column->resized_width);
4730 column->resized_width = MAX (column->min_width,
4731 column->resized_width);
4733 if (column->max_width != -1)
4734 column->resized_width = MIN (column->resized_width,
4737 column->use_resized_width = TRUE;
4739 if (column->resized_width != old_width)
4740 gtk_widget_queue_resize (widget);
4742 gtk_widget_error_bell (widget);
4744 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4745 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4747 gint old_width = column->resized_width;
4749 column->resized_width = MAX (column->resized_width,
4751 column->resized_width += 2;
4753 if (column->max_width != -1)
4754 column->resized_width = MIN (column->resized_width,
4757 column->use_resized_width = TRUE;
4759 if (column->resized_width != old_width)
4760 gtk_widget_queue_resize (widget);
4762 gtk_widget_error_bell (widget);
4769 (event->state & GDK_MOD1_MASK) &&
4770 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4771 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4772 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4773 || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4775 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4777 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4778 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4780 PsppSheetViewColumn *col;
4781 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
4782 if (col != (PsppSheetViewColumn *)0x1)
4783 pspp_sheet_view_move_column_after (tree_view, column, col);
4785 gtk_widget_error_bell (widget);
4787 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4788 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4790 PsppSheetViewColumn *col;
4791 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4792 if (col != (PsppSheetViewColumn *)0x1)
4793 pspp_sheet_view_move_column_after (tree_view, column, col);
4795 gtk_widget_error_bell (widget);
4797 else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4799 PsppSheetViewColumn *col;
4800 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4801 if (col != (PsppSheetViewColumn *)0x1)
4802 pspp_sheet_view_move_column_after (tree_view, column, col);
4804 gtk_widget_error_bell (widget);
4806 else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4808 PsppSheetViewColumn *col;
4809 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4810 if (col != (PsppSheetViewColumn *)0x1)
4811 pspp_sheet_view_move_column_after (tree_view, column, col);
4813 gtk_widget_error_bell (widget);
4820 /* Chain up to the parent class. It handles the keybindings. */
4821 if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4824 if (tree_view->priv->search_entry_avoid_unhandled_binding)
4826 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4830 /* We pass the event to the search_entry. If its text changes, then we start
4831 * the typeahead find capabilities. */
4832 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4833 && tree_view->priv->enable_search
4834 && !tree_view->priv->search_custom_entry_set)
4836 GdkEvent *new_event;
4838 const char *new_text;
4841 gboolean text_modified;
4842 gulong popup_menu_id;
4844 pspp_sheet_view_ensure_interactive_directory (tree_view);
4846 /* Make a copy of the current text */
4847 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4848 new_event = gdk_event_copy ((GdkEvent *) event);
4849 g_object_unref (((GdkEventKey *) new_event)->window);
4850 ((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window));
4851 gtk_widget_realize (tree_view->priv->search_window);
4853 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
4854 "popup-menu", G_CALLBACK (gtk_true),
4857 /* Move the entry off screen */
4858 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4859 gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4860 gdk_screen_get_width (screen) + 1,
4861 gdk_screen_get_height (screen) + 1);
4862 gtk_widget_show (tree_view->priv->search_window);
4864 /* Send the event to the window. If the preedit_changed signal is emitted
4865 * during this event, we will set priv->imcontext_changed */
4866 tree_view->priv->imcontext_changed = FALSE;
4867 retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4868 gdk_event_free (new_event);
4869 gtk_widget_hide (tree_view->priv->search_window);
4871 g_signal_handler_disconnect (tree_view->priv->search_entry,
4874 /* We check to make sure that the entry tried to handle the text, and that
4875 * the text has changed.
4877 new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
4878 text_modified = strcmp (old_text, new_text) != 0;
4880 if (tree_view->priv->imcontext_changed || /* we're in a preedit */
4881 (retval && text_modified)) /* ...or the text was modified */
4883 if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
4885 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
4890 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
4900 pspp_sheet_view_key_release (GtkWidget *widget,
4903 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4905 if (tree_view->priv->rubber_band_status)
4908 return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
4911 /* FIXME Is this function necessary? Can I get an enter_notify event
4912 * w/o either an expose event or a mouse motion event?
4915 pspp_sheet_view_enter_notify (GtkWidget *widget,
4916 GdkEventCrossing *event)
4918 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4922 /* Sanity check it */
4923 if (event->window != tree_view->priv->bin_window)
4926 if (tree_view->priv->row_count == 0)
4929 if (event->mode == GDK_CROSSING_GRAB ||
4930 event->mode == GDK_CROSSING_GTK_GRAB ||
4931 event->mode == GDK_CROSSING_GTK_UNGRAB ||
4932 event->mode == GDK_CROSSING_STATE_CHANGED)
4935 /* find the node internally */
4936 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
4939 pspp_sheet_view_find_offset (tree_view, new_y, &node);
4941 tree_view->priv->event_last_x = event->x;
4942 tree_view->priv->event_last_y = event->y;
4944 prelight_or_select (tree_view, node, event->x, event->y);
4950 pspp_sheet_view_leave_notify (GtkWidget *widget,
4951 GdkEventCrossing *event)
4953 PsppSheetView *tree_view;
4955 if (event->mode == GDK_CROSSING_GRAB)
4958 tree_view = PSPP_SHEET_VIEW (widget);
4960 if (tree_view->priv->prelight_node >= 0)
4961 _pspp_sheet_view_queue_draw_node (tree_view,
4962 tree_view->priv->prelight_node,
4965 tree_view->priv->event_last_x = -10000;
4966 tree_view->priv->event_last_y = -10000;
4968 prelight_or_select (tree_view,
4970 -1000, -1000); /* coords not possibly over an arrow */
4977 pspp_sheet_view_focus_out (GtkWidget *widget,
4978 GdkEventFocus *event)
4980 PsppSheetView *tree_view;
4982 tree_view = PSPP_SHEET_VIEW (widget);
4984 gtk_widget_queue_draw (widget);
4986 /* destroy interactive search dialog */
4987 if (tree_view->priv->search_window)
4988 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
4994 /* Incremental Reflow
4998 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
5001 GtkAllocation allocation;
5002 gint y = pspp_sheet_view_node_find_offset (tree_view, node)
5003 - gtk_adjustment_get_value (tree_view->priv->vadjustment)
5004 + TREE_VIEW_HEADER_HEIGHT (tree_view);
5006 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5008 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
5011 tree_view->priv->fixed_height);
5015 node_is_visible (PsppSheetView *tree_view,
5021 y = pspp_sheet_view_node_find_offset (tree_view, node);
5022 height = ROW_HEIGHT (tree_view);
5024 if (y >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5025 y + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5026 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5032 /* Returns the row height. */
5034 validate_row (PsppSheetView *tree_view,
5039 PsppSheetViewColumn *column;
5040 GList *list, *first_column, *last_column;
5042 gint horizontal_separator;
5043 gint vertical_separator;
5044 gint focus_line_width;
5045 gboolean draw_vgrid_lines, draw_hgrid_lines;
5047 gint grid_line_width;
5048 gboolean wide_separators;
5049 gint separator_height;
5051 gtk_widget_style_get (GTK_WIDGET (tree_view),
5052 "focus-padding", &focus_pad,
5053 "focus-line-width", &focus_line_width,
5054 "horizontal-separator", &horizontal_separator,
5055 "vertical-separator", &vertical_separator,
5056 "grid-line-width", &grid_line_width,
5057 "wide-separators", &wide_separators,
5058 "separator-height", &separator_height,
5062 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5063 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5065 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5066 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5068 for (last_column = g_list_last (tree_view->priv->columns);
5069 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5070 last_column = last_column->prev)
5073 for (first_column = g_list_first (tree_view->priv->columns);
5074 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5075 first_column = first_column->next)
5078 for (list = tree_view->priv->columns; list; list = list->next)
5083 column = list->data;
5085 if (! column->visible)
5088 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5089 pspp_sheet_view_column_cell_get_size (column,
5091 &tmp_width, &tmp_height);
5093 tmp_height += vertical_separator;
5094 height = MAX (height, tmp_height);
5096 tmp_width = tmp_width + horizontal_separator;
5098 if (draw_vgrid_lines)
5100 if (list->data == first_column || list->data == last_column)
5101 tmp_width += grid_line_width / 2.0;
5103 tmp_width += grid_line_width;
5106 if (tmp_width > column->requested_width)
5107 column->requested_width = tmp_width;
5110 if (draw_hgrid_lines)
5111 height += grid_line_width;
5113 tree_view->priv->post_validation_flag = TRUE;
5119 validate_visible_area (PsppSheetView *tree_view)
5121 GtkTreePath *path = NULL;
5122 GtkTreePath *above_path = NULL;
5125 gboolean size_changed = FALSE;
5127 gint area_above = 0;
5128 gint area_below = 0;
5129 GtkAllocation allocation;
5131 if (tree_view->priv->row_count == 0)
5134 if (tree_view->priv->scroll_to_path == NULL)
5137 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5139 total_height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5141 if (total_height == 0)
5144 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5147 /* we are going to scroll, and will update dy */
5148 _pspp_sheet_view_find_node (tree_view, path, &node);
5149 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5151 if (tree_view->priv->scroll_to_use_align)
5153 gint height = ROW_HEIGHT (tree_view);
5154 area_above = (total_height - height) *
5155 tree_view->priv->scroll_to_row_align;
5156 area_below = total_height - area_above - height;
5157 area_above = MAX (area_above, 0);
5158 area_below = MAX (area_below, 0);
5163 * 1) row not visible
5167 gint height = ROW_HEIGHT (tree_view);
5169 dy = pspp_sheet_view_node_find_offset (tree_view, node);
5171 if (dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5172 dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5173 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5175 /* row visible: keep the row at the same position */
5176 area_above = dy - gtk_adjustment_get_value (tree_view->priv->vadjustment);
5177 area_below = (gtk_adjustment_get_value (tree_view->priv->vadjustment) +
5178 gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5183 /* row not visible */
5185 && dy + height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5187 /* row at the beginning -- fixed */
5189 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment)
5190 - area_above - height;
5192 else if (dy >= (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5193 gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5195 /* row at the end -- fixed */
5196 area_above = dy - (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5197 gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
5198 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) -
5199 area_above - height;
5203 area_above = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - height;
5209 /* row somewhere in the middle, bring it to the top
5213 area_below = total_height - height;
5219 /* the scroll to isn't valid; ignore it.
5222 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5223 tree_view->priv->scroll_to_path = NULL;
5227 above_path = gtk_tree_path_copy (path);
5229 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5230 * backwards is much slower then forward, as there is no iter_prev function.
5231 * We go forwards first in case we run out of tree. Then we go backwards to
5234 while (node >= 0 && area_below > 0)
5236 gboolean done = FALSE;
5239 node = pspp_sheet_view_node_next (tree_view, node);
5242 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5244 gtk_tree_path_next (path);
5247 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5257 area_below -= ROW_HEIGHT (tree_view);
5259 gtk_tree_path_free (path);
5261 /* If we ran out of tree, and have extra area_below left, we need to add it
5264 area_above += area_below;
5266 _pspp_sheet_view_find_node (tree_view, above_path, &node);
5268 /* We walk backwards */
5269 while (area_above > 0)
5271 node = pspp_sheet_view_node_prev (tree_view, node);
5273 /* Always find the new path in the tree. We cannot just assume
5274 * a gtk_tree_path_prev() is enough here, as there might be children
5275 * in between this node and the previous sibling node. If this
5276 * appears to be a performance hotspot in profiles, we can look into
5277 * intrigate logic for keeping path, node and iter in sync like
5278 * we do for forward walks. (Which will be hard because of the lacking
5285 gtk_tree_path_free (above_path);
5286 above_path = _pspp_sheet_view_find_path (tree_view, node);
5288 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5290 area_above -= ROW_HEIGHT (tree_view);
5293 /* set the dy here to scroll to the path,
5294 * and sync the top row accordingly
5296 pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5297 pspp_sheet_view_top_row_to_dy (tree_view);
5299 /* update width/height and queue a resize */
5302 GtkRequisition requisition;
5304 /* We temporarily guess a size, under the assumption that it will be the
5305 * same when we get our next size_allocate. If we don't do this, we'll be
5306 * in an inconsistent state if we call top_row_to_dy. */
5308 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5309 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5310 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5311 gtk_adjustment_changed (tree_view->priv->hadjustment);
5312 gtk_adjustment_changed (tree_view->priv->vadjustment);
5313 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5316 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5317 tree_view->priv->scroll_to_path = NULL;
5320 gtk_tree_path_free (above_path);
5322 if (tree_view->priv->scroll_to_column)
5324 tree_view->priv->scroll_to_column = NULL;
5326 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5330 initialize_fixed_height_mode (PsppSheetView *tree_view)
5332 if (!tree_view->priv->row_count)
5335 if (tree_view->priv->fixed_height_set)
5338 if (tree_view->priv->fixed_height < 0)
5345 path = _pspp_sheet_view_find_path (tree_view, node);
5346 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5348 tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5350 gtk_tree_path_free (path);
5352 g_object_notify (G_OBJECT (tree_view), "fixed-height");
5356 /* Our strategy for finding nodes to validate is a little convoluted. We find
5357 * the left-most uninvalidated node. We then try walking right, validating
5358 * nodes. Once we find a valid node, we repeat the previous process of finding
5359 * the first invalid node.
5363 validate_rows_handler (PsppSheetView *tree_view)
5365 initialize_fixed_height_mode (tree_view);
5366 if (tree_view->priv->validate_rows_timer)
5368 g_source_remove (tree_view->priv->validate_rows_timer);
5369 tree_view->priv->validate_rows_timer = 0;
5376 do_presize_handler (PsppSheetView *tree_view)
5378 GtkRequisition requisition;
5380 validate_visible_area (tree_view);
5381 tree_view->priv->presize_handler_timer = 0;
5383 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5386 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5388 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5389 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5390 gtk_adjustment_changed (tree_view->priv->hadjustment);
5391 gtk_adjustment_changed (tree_view->priv->vadjustment);
5392 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5398 presize_handler_callback (gpointer data)
5400 do_presize_handler (PSPP_SHEET_VIEW (data));
5406 install_presize_handler (PsppSheetView *tree_view)
5408 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5411 if (! tree_view->priv->presize_handler_timer)
5413 tree_view->priv->presize_handler_timer =
5414 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5416 if (! tree_view->priv->validate_rows_timer)
5418 tree_view->priv->validate_rows_timer =
5419 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5424 scroll_sync_handler (PsppSheetView *tree_view)
5426 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5427 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5428 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5429 pspp_sheet_view_top_row_to_dy (tree_view);
5431 pspp_sheet_view_dy_to_top_row (tree_view);
5433 tree_view->priv->scroll_sync_timer = 0;
5439 install_scroll_sync_handler (PsppSheetView *tree_view)
5441 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5444 if (!tree_view->priv->scroll_sync_timer)
5446 tree_view->priv->scroll_sync_timer =
5447 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5452 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5456 gtk_tree_row_reference_free (tree_view->priv->top_row);
5460 tree_view->priv->top_row = NULL;
5461 tree_view->priv->top_row_dy = 0;
5465 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5466 tree_view->priv->top_row_dy = offset;
5470 /* Always call this iff dy is in the visible range. If the tree is empty, then
5471 * it's set to be NULL, and top_row_dy is 0;
5474 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5480 if (tree_view->priv->row_count == 0)
5482 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5486 offset = pspp_sheet_view_find_offset (tree_view,
5487 tree_view->priv->dy,
5492 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5496 path = _pspp_sheet_view_find_path (tree_view, node);
5497 pspp_sheet_view_set_top_row (tree_view, path, offset);
5498 gtk_tree_path_free (path);
5504 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5510 /* Avoid recursive calls */
5511 if (tree_view->priv->in_top_row_to_dy)
5514 if (tree_view->priv->top_row)
5515 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5522 _pspp_sheet_view_find_node (tree_view, path, &node);
5525 gtk_tree_path_free (path);
5529 /* keep dy and set new toprow */
5530 gtk_tree_row_reference_free (tree_view->priv->top_row);
5531 tree_view->priv->top_row = NULL;
5532 tree_view->priv->top_row_dy = 0;
5533 /* DO NOT install the idle handler */
5534 pspp_sheet_view_dy_to_top_row (tree_view);
5538 if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5540 /* new top row -- do NOT install the idle handler */
5541 pspp_sheet_view_dy_to_top_row (tree_view);
5545 new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5546 new_dy += tree_view->priv->top_row_dy;
5548 if (new_dy + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
5549 new_dy = tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
5551 new_dy = MAX (0, new_dy);
5553 tree_view->priv->in_top_row_to_dy = TRUE;
5554 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5555 tree_view->priv->in_top_row_to_dy = FALSE;
5560 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5562 install_presize_handler (tree_view);
5568 set_source_row (GdkDragContext *context,
5569 GtkTreeModel *model,
5570 GtkTreePath *source_row)
5572 g_object_set_data_full (G_OBJECT (context),
5573 "gtk-tree-view-source-row",
5574 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5575 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5579 get_source_row (GdkDragContext *context)
5581 GtkTreeRowReference *ref =
5582 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5585 return gtk_tree_row_reference_get_path (ref);
5592 GtkTreeRowReference *dest_row;
5593 guint path_down_mode : 1;
5594 guint empty_view_drop : 1;
5595 guint drop_append_mode : 1;
5600 dest_row_free (gpointer data)
5602 DestRow *dr = (DestRow *)data;
5604 gtk_tree_row_reference_free (dr->dest_row);
5605 g_slice_free (DestRow, dr);
5609 set_dest_row (GdkDragContext *context,
5610 GtkTreeModel *model,
5611 GtkTreePath *dest_row,
5612 gboolean path_down_mode,
5613 gboolean empty_view_drop,
5614 gboolean drop_append_mode)
5620 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5625 dr = g_slice_new (DestRow);
5627 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5628 dr->path_down_mode = path_down_mode != FALSE;
5629 dr->empty_view_drop = empty_view_drop != FALSE;
5630 dr->drop_append_mode = drop_append_mode != FALSE;
5632 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5633 dr, (GDestroyNotify) dest_row_free);
5637 get_dest_row (GdkDragContext *context,
5638 gboolean *path_down_mode)
5641 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5645 GtkTreePath *path = NULL;
5648 *path_down_mode = dr->path_down_mode;
5651 path = gtk_tree_row_reference_get_path (dr->dest_row);
5652 else if (dr->empty_view_drop)
5653 path = gtk_tree_path_new_from_indices (0, -1);
5657 if (path && dr->drop_append_mode)
5658 gtk_tree_path_next (path);
5666 /* Get/set whether drag_motion requested the drag data and
5667 * drag_data_received should thus not actually insert the data,
5668 * since the data doesn't result from a drop.
5671 set_status_pending (GdkDragContext *context,
5672 GdkDragAction suggested_action)
5674 g_object_set_data (G_OBJECT (context),
5675 "gtk-tree-view-status-pending",
5676 GINT_TO_POINTER (suggested_action));
5679 static GdkDragAction
5680 get_status_pending (GdkDragContext *context)
5682 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5683 "gtk-tree-view-status-pending"));
5686 static TreeViewDragInfo*
5687 get_info (PsppSheetView *tree_view)
5689 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5693 destroy_info (TreeViewDragInfo *di)
5695 g_slice_free (TreeViewDragInfo, di);
5698 static TreeViewDragInfo*
5699 ensure_info (PsppSheetView *tree_view)
5701 TreeViewDragInfo *di;
5703 di = get_info (tree_view);
5707 di = g_slice_new0 (TreeViewDragInfo);
5709 g_object_set_data_full (G_OBJECT (tree_view),
5710 "gtk-tree-view-drag-info",
5712 (GDestroyNotify) destroy_info);
5719 remove_info (PsppSheetView *tree_view)
5721 g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5726 drag_scan_timeout (gpointer data)
5728 PsppSheetView *tree_view;
5730 GdkModifierType state;
5731 GtkTreePath *path = NULL;
5732 PsppSheetViewColumn *column = NULL;
5733 GdkRectangle visible_rect;
5735 GDK_THREADS_ENTER ();
5737 tree_view = PSPP_SHEET_VIEW (data);
5739 gdk_window_get_pointer (tree_view->priv->bin_window,
5742 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5744 /* See if we are near the edge. */
5745 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5746 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5747 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5748 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5750 pspp_sheet_view_get_path_at_pos (tree_view,
5751 tree_view->priv->bin_window,
5760 pspp_sheet_view_scroll_to_cell (tree_view,
5766 gtk_tree_path_free (path);
5770 GDK_THREADS_LEAVE ();
5777 add_scroll_timeout (PsppSheetView *tree_view)
5779 if (tree_view->priv->scroll_timeout == 0)
5781 tree_view->priv->scroll_timeout =
5782 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5787 remove_scroll_timeout (PsppSheetView *tree_view)
5789 if (tree_view->priv->scroll_timeout != 0)
5791 g_source_remove (tree_view->priv->scroll_timeout);
5792 tree_view->priv->scroll_timeout = 0;
5797 check_model_dnd (GtkTreeModel *model,
5798 GType required_iface,
5799 const gchar *signal)
5801 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5803 g_warning ("You must override the default '%s' handler "
5804 "on PsppSheetView when using models that don't support "
5805 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5806 "is to connect to '%s' and call "
5807 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5808 "the default handler from running. Look at the source code "
5809 "for the default handler in gtktreeview.c to get an idea what "
5810 "your handler should do. (gtktreeview.c is in the GTK source "
5811 "code.) If you're using GTK from a language other than C, "
5812 "there may be a more natural way to override default handlers, e.g. via derivation.",
5813 signal, g_type_name (required_iface), signal);
5821 scroll_row_timeout (gpointer data)
5823 PsppSheetView *tree_view = data;
5825 pspp_sheet_view_horizontal_autoscroll (tree_view);
5826 pspp_sheet_view_vertical_autoscroll (tree_view);
5828 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5829 pspp_sheet_view_update_rubber_band (tree_view);
5834 /* Returns TRUE if event should not be propagated to parent widgets */
5836 set_destination_row (PsppSheetView *tree_view,
5837 GdkDragContext *context,
5838 /* coordinates relative to the widget */
5841 GdkDragAction *suggested_action,
5844 GtkTreePath *path = NULL;
5845 PsppSheetViewDropPosition pos;
5846 PsppSheetViewDropPosition old_pos;
5847 TreeViewDragInfo *di;
5849 GtkTreePath *old_dest_path = NULL;
5850 gboolean can_drop = FALSE;
5852 *suggested_action = 0;
5855 widget = GTK_WIDGET (tree_view);
5857 di = get_info (tree_view);
5859 if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5861 /* someone unset us as a drag dest, note that if
5862 * we return FALSE drag_leave isn't called
5865 pspp_sheet_view_set_drag_dest_row (tree_view,
5867 PSPP_SHEET_VIEW_DROP_BEFORE);
5869 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5871 return FALSE; /* no longer a drop site */
5874 *target = gtk_drag_dest_find_target (widget, context,
5875 gtk_drag_dest_get_target_list (widget));
5876 if (*target == GDK_NONE)
5881 if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
5887 GtkTreeModel *model;
5889 /* the row got dropped on empty space, let's setup a special case
5893 gtk_tree_path_free (path);
5895 model = pspp_sheet_view_get_model (tree_view);
5897 n_children = gtk_tree_model_iter_n_children (model, NULL);
5900 pos = PSPP_SHEET_VIEW_DROP_AFTER;
5901 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
5905 pos = PSPP_SHEET_VIEW_DROP_BEFORE;
5906 path = gtk_tree_path_new_from_indices (0, -1);
5916 /* If we left the current row's "open" zone, unset the timeout for
5919 pspp_sheet_view_get_drag_dest_row (tree_view,
5924 gtk_tree_path_free (old_dest_path);
5926 if (TRUE /* FIXME if the location droppable predicate */)
5934 GtkWidget *source_widget;
5936 *suggested_action = gdk_drag_context_get_suggested_action (context);
5937 source_widget = gtk_drag_get_source_widget (context);
5939 if (source_widget == widget)
5941 /* Default to MOVE, unless the user has
5942 * pressed ctrl or shift to affect available actions
5944 if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
5945 *suggested_action = GDK_ACTION_MOVE;
5948 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5953 /* can't drop here */
5954 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5956 PSPP_SHEET_VIEW_DROP_BEFORE);
5960 gtk_tree_path_free (path);
5966 get_logical_dest_row (PsppSheetView *tree_view,
5967 gboolean *path_down_mode,
5968 gboolean *drop_append_mode)
5970 /* adjust path to point to the row the drop goes in front of */
5971 GtkTreePath *path = NULL;
5972 PsppSheetViewDropPosition pos;
5974 g_return_val_if_fail (path_down_mode != NULL, NULL);
5975 g_return_val_if_fail (drop_append_mode != NULL, NULL);
5977 *path_down_mode = FALSE;
5978 *drop_append_mode = 0;
5980 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
5985 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
5987 else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
5988 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
5989 *path_down_mode = TRUE;
5993 GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
5995 g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
5997 if (!gtk_tree_model_get_iter (model, &iter, path) ||
5998 !gtk_tree_model_iter_next (model, &iter))
5999 *drop_append_mode = 1;
6002 *drop_append_mode = 0;
6003 gtk_tree_path_next (path);
6011 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
6012 GdkEventMotion *event)
6014 GtkWidget *widget = GTK_WIDGET (tree_view);
6015 GdkDragContext *context;
6016 TreeViewDragInfo *di;
6017 GtkTreePath *path = NULL;
6019 gint cell_x, cell_y;
6020 GtkTreeModel *model;
6021 gboolean retval = FALSE;
6023 di = get_info (tree_view);
6025 if (di == NULL || !di->source_set)
6028 if (tree_view->priv->pressed_button < 0)
6031 if (!gtk_drag_check_threshold (widget,
6032 tree_view->priv->press_start_x,
6033 tree_view->priv->press_start_y,
6034 event->x, event->y))
6037 model = pspp_sheet_view_get_model (tree_view);
6042 button = tree_view->priv->pressed_button;
6043 tree_view->priv->pressed_button = -1;
6045 pspp_sheet_view_get_path_at_pos (tree_view,
6046 tree_view->priv->press_start_x,
6047 tree_view->priv->press_start_y,
6056 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6057 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6061 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6064 /* Now we can begin the drag */
6068 context = gtk_drag_begin (widget,
6069 gtk_drag_source_get_target_list (widget),
6074 set_source_row (context, model, path);
6078 gtk_tree_path_free (path);
6086 pspp_sheet_view_drag_begin (GtkWidget *widget,
6087 GdkDragContext *context)
6090 PsppSheetView *tree_view;
6091 GtkTreePath *path = NULL;
6092 gint cell_x, cell_y;
6094 TreeViewDragInfo *di;
6096 tree_view = PSPP_SHEET_VIEW (widget);
6098 /* if the user uses a custom DND source impl, we don't set the icon here */
6099 di = get_info (tree_view);
6101 if (di == NULL || !di->source_set)
6104 pspp_sheet_view_get_path_at_pos (tree_view,
6105 tree_view->priv->press_start_x,
6106 tree_view->priv->press_start_y,
6112 g_return_if_fail (path != NULL);
6114 row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6117 gtk_drag_set_icon_pixmap (context,
6118 gdk_drawable_get_colormap (row_pix),
6121 /* the + 1 is for the black border in the icon */
6122 tree_view->priv->press_start_x + 1,
6125 g_object_unref (row_pix);
6126 gtk_tree_path_free (path);
6132 pspp_sheet_view_drag_end (GtkWidget *widget,
6133 GdkDragContext *context)
6138 /* Default signal implementations for the drag signals */
6140 pspp_sheet_view_drag_data_get (GtkWidget *widget,
6141 GdkDragContext *context,
6142 GtkSelectionData *selection_data,
6146 PsppSheetView *tree_view;
6147 GtkTreeModel *model;
6148 TreeViewDragInfo *di;
6149 GtkTreePath *source_row;
6151 tree_view = PSPP_SHEET_VIEW (widget);
6153 model = pspp_sheet_view_get_model (tree_view);
6158 di = get_info (PSPP_SHEET_VIEW (widget));
6163 source_row = get_source_row (context);
6165 if (source_row == NULL)
6168 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6169 * any model; for DragSource models there are some other targets
6173 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6174 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6179 /* If drag_data_get does nothing, try providing row data. */
6180 if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6182 gtk_tree_set_row_drag_data (selection_data,
6188 gtk_tree_path_free (source_row);
6193 pspp_sheet_view_drag_data_delete (GtkWidget *widget,
6194 GdkDragContext *context)
6196 TreeViewDragInfo *di;
6197 GtkTreeModel *model;
6198 PsppSheetView *tree_view;
6199 GtkTreePath *source_row;
6201 tree_view = PSPP_SHEET_VIEW (widget);
6202 model = pspp_sheet_view_get_model (tree_view);
6204 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6207 di = get_info (tree_view);
6212 source_row = get_source_row (context);
6214 if (source_row == NULL)
6217 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6220 gtk_tree_path_free (source_row);
6222 set_source_row (context, NULL, NULL);
6226 pspp_sheet_view_drag_leave (GtkWidget *widget,
6227 GdkDragContext *context,
6230 /* unset any highlight row */
6231 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6233 PSPP_SHEET_VIEW_DROP_BEFORE);
6235 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6240 pspp_sheet_view_drag_motion (GtkWidget *widget,
6241 GdkDragContext *context,
6242 /* coordinates relative to the widget */
6248 GtkTreePath *path = NULL;
6249 PsppSheetViewDropPosition pos;
6250 PsppSheetView *tree_view;
6251 GdkDragAction suggested_action = 0;
6254 tree_view = PSPP_SHEET_VIEW (widget);
6256 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6259 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6261 /* we only know this *after* set_desination_row */
6262 empty = tree_view->priv->empty_view_drop;
6264 if (path == NULL && !empty)
6266 /* Can't drop here. */
6267 gdk_drag_status (context, 0, time);
6271 if (tree_view->priv->open_dest_timeout == 0 &&
6272 (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6273 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6279 add_scroll_timeout (tree_view);
6282 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6284 /* Request data so we can use the source row when
6285 * determining whether to accept the drop
6287 set_status_pending (context, suggested_action);
6288 gtk_drag_get_data (widget, context, target, time);
6292 set_status_pending (context, 0);
6293 gdk_drag_status (context, suggested_action, time);
6298 gtk_tree_path_free (path);
6305 pspp_sheet_view_drag_drop (GtkWidget *widget,
6306 GdkDragContext *context,
6307 /* coordinates relative to the widget */
6312 PsppSheetView *tree_view;
6314 GdkDragAction suggested_action = 0;
6315 GdkAtom target = GDK_NONE;
6316 TreeViewDragInfo *di;
6317 GtkTreeModel *model;
6318 gboolean path_down_mode;
6319 gboolean drop_append_mode;
6321 tree_view = PSPP_SHEET_VIEW (widget);
6323 model = pspp_sheet_view_get_model (tree_view);
6325 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6327 di = get_info (tree_view);
6332 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6335 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6338 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6340 if (target != GDK_NONE && path != NULL)
6342 /* in case a motion had requested drag data, change things so we
6343 * treat drag data receives as a drop.
6345 set_status_pending (context, 0);
6346 set_dest_row (context, model, path,
6347 path_down_mode, tree_view->priv->empty_view_drop,
6352 gtk_tree_path_free (path);
6354 /* Unset this thing */
6355 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6357 PSPP_SHEET_VIEW_DROP_BEFORE);
6359 if (target != GDK_NONE)
6361 gtk_drag_get_data (widget, context, target, time);
6369 pspp_sheet_view_drag_data_received (GtkWidget *widget,
6370 GdkDragContext *context,
6371 /* coordinates relative to the widget */
6374 GtkSelectionData *selection_data,
6379 TreeViewDragInfo *di;
6380 gboolean accepted = FALSE;
6381 GtkTreeModel *model;
6382 PsppSheetView *tree_view;
6383 GtkTreePath *dest_row;
6384 GdkDragAction suggested_action;
6385 gboolean path_down_mode;
6386 gboolean drop_append_mode;
6388 tree_view = PSPP_SHEET_VIEW (widget);
6390 model = pspp_sheet_view_get_model (tree_view);
6392 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6395 di = get_info (tree_view);
6400 suggested_action = get_status_pending (context);
6402 if (suggested_action)
6404 /* We are getting this data due to a request in drag_motion,
6405 * rather than due to a request in drag_drop, so we are just
6406 * supposed to call drag_status, not actually paste in the
6409 path = get_logical_dest_row (tree_view, &path_down_mode,
6413 suggested_action = 0;
6414 else if (path_down_mode)
6415 gtk_tree_path_down (path);
6417 if (suggested_action)
6419 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6425 path_down_mode = FALSE;
6426 gtk_tree_path_up (path);
6428 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6431 suggested_action = 0;
6434 suggested_action = 0;
6438 gdk_drag_status (context, suggested_action, time);
6441 gtk_tree_path_free (path);
6443 /* If you can't drop, remove user drop indicator until the next motion */
6444 if (suggested_action == 0)
6445 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6447 PSPP_SHEET_VIEW_DROP_BEFORE);
6452 dest_row = get_dest_row (context, &path_down_mode);
6454 if (dest_row == NULL)
6457 if (gtk_selection_data_get_length (selection_data) >= 0)
6461 gtk_tree_path_down (dest_row);
6462 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6463 dest_row, selection_data))
6464 gtk_tree_path_up (dest_row);
6468 if (gtk_selection_data_get_length (selection_data) >= 0)
6470 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6476 gtk_drag_finish (context,
6478 (gdk_drag_context_get_actions (context) == GDK_ACTION_MOVE),
6481 if (gtk_tree_path_get_depth (dest_row) == 1
6482 && gtk_tree_path_get_indices (dest_row)[0] == 0)
6484 /* special special case drag to "0", scroll to first item */
6485 if (!tree_view->priv->scroll_to_path)
6486 pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6489 gtk_tree_path_free (dest_row);
6492 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6497 /* GtkContainer Methods
6502 pspp_sheet_view_remove (GtkContainer *container,
6505 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6506 PsppSheetViewChild *child = NULL;
6509 tmp_list = tree_view->priv->children;
6512 child = tmp_list->data;
6513 if (child->widget == widget)
6515 gtk_widget_unparent (widget);
6517 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6518 g_list_free_1 (tmp_list);
6519 g_slice_free (PsppSheetViewChild, child);
6523 tmp_list = tmp_list->next;
6526 tmp_list = tree_view->priv->columns;
6530 PsppSheetViewColumn *column;
6531 column = tmp_list->data;
6532 if (column->button == widget)
6534 gtk_widget_unparent (widget);
6537 tmp_list = tmp_list->next;
6542 pspp_sheet_view_forall (GtkContainer *container,
6543 gboolean include_internals,
6544 GtkCallback callback,
6545 gpointer callback_data)
6547 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6548 PsppSheetViewChild *child = NULL;
6549 PsppSheetViewColumn *column;
6552 tmp_list = tree_view->priv->children;
6555 child = tmp_list->data;
6556 tmp_list = tmp_list->next;
6558 (* callback) (child->widget, callback_data);
6560 if (include_internals == FALSE)
6563 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6565 column = tmp_list->data;
6568 (* callback) (column->button, callback_data);
6572 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6573 * cells. If so we draw one big row-spanning focus rectangle.
6576 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6580 if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6581 return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6583 for (list = tree_view->priv->columns; list; list = list->next)
6585 if (!((PsppSheetViewColumn *)list->data)->visible)
6587 if (_pspp_sheet_view_column_count_special_cells (list->data))
6595 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6596 PsppSheetViewColumn *focus_column,
6597 gboolean clamp_column_visible)
6599 g_return_if_fail (focus_column != NULL);
6601 tree_view->priv->focus_column = focus_column;
6603 if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view)) != focus_column->button)
6604 gtk_widget_grab_focus (focus_column->button);
6606 if (clamp_column_visible)
6607 pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6610 /* Returns TRUE if the focus is within the headers, after the focus operation is
6614 pspp_sheet_view_header_focus (PsppSheetView *tree_view,
6615 GtkDirectionType dir,
6616 gboolean clamp_column_visible)
6618 GtkWidget *focus_child;
6619 PsppSheetViewColumn *focus_column;
6620 GList *last_column, *first_column;
6624 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6627 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
6629 first_column = tree_view->priv->columns;
6630 while (first_column)
6632 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6634 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6636 first_column = first_column->next;
6639 /* No headers are visible, or are focusable. We can't focus in or out.
6641 if (first_column == NULL)
6644 last_column = g_list_last (tree_view->priv->columns);
6647 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6649 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6651 last_column = last_column->prev;
6655 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6659 case GTK_DIR_TAB_BACKWARD:
6660 case GTK_DIR_TAB_FORWARD:
6663 if (focus_child == NULL)
6665 if (tree_view->priv->focus_column != NULL &&
6666 pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6667 focus_column = tree_view->priv->focus_column;
6669 focus_column = first_column->data;
6670 pspp_sheet_view_focus_column (tree_view, focus_column,
6671 clamp_column_visible);
6678 if (focus_child == NULL)
6680 if (tree_view->priv->focus_column != NULL)
6681 focus_column = tree_view->priv->focus_column;
6682 else if (dir == GTK_DIR_LEFT)
6683 focus_column = last_column->data;
6685 focus_column = first_column->data;
6686 pspp_sheet_view_focus_column (tree_view, focus_column,
6687 clamp_column_visible);
6691 if (gtk_widget_child_focus (focus_child, dir))
6693 /* The focus moves inside the button. */
6694 /* This is probably a great example of bad UI */
6695 if (clamp_column_visible)
6696 pspp_sheet_view_clamp_column_visible (tree_view,
6697 tree_view->priv->focus_column,
6702 /* We need to move the focus among the row of buttons. */
6703 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6704 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6707 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6708 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6710 gtk_widget_error_bell (GTK_WIDGET (tree_view));
6716 PsppSheetViewColumn *column;
6718 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6719 tmp_list = tmp_list->next;
6721 tmp_list = tmp_list->prev;
6723 if (tmp_list == NULL)
6725 g_warning ("Internal button not found");
6728 column = tmp_list->data;
6729 if (column->visible &&
6730 pspp_sheet_view_column_can_focus (column))
6734 pspp_sheet_view_focus_column (tree_view, column,
6735 clamp_column_visible);
6743 g_assert_not_reached ();
6750 /* This function returns in 'path' the first focusable path, if the given path
6751 * is already focusable, it's the returned one.
6755 search_first_focusable_path (PsppSheetView *tree_view,
6757 gboolean search_forward,
6760 /* XXX this function is trivial given that the sheetview doesn't support
6764 if (!path || !*path)
6767 _pspp_sheet_view_find_node (tree_view, *path, &node);
6775 return (*path != NULL);
6779 pspp_sheet_view_focus (GtkWidget *widget,
6780 GtkDirectionType direction)
6782 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6783 GtkContainer *container = GTK_CONTAINER (widget);
6784 GtkWidget *focus_child;
6786 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6789 focus_child = gtk_container_get_focus_child (container);
6791 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6792 /* Case 1. Headers currently have focus. */
6799 pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6801 case GTK_DIR_TAB_BACKWARD:
6804 case GTK_DIR_TAB_FORWARD:
6806 gtk_widget_grab_focus (widget);
6809 g_assert_not_reached ();
6814 /* Case 2. We don't have focus at all. */
6815 if (!gtk_widget_has_focus (widget))
6817 if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6818 gtk_widget_grab_focus (widget);
6822 /* Case 3. We have focus already. */
6823 if (direction == GTK_DIR_TAB_BACKWARD)
6824 return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6825 else if (direction == GTK_DIR_TAB_FORWARD)
6828 /* Other directions caught by the keybindings */
6829 gtk_widget_grab_focus (widget);
6834 pspp_sheet_view_grab_focus (GtkWidget *widget)
6836 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6838 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6842 pspp_sheet_view_style_updated (GtkWidget *widget)
6844 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6846 PsppSheetViewColumn *column;
6847 GtkStyleContext *context;
6849 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->style_updated (widget);
6851 if (gtk_widget_get_realized (widget))
6853 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
6854 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
6855 gtk_style_context_set_background (context, tree_view->priv->header_window);
6856 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6859 gtk_widget_style_get (widget,
6860 "expander-size", &tree_view->priv->expander_size,
6862 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6864 for (list = tree_view->priv->columns; list; list = list->next)
6866 column = list->data;
6867 _pspp_sheet_view_column_cell_set_dirty (column);
6870 tree_view->priv->fixed_height = -1;
6872 /* Invalidate cached button style. */
6873 if (tree_view->priv->button_style)
6875 g_object_unref (tree_view->priv->button_style);
6876 tree_view->priv->button_style = NULL;
6879 gtk_widget_queue_resize (widget);
6884 pspp_sheet_view_set_focus_child (GtkContainer *container,
6887 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6890 for (list = tree_view->priv->columns; list; list = list->next)
6892 if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
6894 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
6899 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
6903 pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
6904 GtkAdjustment *hadj,
6905 GtkAdjustment *vadj)
6907 gboolean need_adjust = FALSE;
6909 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
6912 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
6914 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6916 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
6918 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6920 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
6922 g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
6923 pspp_sheet_view_adjustment_changed,
6925 g_object_unref (tree_view->priv->hadjustment);
6928 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
6930 g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
6931 pspp_sheet_view_adjustment_changed,
6933 g_object_unref (tree_view->priv->vadjustment);
6936 if (tree_view->priv->hadjustment != hadj)
6938 tree_view->priv->hadjustment = hadj;
6939 g_object_ref_sink (tree_view->priv->hadjustment);
6941 g_signal_connect (tree_view->priv->hadjustment, "value-changed",
6942 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6947 if (tree_view->priv->vadjustment != vadj)
6949 tree_view->priv->vadjustment = vadj;
6950 g_object_ref_sink (tree_view->priv->vadjustment);
6952 g_signal_connect (tree_view->priv->vadjustment, "value-changed",
6953 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6959 pspp_sheet_view_adjustment_changed (NULL, tree_view);
6964 pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
6965 GtkMovementStep step,
6968 PsppSheetSelectMode mode;
6969 GdkModifierType state;
6971 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
6972 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
6973 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
6974 step == GTK_MOVEMENT_DISPLAY_LINES ||
6975 step == GTK_MOVEMENT_PAGES ||
6976 step == GTK_MOVEMENT_BUFFER_ENDS ||
6977 step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
6979 if (tree_view->priv->row_count == 0)
6981 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
6984 pspp_sheet_view_stop_editing (tree_view, FALSE);
6985 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
6986 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
6989 if (gtk_get_current_event_state (&state))
6991 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
6992 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
6993 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
6994 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
6996 /* else we assume not pressed */
7000 case GTK_MOVEMENT_LOGICAL_POSITIONS:
7001 pspp_sheet_view_move_cursor_tab (tree_view, count);
7003 case GTK_MOVEMENT_VISUAL_POSITIONS:
7004 pspp_sheet_view_move_cursor_left_right (tree_view, count, mode);
7006 case GTK_MOVEMENT_DISPLAY_LINES:
7007 pspp_sheet_view_move_cursor_up_down (tree_view, count, mode);
7009 case GTK_MOVEMENT_PAGES:
7010 pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode);
7012 case GTK_MOVEMENT_BUFFER_ENDS:
7013 pspp_sheet_view_move_cursor_start_end (tree_view, count, mode);
7015 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7016 pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode);
7019 g_assert_not_reached ();
7026 pspp_sheet_view_put (PsppSheetView *tree_view,
7027 GtkWidget *child_widget,
7029 PsppSheetViewColumn *column)
7031 PsppSheetViewChild *child;
7033 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7034 g_return_if_fail (GTK_IS_WIDGET (child_widget));
7036 child = g_slice_new (PsppSheetViewChild);
7038 child->widget = child_widget;
7039 _pspp_sheet_view_find_node (tree_view, path, &child->node);
7040 if (child->node < 0)
7042 g_assert_not_reached ();
7044 child->column = column;
7046 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7048 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7049 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7051 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7054 /* TreeModel Callbacks
7058 pspp_sheet_view_row_changed (GtkTreeModel *model,
7063 PsppSheetView *tree_view = (PsppSheetView *)data;
7065 gboolean free_path = FALSE;
7066 GtkTreePath *cursor_path;
7068 g_return_if_fail (path != NULL || iter != NULL);
7070 if (tree_view->priv->cursor != NULL)
7071 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7075 if (tree_view->priv->edited_column &&
7076 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7077 pspp_sheet_view_stop_editing (tree_view, TRUE);
7079 if (cursor_path != NULL)
7080 gtk_tree_path_free (cursor_path);
7084 path = gtk_tree_model_get_path (model, iter);
7087 else if (iter == NULL)
7088 gtk_tree_model_get_iter (model, iter, path);
7090 _pspp_sheet_view_find_node (tree_view,
7096 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7097 pspp_sheet_view_node_queue_redraw (tree_view, node);
7101 gtk_tree_path_free (path);
7105 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7110 PsppSheetView *tree_view = (PsppSheetView *) data;
7113 gint height = tree_view->priv->fixed_height;
7114 gboolean free_path = FALSE;
7115 gboolean node_visible = TRUE;
7117 g_return_if_fail (path != NULL || iter != NULL);
7121 path = gtk_tree_model_get_path (model, iter);
7124 else if (iter == NULL)
7125 gtk_tree_model_get_iter (model, iter, path);
7127 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7129 /* Update all row-references */
7130 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7131 indices = gtk_tree_path_get_indices (path);
7132 tmpnode = indices[0];
7134 range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7138 if (node_visible && node_is_visible (tree_view, tmpnode))
7139 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7141 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7144 install_presize_handler (tree_view);
7146 gtk_tree_path_free (path);
7150 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7154 PsppSheetView *tree_view = (PsppSheetView *)data;
7157 g_return_if_fail (path != NULL);
7159 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7161 _pspp_sheet_view_find_node (tree_view, path, &node);
7166 range_tower_delete (tree_view->priv->selected, node, 1);
7168 /* Ensure we don't have a dangling pointer to a dead node */
7169 ensure_unprelighted (tree_view);
7171 /* Cancel editting if we've started */
7172 pspp_sheet_view_stop_editing (tree_view, TRUE);
7174 if (tree_view->priv->destroy_count_func)
7176 gint child_count = 0;
7177 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7180 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7182 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7184 gtk_tree_row_reference_free (tree_view->priv->top_row);
7185 tree_view->priv->top_row = NULL;
7188 install_scroll_sync_handler (tree_view);
7190 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7193 if (helper_data.changed)
7194 g_signal_emit_by_name (tree_view->priv->selection, "changed");
7199 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7200 GtkTreePath *parent,
7205 PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7208 /* XXX need to adjust selection */
7209 len = gtk_tree_model_iter_n_children (model, iter);
7214 gtk_tree_row_reference_reordered (G_OBJECT (data),
7219 if (gtk_tree_path_get_depth (parent) != 0)
7222 if (tree_view->priv->edited_column)
7223 pspp_sheet_view_stop_editing (tree_view, TRUE);
7225 /* we need to be unprelighted */
7226 ensure_unprelighted (tree_view);
7228 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7230 pspp_sheet_view_dy_to_top_row (tree_view);
7234 /* Internal tree functions
7239 pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view,
7240 PsppSheetViewColumn *column,
7244 PsppSheetViewColumn *tmp_column = NULL;
7255 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7258 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7260 list = (rtl ? list->prev : list->next))
7262 tmp_column = list->data;
7264 if (tmp_column == column)
7267 if (tmp_column->visible)
7268 total_width += tmp_column->width;
7271 if (tmp_column != column)
7273 g_warning (G_STRLOC": passed-in column isn't in the tree");
7282 if (column->visible)
7283 *x2 = total_width + column->width;
7285 *x2 = total_width; /* width of 0 */
7289 /* Make sure the node is visible vertically */
7291 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7294 gint node_dy, height;
7295 GtkTreePath *path = NULL;
7297 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7300 /* just return if the node is visible, avoiding a costly expose */
7301 node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7302 height = ROW_HEIGHT (tree_view);
7303 if (node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment)
7304 && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
7305 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
7308 path = _pspp_sheet_view_find_path (tree_view, node);
7311 /* We process updates because we want to clear old selected items when we scroll.
7312 * if this is removed, we get a "selection streak" at the bottom. */
7313 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7314 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7315 gtk_tree_path_free (path);
7320 pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
7321 PsppSheetViewColumn *column,
7322 gboolean focus_to_cell)
7329 x = column->allocation.x;
7330 width = column->allocation.width;
7332 if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7334 /* The column is larger than the horizontal page size. If the
7335 * column has cells which can be focussed individually, then we make
7336 * sure the cell which gets focus is fully visible (if even the
7337 * focus cell is bigger than the page size, we make sure the
7338 * left-hand side of the cell is visible).
7340 * If the column does not have those so-called special cells, we
7341 * make sure the left-hand side of the column is visible.
7344 if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7346 GtkTreePath *cursor_path;
7347 GdkRectangle background_area, cell_area, focus_area;
7349 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7351 pspp_sheet_view_get_cell_area (tree_view,
7352 cursor_path, column, &cell_area);
7353 pspp_sheet_view_get_background_area (tree_view,
7354 cursor_path, column,
7357 gtk_tree_path_free (cursor_path);
7359 _pspp_sheet_view_column_get_focus_area (column,
7365 width = focus_area.width;
7367 if (width < gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7369 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7370 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7371 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7372 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7373 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7377 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7379 gtk_adjustment_get_lower (tree_view->priv->hadjustment),
7380 gtk_adjustment_get_upper (tree_view->priv->hadjustment)
7381 - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)));
7385 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7386 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7387 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7388 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7389 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7394 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7399 path = gtk_tree_path_new ();
7401 gtk_tree_path_append_index (path, node);
7406 _pspp_sheet_view_find_node (PsppSheetView *tree_view,
7410 gint *indices = gtk_tree_path_get_indices (path);
7411 gint depth = gtk_tree_path_get_depth (path);
7414 if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7420 pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
7423 gboolean add_shifted_binding,
7424 GtkMovementStep step,
7428 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7433 if (add_shifted_binding)
7434 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7439 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7442 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7447 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7454 pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view,
7455 PsppSheetViewColumn *column)
7457 PsppSheetViewColumn *left_column;
7458 PsppSheetViewColumn *cur_column = NULL;
7459 PsppSheetViewColumnReorder *reorder;
7464 /* We want to precalculate the motion list such that we know what column slots
7468 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7470 /* First, identify all possible drop spots */
7472 tmp_list = g_list_last (tree_view->priv->columns);
7474 tmp_list = g_list_first (tree_view->priv->columns);
7478 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7479 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7481 if (cur_column->visible == FALSE)
7484 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7485 if (left_column != column && cur_column != column &&
7486 tree_view->priv->column_drop_func &&
7487 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7489 left_column = cur_column;
7492 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7493 reorder->left_column = left_column;
7494 left_column = reorder->right_column = cur_column;
7496 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7499 /* Add the last one */
7500 if (tree_view->priv->column_drop_func == NULL ||
7501 ((left_column != column) &&
7502 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7504 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7505 reorder->left_column = left_column;
7506 reorder->right_column = NULL;
7507 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7510 /* We quickly check to see if it even makes sense to reorder columns. */
7511 /* If there is nothing that can be moved, then we return */
7513 if (tree_view->priv->column_drag_info == NULL)
7516 /* We know there are always 2 slots possbile, as you can always return column. */
7517 /* If that's all there is, return */
7518 if (tree_view->priv->column_drag_info->next == NULL ||
7519 (tree_view->priv->column_drag_info->next->next == NULL &&
7520 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7521 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7523 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7524 g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7525 g_list_free (tree_view->priv->column_drag_info);
7526 tree_view->priv->column_drag_info = NULL;
7529 /* We fill in the ranges for the columns, now that we've isolated them */
7530 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7532 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7534 reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7536 reorder->left_align = left;
7537 if (tmp_list->next != NULL)
7539 g_assert (tmp_list->next->data);
7540 left = reorder->right_align = (reorder->right_column->allocation.x +
7541 reorder->right_column->allocation.width +
7542 ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7546 gint width = gdk_window_get_width (tree_view->priv->header_window);
7547 reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7553 _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view,
7554 PsppSheetViewColumn *column)
7556 GdkEvent *send_event;
7557 GtkAllocation allocation;
7559 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7560 GdkDisplay *display = gdk_screen_get_display (screen);
7562 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7563 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7564 g_return_if_fail (column->button);
7566 pspp_sheet_view_set_column_drag_info (tree_view, column);
7568 if (tree_view->priv->column_drag_info == NULL)
7571 if (tree_view->priv->drag_window == NULL)
7573 GdkWindowAttr attributes;
7574 guint attributes_mask;
7576 attributes.window_type = GDK_WINDOW_CHILD;
7577 attributes.wclass = GDK_INPUT_OUTPUT;
7578 attributes.x = column->allocation.x;
7580 attributes.width = column->allocation.width;
7581 attributes.height = column->allocation.height;
7582 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7583 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7584 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL ;
7586 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7589 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7592 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7593 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7595 gtk_grab_remove (column->button);
7597 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7598 send_event->crossing.send_event = TRUE;
7599 send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button)));
7600 send_event->crossing.subwindow = NULL;
7601 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7602 send_event->crossing.time = GDK_CURRENT_TIME;
7604 gtk_propagate_event (column->button, send_event);
7605 gdk_event_free (send_event);
7607 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7608 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7609 send_event->button.send_event = TRUE;
7610 send_event->button.time = GDK_CURRENT_TIME;
7611 send_event->button.x = -1;
7612 send_event->button.y = -1;
7613 send_event->button.axes = NULL;
7614 send_event->button.state = 0;
7615 send_event->button.button = 1;
7616 send_event->button.device =
7617 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
7619 send_event->button.x_root = 0;
7620 send_event->button.y_root = 0;
7622 gtk_propagate_event (column->button, send_event);
7623 gdk_event_free (send_event);
7625 /* Kids, don't try this at home */
7626 g_object_ref (column->button);
7627 gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7628 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7629 gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7630 g_object_unref (column->button);
7632 tree_view->priv->drag_column_x = column->allocation.x;
7633 allocation = column->allocation;
7635 gtk_widget_size_allocate (column->button, &allocation);
7636 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7638 tree_view->priv->drag_column = column;
7639 gdk_window_show (tree_view->priv->drag_window);
7641 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7643 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7644 while (gtk_events_pending ())
7645 gtk_main_iteration ();
7647 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7648 gdk_pointer_grab (tree_view->priv->drag_window,
7650 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7651 NULL, NULL, GDK_CURRENT_TIME);
7652 gdk_keyboard_grab (tree_view->priv->drag_window,
7658 _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view,
7660 const GdkRectangle *clip_rect)
7663 GtkAllocation allocation;
7665 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7668 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
7670 rect.width = MAX (tree_view->priv->width, allocation.width);
7672 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7673 rect.height = ROW_HEIGHT (tree_view);
7677 GdkRectangle new_rect;
7679 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7681 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7685 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7690 pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
7692 const GdkRectangle *clip_rect)
7696 _pspp_sheet_view_find_node (tree_view, path, &node);
7699 _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7703 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7706 GtkTreePath *cursor_path;
7708 if ((tree_view->priv->row_count == 0) ||
7709 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7713 if (tree_view->priv->cursor)
7714 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7716 if (cursor_path == NULL)
7718 /* There's no cursor. Move the cursor to the first selected row, if any
7719 * are selected, otherwise to the first row in the sheetview.
7721 GList *selected_rows;
7722 GtkTreeModel *model;
7723 PsppSheetSelection *selection;
7725 selection = pspp_sheet_view_get_selection (tree_view);
7726 selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7730 /* XXX we could avoid doing O(n) work to get this result */
7731 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7732 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7733 g_list_free (selected_rows);
7737 cursor_path = gtk_tree_path_new_first ();
7738 search_first_focusable_path (tree_view, &cursor_path,
7742 gtk_tree_row_reference_free (tree_view->priv->cursor);
7743 tree_view->priv->cursor = NULL;
7747 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7748 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7749 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0);
7751 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0);
7757 /* Now find a column for the cursor. */
7758 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7760 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7761 gtk_tree_path_free (cursor_path);
7763 if (tree_view->priv->focus_column == NULL)
7766 for (list = tree_view->priv->columns; list; list = list->next)
7768 if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7770 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7771 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7772 pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7782 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7784 PsppSheetSelectMode mode)
7786 gint selection_count;
7787 int cursor_node = -1;
7788 int new_cursor_node = -1;
7789 GtkTreePath *cursor_path = NULL;
7790 gboolean grab_focus = TRUE;
7792 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7796 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7797 /* FIXME: we lost the cursor; should we get the first? */
7800 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7801 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7803 if (cursor_node < 0)
7804 /* FIXME: we lost the cursor; should we get the first? */
7807 selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7809 if (selection_count == 0
7810 && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7811 && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
7813 /* Don't move the cursor, but just select the current node */
7814 new_cursor_node = cursor_node;
7819 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7821 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7824 gtk_tree_path_free (cursor_path);
7826 if (new_cursor_node)
7828 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7830 search_first_focusable_path (tree_view, &cursor_path,
7835 gtk_tree_path_free (cursor_path);
7839 * If the list has only one item and multi-selection is set then select
7840 * the row (if not yet selected).
7842 if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7843 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7844 new_cursor_node < 0)
7847 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7849 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7851 if (new_cursor_node < 0
7852 && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
7854 new_cursor_node = cursor_node;
7858 new_cursor_node = -1;
7862 if (new_cursor_node >= 0)
7864 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7865 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode);
7866 gtk_tree_path_free (cursor_path);
7870 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7872 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
7874 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
7876 GTK_DIR_UP : GTK_DIR_DOWN))
7878 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
7881 gtk_widget_child_focus (toplevel,
7883 GTK_DIR_TAB_BACKWARD :
7884 GTK_DIR_TAB_FORWARD);
7891 gtk_widget_error_bell (GTK_WIDGET (tree_view));
7896 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7898 return new_cursor_node >= 0;
7902 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
7904 PsppSheetSelectMode mode)
7906 int cursor_node = -1;
7907 GtkTreePath *old_cursor_path = NULL;
7908 GtkTreePath *cursor_path = NULL;
7909 int start_cursor_node = -1;
7912 gint vertical_separator;
7914 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7917 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
7918 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7920 /* This is sorta weird. Focus in should give us a cursor */
7923 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
7924 _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
7926 if (cursor_node < 0)
7928 /* FIXME: we lost the cursor. Should we try to get one? */
7929 gtk_tree_path_free (old_cursor_path);
7933 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7934 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
7935 y += tree_view->priv->cursor_offset;
7936 y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment);
7937 y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment), (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator);
7939 if (y >= tree_view->priv->height)
7940 y = tree_view->priv->height - 1;
7942 tree_view->priv->cursor_offset =
7943 pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
7945 if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
7947 cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7948 tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
7951 y -= tree_view->priv->cursor_offset;
7952 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7954 start_cursor_node = cursor_node;
7956 if (! search_first_focusable_path (tree_view, &cursor_path,
7960 /* It looks like we reached the end of the view without finding
7961 * a focusable row. We will step backwards to find the last
7964 cursor_node = start_cursor_node;
7965 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7967 search_first_focusable_path (tree_view, &cursor_path,
7976 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7978 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode);
7981 pspp_sheet_view_scroll_to_point (tree_view, -1, y);
7982 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7983 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
7985 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
7986 gtk_widget_error_bell (GTK_WIDGET (tree_view));
7988 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7991 gtk_tree_path_free (old_cursor_path);
7992 gtk_tree_path_free (cursor_path);
7996 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
7998 PsppSheetSelectMode mode)
8000 int cursor_node = -1;
8001 GtkTreePath *cursor_path = NULL;
8002 PsppSheetViewColumn *column;
8005 gboolean found_column = FALSE;
8008 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8010 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8013 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8014 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8018 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8019 if (cursor_node < 0)
8021 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8023 gtk_tree_path_free (cursor_path);
8026 gtk_tree_path_free (cursor_path);
8028 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8029 if (tree_view->priv->focus_column)
8031 for (; list; list = (rtl ? list->prev : list->next))
8033 if (list->data == tree_view->priv->focus_column)
8040 gboolean left, right;
8042 column = list->data;
8043 if (column->visible == FALSE || column->row_head)
8046 pspp_sheet_view_column_cell_set_cell_data (column,
8047 tree_view->priv->model,
8052 right = list->prev ? TRUE : FALSE;
8053 left = list->next ? TRUE : FALSE;
8057 left = list->prev ? TRUE : FALSE;
8058 right = list->next ? TRUE : FALSE;
8061 if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8063 tree_view->priv->focus_column = column;
8064 found_column = TRUE;
8069 list = rtl ? list->prev : list->next;
8071 list = rtl ? list->next : list->prev;
8076 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8077 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8078 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8082 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8085 pspp_sheet_view_clamp_column_visible (tree_view,
8086 tree_view->priv->focus_column, TRUE);
8090 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8092 PsppSheetSelectMode mode)
8094 int cursor_node = -1;
8095 GtkTreePath *cursor_path = NULL;
8096 PsppSheetViewColumn *column;
8097 PsppSheetViewColumn *found_column;
8102 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8104 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8107 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8108 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8112 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8113 if (cursor_node < 0)
8115 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8117 gtk_tree_path_free (cursor_path);
8120 gtk_tree_path_free (cursor_path);
8122 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8123 if (tree_view->priv->focus_column)
8125 for (; list; list = (rtl ? list->prev : list->next))
8127 if (list->data == tree_view->priv->focus_column)
8132 found_column = NULL;
8135 gboolean left, right;
8137 column = list->data;
8138 if (column->visible == FALSE || column->row_head)
8141 pspp_sheet_view_column_cell_set_cell_data (column,
8142 tree_view->priv->model,
8147 right = list->prev ? TRUE : FALSE;
8148 left = list->next ? TRUE : FALSE;
8152 left = list->prev ? TRUE : FALSE;
8153 right = list->next ? TRUE : FALSE;
8156 if (column->tabbable
8157 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8158 found_column = column;
8162 list = rtl ? list->prev : list->next;
8164 list = rtl ? list->next : list->prev;
8169 tree_view->priv->focus_column = found_column;
8170 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8171 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8172 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8175 pspp_sheet_view_clamp_column_visible (tree_view,
8176 tree_view->priv->focus_column, TRUE);
8180 try_move_cursor_tab (PsppSheetView *tree_view,
8181 gboolean start_at_focus_column,
8184 PsppSheetViewColumn *column;
8186 int cursor_node = -1;
8187 GtkTreePath *cursor_path = NULL;
8191 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8192 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8196 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8197 if (cursor_node < 0)
8199 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8201 gtk_tree_path_free (cursor_path);
8204 gtk_tree_path_free (cursor_path);
8206 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8207 if (start_at_focus_column)
8210 ? g_list_last (tree_view->priv->columns)
8211 : g_list_first (tree_view->priv->columns));
8212 if (tree_view->priv->focus_column)
8214 for (; list; list = (rtl ? list->prev : list->next))
8216 if (list->data == tree_view->priv->focus_column)
8223 list = (rtl ^ (count == 1)
8224 ? g_list_first (tree_view->priv->columns)
8225 : g_list_last (tree_view->priv->columns));
8230 gboolean left, right;
8232 column = list->data;
8233 if (column->visible == FALSE || !column->tabbable)
8236 pspp_sheet_view_column_cell_set_cell_data (column,
8237 tree_view->priv->model,
8242 right = list->prev ? TRUE : FALSE;
8243 left = list->next ? TRUE : FALSE;
8247 left = list->prev ? TRUE : FALSE;
8248 right = list->next ? TRUE : FALSE;
8251 if (column->tabbable
8252 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8254 tree_view->priv->focus_column = column;
8255 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8256 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8257 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8262 list = rtl ? list->prev : list->next;
8264 list = rtl ? list->next : list->prev;
8271 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8274 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8277 if (!try_move_cursor_tab (tree_view, TRUE, count))
8279 if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0)
8280 && !try_move_cursor_tab (tree_view, FALSE, count))
8281 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8284 pspp_sheet_view_clamp_column_visible (tree_view,
8285 tree_view->priv->focus_column, TRUE);
8289 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8291 PsppSheetSelectMode mode)
8295 GtkTreePath *old_path;
8297 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8300 g_return_if_fail (tree_view->priv->row_count > 0);
8302 pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8306 /* Now go forward to find the first focusable row. */
8307 path = _pspp_sheet_view_find_path (tree_view, 0);
8308 search_first_focusable_path (tree_view, &path,
8309 TRUE, &cursor_node);
8313 /* Now go backwards to find last focusable row. */
8314 path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8315 search_first_focusable_path (tree_view, &path,
8316 FALSE, &cursor_node);
8322 if (gtk_tree_path_compare (old_path, path))
8324 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
8325 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8329 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8333 gtk_tree_path_free (old_path);
8334 gtk_tree_path_free (path);
8338 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8340 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8343 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8344 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8347 pspp_sheet_selection_select_all (tree_view->priv->selection);
8353 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8355 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8358 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8359 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8362 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8368 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8369 gboolean start_editing,
8370 PsppSheetSelectMode mode)
8373 int cursor_node = -1;
8374 GtkTreePath *cursor_path = NULL;
8376 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8379 if (tree_view->priv->cursor)
8380 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8382 if (cursor_path == NULL)
8385 _pspp_sheet_view_find_node (tree_view, cursor_path,
8388 if (cursor_node < 0)
8390 gtk_tree_path_free (cursor_path);
8394 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing &&
8395 tree_view->priv->focus_column)
8397 if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8399 gtk_tree_path_free (cursor_path);
8404 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8410 /* We bail out if the original (tree, node) don't exist anymore after
8411 * handling the selection-changed callback. We do return TRUE because
8412 * the key press has been handled at this point.
8414 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8416 if (cursor_node != new_node)
8419 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8421 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8422 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8424 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8425 pspp_sheet_view_row_activated (tree_view, cursor_path,
8426 tree_view->priv->focus_column);
8428 gtk_tree_path_free (cursor_path);
8434 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8437 int cursor_node = -1;
8438 GtkTreePath *cursor_path = NULL;
8440 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8444 if (tree_view->priv->cursor)
8445 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8447 if (cursor_path == NULL)
8450 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8451 if (cursor_node < 0)
8453 gtk_tree_path_free (cursor_path);
8457 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8460 PSPP_SHEET_SELECT_MODE_TOGGLE,
8463 /* We bail out if the original (tree, node) don't exist anymore after
8464 * handling the selection-changed callback. We do return TRUE because
8465 * the key press has been handled at this point.
8467 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8469 if (cursor_node != new_node)
8472 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8474 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8475 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8476 gtk_tree_path_free (cursor_path);
8482 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8484 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8485 tree_view->priv->typeselect_flush_timeout = 0;
8490 /* Cut and paste from gtkwindow.c */
8492 send_focus_change (GtkWidget *widget,
8495 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8497 fevent->focus_change.type = GDK_FOCUS_CHANGE;
8498 fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
8499 fevent->focus_change.in = in;
8501 gtk_widget_send_focus_change (widget, fevent);
8502 gdk_event_free (fevent);
8506 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8508 GtkWidget *frame, *vbox, *toplevel;
8511 if (tree_view->priv->search_custom_entry_set)
8514 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8515 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8517 if (tree_view->priv->search_window != NULL)
8519 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8520 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8521 GTK_WINDOW (tree_view->priv->search_window));
8522 else if (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)))
8523 gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
8524 GTK_WINDOW (tree_view->priv->search_window));
8525 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8529 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8530 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8532 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8533 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8534 GTK_WINDOW (tree_view->priv->search_window));
8536 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8537 GDK_WINDOW_TYPE_HINT_UTILITY);
8538 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8539 g_signal_connect (tree_view->priv->search_window, "delete-event",
8540 G_CALLBACK (pspp_sheet_view_search_delete_event),
8542 g_signal_connect (tree_view->priv->search_window, "key-press-event",
8543 G_CALLBACK (pspp_sheet_view_search_key_press_event),
8545 g_signal_connect (tree_view->priv->search_window, "button-press-event",
8546 G_CALLBACK (pspp_sheet_view_search_button_press_event),
8548 g_signal_connect (tree_view->priv->search_window, "scroll-event",
8549 G_CALLBACK (pspp_sheet_view_search_scroll_event),
8552 frame = gtk_frame_new (NULL);
8553 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8554 gtk_widget_show (frame);
8555 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8557 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
8558 gtk_widget_show (vbox);
8559 gtk_container_add (GTK_CONTAINER (frame), vbox);
8560 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8563 tree_view->priv->search_entry = gtk_entry_new ();
8564 gtk_widget_show (tree_view->priv->search_entry);
8565 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8566 G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8568 g_signal_connect (tree_view->priv->search_entry,
8569 "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8573 g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8575 G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8579 gtk_container_add (GTK_CONTAINER (vbox),
8580 tree_view->priv->search_entry);
8582 gtk_widget_realize (tree_view->priv->search_entry);
8585 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8586 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8589 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8590 gboolean keybinding)
8592 /* We only start interactive search if we have focus or the columns
8593 * have focus. If one of our children have focus, we don't want to
8597 gboolean found_focus = FALSE;
8598 GtkWidgetClass *entry_parent_class;
8600 if (!tree_view->priv->enable_search && !keybinding)
8603 if (tree_view->priv->search_custom_entry_set)
8606 if (tree_view->priv->search_window != NULL &&
8607 gtk_widget_get_visible (tree_view->priv->search_window))
8610 for (list = tree_view->priv->columns; list; list = list->next)
8612 PsppSheetViewColumn *column;
8614 column = list->data;
8615 if (! column->visible)
8618 if (column->button && gtk_widget_has_focus (column->button))
8625 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8631 if (tree_view->priv->search_column < 0)
8634 pspp_sheet_view_ensure_interactive_directory (tree_view);
8637 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8640 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8641 gtk_widget_show (tree_view->priv->search_window);
8642 if (tree_view->priv->search_entry_changed_id == 0)
8644 tree_view->priv->search_entry_changed_id =
8645 g_signal_connect (tree_view->priv->search_entry, "changed",
8646 G_CALLBACK (pspp_sheet_view_search_init),
8650 tree_view->priv->typeselect_flush_timeout =
8651 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8652 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8655 /* Grab focus will select all the text. We don't want that to happen, so we
8656 * call the parent instance and bypass the selection change. This is probably
8657 * really non-kosher. */
8658 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8659 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8661 /* send focus-in event */
8662 send_focus_change (tree_view->priv->search_entry, TRUE);
8664 /* search first matching iter */
8665 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8671 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8673 return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8676 /* this function returns the new width of the column being resized given
8677 * the column and x position of the cursor; the x cursor position is passed
8678 * in as a pointer and automagicly corrected if it's beyond min/max limits
8681 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8685 PsppSheetViewColumn *column;
8689 /* first translate the x position from gtk_widget_get_window (widget)
8690 * to clist->clist_window
8692 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8693 column = g_list_nth (tree_view->priv->columns, i)->data;
8694 width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8696 /* Clamp down the value */
8697 if (column->min_width == -1)
8698 width = MAX (column->button_request, width);
8700 width = MAX (column->min_width, width);
8701 if (column->max_width != -1)
8702 width = MIN (width, column->max_width);
8704 *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8710 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8714 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8715 PsppSheetView *tree_view)
8717 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8721 gdk_window_move (tree_view->priv->bin_window,
8722 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8723 TREE_VIEW_HEADER_HEIGHT (tree_view));
8724 gdk_window_move (tree_view->priv->header_window,
8725 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8727 dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8729 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8733 /* update our dy and top_row */
8734 tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8736 update_prelight (tree_view,
8737 tree_view->priv->event_last_x,
8738 tree_view->priv->event_last_y);
8740 if (!tree_view->priv->in_top_row_to_dy)
8741 pspp_sheet_view_dy_to_top_row (tree_view);
8744 update_childrens_allocation(tree_view);
8752 * pspp_sheet_view_new:
8754 * Creates a new #PsppSheetView widget.
8756 * Return value: A newly created #PsppSheetView widget.
8759 pspp_sheet_view_new (void)
8761 return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
8765 * pspp_sheet_view_new_with_model:
8766 * @model: the model.
8768 * Creates a new #PsppSheetView widget with the model initialized to @model.
8770 * Return value: A newly created #PsppSheetView widget.
8773 pspp_sheet_view_new_with_model (GtkTreeModel *model)
8775 return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
8782 * pspp_sheet_view_get_model:
8783 * @tree_view: a #PsppSheetView
8785 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
8788 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
8791 pspp_sheet_view_get_model (PsppSheetView *tree_view)
8793 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8795 return tree_view->priv->model;
8799 * pspp_sheet_view_set_model:
8800 * @tree_view: A #GtkTreeNode.
8801 * @model: (allow-none): The model.
8803 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
8804 * set, it will remove it before setting the new model. If @model is %NULL,
8805 * then it will unset the old model.
8808 pspp_sheet_view_set_model (PsppSheetView *tree_view,
8809 GtkTreeModel *model)
8811 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8812 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
8814 if (model == tree_view->priv->model)
8817 if (tree_view->priv->scroll_to_path)
8819 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8820 tree_view->priv->scroll_to_path = NULL;
8823 if (tree_view->priv->model)
8825 GList *tmplist = tree_view->priv->columns;
8827 if (tree_view->priv->selected)
8828 range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
8829 pspp_sheet_view_stop_editing (tree_view, TRUE);
8831 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8832 pspp_sheet_view_row_changed,
8834 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8835 pspp_sheet_view_row_inserted,
8837 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8838 pspp_sheet_view_row_deleted,
8840 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8841 pspp_sheet_view_rows_reordered,
8844 for (; tmplist; tmplist = tmplist->next)
8845 _pspp_sheet_view_column_unset_model (tmplist->data,
8846 tree_view->priv->model);
8848 tree_view->priv->prelight_node = -1;
8850 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
8851 tree_view->priv->drag_dest_row = NULL;
8852 gtk_tree_row_reference_free (tree_view->priv->cursor);
8853 tree_view->priv->cursor = NULL;
8854 gtk_tree_row_reference_free (tree_view->priv->anchor);
8855 tree_view->priv->anchor = NULL;
8856 gtk_tree_row_reference_free (tree_view->priv->top_row);
8857 tree_view->priv->top_row = NULL;
8858 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8859 tree_view->priv->scroll_to_path = NULL;
8861 tree_view->priv->scroll_to_column = NULL;
8863 g_object_unref (tree_view->priv->model);
8865 tree_view->priv->search_column = -1;
8866 tree_view->priv->fixed_height = -1;
8867 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
8868 tree_view->priv->last_button_x = -1;
8869 tree_view->priv->last_button_y = -1;
8872 tree_view->priv->model = model;
8874 if (tree_view->priv->model)
8878 if (tree_view->priv->search_column == -1)
8880 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
8882 GType type = gtk_tree_model_get_column_type (model, i);
8884 if (g_value_type_transformable (type, G_TYPE_STRING))
8886 tree_view->priv->search_column = i;
8892 g_object_ref (tree_view->priv->model);
8893 g_signal_connect (tree_view->priv->model,
8895 G_CALLBACK (pspp_sheet_view_row_changed),
8897 g_signal_connect (tree_view->priv->model,
8899 G_CALLBACK (pspp_sheet_view_row_inserted),
8901 g_signal_connect (tree_view->priv->model,
8903 G_CALLBACK (pspp_sheet_view_row_deleted),
8905 g_signal_connect (tree_view->priv->model,
8907 G_CALLBACK (pspp_sheet_view_rows_reordered),
8910 tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
8912 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
8913 install_presize_handler (tree_view);
8916 g_object_notify (G_OBJECT (tree_view), "model");
8918 if (tree_view->priv->selection)
8919 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
8921 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8922 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8926 * pspp_sheet_view_get_selection:
8927 * @tree_view: A #PsppSheetView.
8929 * Gets the #PsppSheetSelection associated with @tree_view.
8931 * Return value: A #PsppSheetSelection object.
8933 PsppSheetSelection *
8934 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
8936 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8938 return tree_view->priv->selection;
8942 * pspp_sheet_view_get_hadjustment:
8943 * @tree_view: A #PsppSheetView
8945 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
8947 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
8951 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
8953 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8955 return pspp_sheet_view_do_get_hadjustment (tree_view);
8958 static GtkAdjustment *
8959 pspp_sheet_view_do_get_hadjustment (PsppSheetView *tree_view)
8961 return tree_view->priv->hadjustment;
8965 * pspp_sheet_view_set_hadjustment:
8966 * @tree_view: A #PsppSheetView
8967 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
8969 * Sets the #GtkAdjustment for the current horizontal aspect.
8972 pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view,
8973 GtkAdjustment *adjustment)
8975 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8977 pspp_sheet_view_set_adjustments (tree_view,
8979 tree_view->priv->vadjustment);
8981 g_object_notify (G_OBJECT (tree_view), "hadjustment");
8985 pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
8986 GtkAdjustment *adjustment)
8988 PsppSheetViewPrivate *priv = tree_view->priv;
8990 if (adjustment && priv->hadjustment == adjustment)
8993 if (priv->hadjustment != NULL)
8995 g_signal_handlers_disconnect_by_func (priv->hadjustment,
8996 pspp_sheet_view_adjustment_changed,
8998 g_object_unref (priv->hadjustment);
9001 if (adjustment == NULL)
9002 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9005 g_signal_connect (adjustment, "value-changed",
9006 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9007 priv->hadjustment = g_object_ref_sink (adjustment);
9008 /* FIXME: Adjustment should probably be populated here with fresh values, but
9009 * internal details are too complicated for me to decipher right now.
9011 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9013 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9017 * pspp_sheet_view_get_vadjustment:
9018 * @tree_view: A #PsppSheetView
9020 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9022 * Return value: (transfer none): A #GtkAdjustment object, or %NULL
9023 * if none is currently being used.
9025 * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
9028 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9030 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9032 return pspp_sheet_view_do_get_vadjustment (tree_view);
9035 static GtkAdjustment *
9036 pspp_sheet_view_do_get_vadjustment (PsppSheetView *tree_view)
9038 return tree_view->priv->vadjustment;
9042 * pspp_sheet_view_set_vadjustment:
9043 * @tree_view: A #PsppSheetView
9044 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9046 * Sets the #GtkAdjustment for the current vertical aspect.
9048 * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
9051 pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view,
9052 GtkAdjustment *adjustment)
9054 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9055 g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
9057 pspp_sheet_view_do_set_vadjustment (tree_view, adjustment);
9061 pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
9062 GtkAdjustment *adjustment)
9064 PsppSheetViewPrivate *priv = tree_view->priv;
9066 if (adjustment && priv->vadjustment == adjustment)
9069 if (priv->vadjustment != NULL)
9071 g_signal_handlers_disconnect_by_func (priv->vadjustment,
9072 pspp_sheet_view_adjustment_changed,
9074 g_object_unref (priv->vadjustment);
9077 if (adjustment == NULL)
9078 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9081 g_signal_connect (adjustment, "value-changed",
9082 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9083 priv->vadjustment = g_object_ref_sink (adjustment);
9084 /* FIXME: Adjustment should probably be populated here with fresh values, but
9085 * internal details are too complicated for me to decipher right now.
9087 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9088 g_object_notify (G_OBJECT (tree_view), "vadjustment");
9091 /* Column and header operations */
9094 * pspp_sheet_view_get_headers_visible:
9095 * @tree_view: A #PsppSheetView.
9097 * Returns %TRUE if the headers on the @tree_view are visible.
9099 * Return value: Whether the headers are visible or not.
9102 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9104 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9106 return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9110 * pspp_sheet_view_set_headers_visible:
9111 * @tree_view: A #PsppSheetView.
9112 * @headers_visible: %TRUE if the headers are visible
9114 * Sets the visibility state of the headers.
9117 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9118 gboolean headers_visible)
9122 PsppSheetViewColumn *column;
9123 GtkAllocation allocation;
9125 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9127 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9129 headers_visible = !! headers_visible;
9131 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9134 if (headers_visible)
9135 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9137 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9139 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9141 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9142 if (headers_visible)
9144 gdk_window_move_resize (tree_view->priv->bin_window, x, y + TREE_VIEW_HEADER_HEIGHT (tree_view),
9145 tree_view->priv->width, allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view));
9147 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9148 pspp_sheet_view_map_buttons (tree_view);
9152 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9154 for (list = tree_view->priv->columns; list; list = list->next)
9156 column = list->data;
9158 gtk_widget_unmap (column->button);
9160 gdk_window_hide (tree_view->priv->header_window);
9164 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view));
9165 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2);
9166 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
9167 gtk_adjustment_set_upper (tree_view->priv->vadjustment, tree_view->priv->height);
9168 gtk_adjustment_changed (tree_view->priv->vadjustment);
9170 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9172 g_object_notify (G_OBJECT (tree_view), "headers-visible");
9176 * pspp_sheet_view_columns_autosize:
9177 * @tree_view: A #PsppSheetView.
9179 * Resizes all columns to their optimal width. Only works after the
9180 * treeview has been realized.
9183 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9185 gboolean dirty = FALSE;
9187 PsppSheetViewColumn *column;
9189 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9191 for (list = tree_view->priv->columns; list; list = list->next)
9193 column = list->data;
9194 _pspp_sheet_view_column_cell_set_dirty (column);
9199 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9203 * pspp_sheet_view_set_headers_clickable:
9204 * @tree_view: A #PsppSheetView.
9205 * @setting: %TRUE if the columns are clickable.
9207 * Allow the column title buttons to be clicked.
9210 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9215 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9217 for (list = tree_view->priv->columns; list; list = list->next)
9218 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9220 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9225 * pspp_sheet_view_get_headers_clickable:
9226 * @tree_view: A #PsppSheetView.
9228 * Returns whether all header columns are clickable.
9230 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9235 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9239 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9241 for (list = tree_view->priv->columns; list; list = list->next)
9242 if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9249 * pspp_sheet_view_set_rules_hint
9250 * @tree_view: a #PsppSheetView
9251 * @setting: %TRUE if the tree requires reading across rows
9253 * This function tells GTK+ that the user interface for your
9254 * application requires users to read across tree rows and associate
9255 * cells with one another. By default, GTK+ will then render the tree
9256 * with alternating row colors. Do <emphasis>not</emphasis> use it
9257 * just because you prefer the appearance of the ruled tree; that's a
9258 * question for the theme. Some themes will draw tree rows in
9259 * alternating colors even when rules are turned off, and users who
9260 * prefer that appearance all the time can choose those themes. You
9261 * should call this function only as a <emphasis>semantic</emphasis>
9262 * hint to the theme engine that your tree makes alternating colors
9263 * useful from a functional standpoint (since it has lots of columns,
9268 pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view,
9271 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9273 setting = setting != FALSE;
9275 if (tree_view->priv->has_rules != setting)
9277 tree_view->priv->has_rules = setting;
9278 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9281 g_object_notify (G_OBJECT (tree_view), "rules-hint");
9285 * pspp_sheet_view_get_rules_hint
9286 * @tree_view: a #PsppSheetView
9288 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9290 * Return value: %TRUE if rules are useful for the user of this tree
9293 pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view)
9295 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9297 return tree_view->priv->has_rules;
9300 /* Public Column functions
9304 * pspp_sheet_view_append_column:
9305 * @tree_view: A #PsppSheetView.
9306 * @column: The #PsppSheetViewColumn to add.
9308 * Appends @column to the list of columns.
9310 * Return value: The number of columns in @tree_view after appending.
9313 pspp_sheet_view_append_column (PsppSheetView *tree_view,
9314 PsppSheetViewColumn *column)
9316 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9317 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9318 g_return_val_if_fail (column->tree_view == NULL, -1);
9320 return pspp_sheet_view_insert_column (tree_view, column, -1);
9325 * pspp_sheet_view_remove_column:
9326 * @tree_view: A #PsppSheetView.
9327 * @column: The #PsppSheetViewColumn to remove.
9329 * Removes @column from @tree_view.
9331 * Return value: The number of columns in @tree_view after removing.
9334 pspp_sheet_view_remove_column (PsppSheetView *tree_view,
9335 PsppSheetViewColumn *column)
9337 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9338 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9339 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9341 if (tree_view->priv->focus_column == column)
9342 tree_view->priv->focus_column = NULL;
9344 if (tree_view->priv->edited_column == column)
9346 pspp_sheet_view_stop_editing (tree_view, TRUE);
9348 /* no need to, but just to be sure ... */
9349 tree_view->priv->edited_column = NULL;
9352 _pspp_sheet_view_column_unset_tree_view (column);
9354 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9355 tree_view->priv->n_columns--;
9357 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9361 _pspp_sheet_view_column_unrealize_button (column);
9362 for (list = tree_view->priv->columns; list; list = list->next)
9364 PsppSheetViewColumn *tmp_column;
9366 tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9367 if (tmp_column->visible)
9368 _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9371 if (tree_view->priv->n_columns == 0 &&
9372 pspp_sheet_view_get_headers_visible (tree_view) &&
9373 tree_view->priv->header_window)
9374 gdk_window_hide (tree_view->priv->header_window);
9376 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9379 g_object_unref (column);
9380 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9382 return tree_view->priv->n_columns;
9386 * pspp_sheet_view_insert_column:
9387 * @tree_view: A #PsppSheetView.
9388 * @column: The #PsppSheetViewColumn to be inserted.
9389 * @position: The position to insert @column in.
9391 * This inserts the @column into the @tree_view at @position. If @position is
9392 * -1, then the column is inserted at the end.
9394 * Return value: The number of columns in @tree_view after insertion.
9397 pspp_sheet_view_insert_column (PsppSheetView *tree_view,
9398 PsppSheetViewColumn *column,
9401 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9402 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9403 g_return_val_if_fail (column->tree_view == NULL, -1);
9405 g_object_ref_sink (column);
9407 if (tree_view->priv->n_columns == 0 &&
9408 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9409 pspp_sheet_view_get_headers_visible (tree_view))
9411 gdk_window_show (tree_view->priv->header_window);
9414 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9416 tree_view->priv->n_columns++;
9418 _pspp_sheet_view_column_set_tree_view (column, tree_view);
9420 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9424 _pspp_sheet_view_column_realize_button (column);
9426 for (list = tree_view->priv->columns; list; list = list->next)
9428 column = PSPP_SHEET_VIEW_COLUMN (list->data);
9429 if (column->visible)
9430 _pspp_sheet_view_column_cell_set_dirty (column);
9432 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9435 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9437 return tree_view->priv->n_columns;
9441 * pspp_sheet_view_insert_column_with_attributes:
9442 * @tree_view: A #PsppSheetView
9443 * @position: The position to insert the new column in.
9444 * @title: The title to set the header to.
9445 * @cell: The #GtkCellRenderer.
9446 * @Varargs: A %NULL-terminated list of attributes.
9448 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9449 * @position. If @position is -1, then the newly created column is inserted at
9450 * the end. The column is initialized with the attributes given.
9452 * Return value: The number of columns in @tree_view after insertion.
9455 pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view,
9458 GtkCellRenderer *cell,
9461 PsppSheetViewColumn *column;
9466 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9468 column = pspp_sheet_view_column_new ();
9469 pspp_sheet_view_column_set_title (column, title);
9470 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9472 va_start (args, cell);
9474 attribute = va_arg (args, gchar *);
9476 while (attribute != NULL)
9478 column_id = va_arg (args, gint);
9479 pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9480 attribute = va_arg (args, gchar *);
9485 pspp_sheet_view_insert_column (tree_view, column, position);
9487 return tree_view->priv->n_columns;
9491 * pspp_sheet_view_insert_column_with_data_func:
9492 * @tree_view: a #PsppSheetView
9493 * @position: Position to insert, -1 for append
9494 * @title: column title
9495 * @cell: cell renderer for column
9496 * @func: function to set attributes of cell renderer
9497 * @data: data for @func
9498 * @dnotify: destroy notifier for @data
9500 * Convenience function that inserts a new column into the #PsppSheetView
9501 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9502 * attributes (normally using data from the model). See also
9503 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9505 * Return value: number of columns in the tree view post-insert
9508 pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view,
9511 GtkCellRenderer *cell,
9512 PsppSheetCellDataFunc func,
9514 GDestroyNotify dnotify)
9516 PsppSheetViewColumn *column;
9518 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9520 column = pspp_sheet_view_column_new ();
9521 pspp_sheet_view_column_set_title (column, title);
9522 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9523 pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9525 pspp_sheet_view_insert_column (tree_view, column, position);
9527 return tree_view->priv->n_columns;
9531 * pspp_sheet_view_get_column:
9532 * @tree_view: A #PsppSheetView.
9533 * @n: The position of the column, counting from 0.
9535 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9537 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9540 PsppSheetViewColumn *
9541 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9544 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9546 if (n < 0 || n >= tree_view->priv->n_columns)
9549 if (tree_view->priv->columns == NULL)
9552 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9556 * pspp_sheet_view_get_columns:
9557 * @tree_view: A #PsppSheetView
9559 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9560 * The returned list must be freed with g_list_free ().
9562 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9565 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9567 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9569 return g_list_copy (tree_view->priv->columns);
9573 * pspp_sheet_view_move_column_after:
9574 * @tree_view: A #PsppSheetView
9575 * @column: The #PsppSheetViewColumn to be moved.
9576 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9578 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9579 * @column is placed in the first position.
9582 pspp_sheet_view_move_column_after (PsppSheetView *tree_view,
9583 PsppSheetViewColumn *column,
9584 PsppSheetViewColumn *base_column)
9586 GList *column_list_el, *base_el = NULL;
9588 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9590 column_list_el = g_list_find (tree_view->priv->columns, column);
9591 g_return_if_fail (column_list_el != NULL);
9595 base_el = g_list_find (tree_view->priv->columns, base_column);
9596 g_return_if_fail (base_el != NULL);
9599 if (column_list_el->prev == base_el)
9602 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9603 if (base_el == NULL)
9605 column_list_el->prev = NULL;
9606 column_list_el->next = tree_view->priv->columns;
9607 if (column_list_el->next)
9608 column_list_el->next->prev = column_list_el;
9609 tree_view->priv->columns = column_list_el;
9613 column_list_el->prev = base_el;
9614 column_list_el->next = base_el->next;
9615 if (column_list_el->next)
9616 column_list_el->next->prev = column_list_el;
9617 base_el->next = column_list_el;
9620 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9622 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9623 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9626 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9630 * pspp_sheet_view_set_column_drag_function:
9631 * @tree_view: A #PsppSheetView.
9632 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9633 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9634 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9636 * Sets a user function for determining where a column may be dropped when
9637 * dragged. This function is called on every column pair in turn at the
9638 * beginning of a column drag to determine where a drop can take place. The
9639 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9640 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9641 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9642 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9643 * @tree_view reverts to the default behavior of allowing all columns to be
9644 * dropped everywhere.
9647 pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view,
9648 PsppSheetViewColumnDropFunc func,
9650 GDestroyNotify destroy)
9652 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9654 if (tree_view->priv->column_drop_func_data_destroy)
9655 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9657 tree_view->priv->column_drop_func = func;
9658 tree_view->priv->column_drop_func_data = user_data;
9659 tree_view->priv->column_drop_func_data_destroy = destroy;
9663 * pspp_sheet_view_scroll_to_point:
9664 * @tree_view: a #PsppSheetView
9665 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9666 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9668 * Scrolls the tree view such that the top-left corner of the visible
9669 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9670 * in tree coordinates. The @tree_view must be realized before
9671 * this function is called. If it isn't, you probably want to be
9672 * using pspp_sheet_view_scroll_to_cell().
9674 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9677 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9681 GtkAdjustment *hadj;
9682 GtkAdjustment *vadj;
9684 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9685 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9687 hadj = tree_view->priv->hadjustment;
9688 vadj = tree_view->priv->vadjustment;
9691 gtk_adjustment_set_value (hadj, CLAMP (tree_x, gtk_adjustment_get_lower (hadj), gtk_adjustment_get_upper (hadj) - gtk_adjustment_get_page_size (hadj)));
9693 gtk_adjustment_set_value (vadj, CLAMP (tree_y, gtk_adjustment_get_lower (vadj), gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj)));
9697 * pspp_sheet_view_scroll_to_cell:
9698 * @tree_view: A #PsppSheetView.
9699 * @path: (allow-none): The path of the row to move to, or %NULL.
9700 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9701 * @use_align: whether to use alignment arguments, or %FALSE.
9702 * @row_align: The vertical alignment of the row specified by @path.
9703 * @col_align: The horizontal alignment of the column specified by @column.
9705 * Moves the alignments of @tree_view to the position specified by @column and
9706 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9707 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9708 * or @path need to be non-%NULL. @row_align determines where the row is
9709 * placed, and @col_align determines where @column is placed. Both are expected
9710 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9711 * right/bottom alignment, 0.5 means center.
9713 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9714 * tree does the minimum amount of work to scroll the cell onto the screen.
9715 * This means that the cell will be scrolled to the edge closest to its current
9716 * position. If the cell is currently visible on the screen, nothing is done.
9718 * This function only works if the model is set, and @path is a valid row on the
9719 * model. If the model changes before the @tree_view is realized, the centered
9720 * path will be modified to reflect this change.
9723 pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view,
9725 PsppSheetViewColumn *column,
9730 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9731 g_return_if_fail (tree_view->priv->model != NULL);
9732 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9733 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9734 g_return_if_fail (path != NULL || column != NULL);
9737 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9738 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9740 row_align = CLAMP (row_align, 0.0, 1.0);
9741 col_align = CLAMP (col_align, 0.0, 1.0);
9744 /* Note: Despite the benefits that come from having one code path for the
9745 * scrolling code, we short-circuit validate_visible_area's immplementation as
9746 * it is much slower than just going to the point.
9748 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9749 !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9750 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9752 if (tree_view->priv->scroll_to_path)
9753 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9755 tree_view->priv->scroll_to_path = NULL;
9756 tree_view->priv->scroll_to_column = NULL;
9759 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
9761 tree_view->priv->scroll_to_column = column;
9762 tree_view->priv->scroll_to_use_align = use_align;
9763 tree_view->priv->scroll_to_row_align = row_align;
9764 tree_view->priv->scroll_to_col_align = col_align;
9766 install_presize_handler (tree_view);
9770 GdkRectangle cell_rect;
9771 GdkRectangle vis_rect;
9772 gint dest_x, dest_y;
9774 pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
9775 pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
9777 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
9779 dest_x = vis_rect.x;
9780 dest_y = vis_rect.y;
9786 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
9790 if (cell_rect.x < vis_rect.x)
9791 dest_x = cell_rect.x;
9792 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
9793 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
9801 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
9802 dest_y = MAX (dest_y, 0);
9806 if (cell_rect.y < vis_rect.y)
9807 dest_y = cell_rect.y;
9808 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
9809 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
9813 pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
9818 * pspp_sheet_view_row_activated:
9819 * @tree_view: A #PsppSheetView
9820 * @path: The #GtkTreePath to be activated.
9821 * @column: The #PsppSheetViewColumn to be activated.
9823 * Activates the cell determined by @path and @column.
9826 pspp_sheet_view_row_activated (PsppSheetView *tree_view,
9828 PsppSheetViewColumn *column)
9830 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9832 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
9837 * pspp_sheet_view_get_reorderable:
9838 * @tree_view: a #PsppSheetView
9840 * Retrieves whether the user can reorder the tree via drag-and-drop. See
9841 * pspp_sheet_view_set_reorderable().
9843 * Return value: %TRUE if the tree can be reordered.
9846 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
9848 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9850 return tree_view->priv->reorderable;
9854 * pspp_sheet_view_set_reorderable:
9855 * @tree_view: A #PsppSheetView.
9856 * @reorderable: %TRUE, if the tree can be reordered.
9858 * This function is a convenience function to allow you to reorder
9859 * models that support the #GtkDragSourceIface and the
9860 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
9861 * these. If @reorderable is %TRUE, then the user can reorder the
9862 * model by dragging and dropping rows. The developer can listen to
9863 * these changes by connecting to the model's row_inserted and
9864 * row_deleted signals. The reordering is implemented by setting up
9865 * the tree view as a drag source and destination. Therefore, drag and
9866 * drop can not be used in a reorderable view for any other purpose.
9868 * This function does not give you any degree of control over the order -- any
9869 * reordering is allowed. If more control is needed, you should probably
9870 * handle drag and drop manually.
9873 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
9874 gboolean reorderable)
9876 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9878 reorderable = reorderable != FALSE;
9880 if (tree_view->priv->reorderable == reorderable)
9885 const GtkTargetEntry row_targets[] = {
9886 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
9889 pspp_sheet_view_enable_model_drag_source (tree_view,
9892 G_N_ELEMENTS (row_targets),
9894 pspp_sheet_view_enable_model_drag_dest (tree_view,
9896 G_N_ELEMENTS (row_targets),
9901 pspp_sheet_view_unset_rows_drag_source (tree_view);
9902 pspp_sheet_view_unset_rows_drag_dest (tree_view);
9905 tree_view->priv->reorderable = reorderable;
9907 g_object_notify (G_OBJECT (tree_view), "reorderable");
9910 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
9911 is pressed, other rows will be unselected.
9913 If CLAMP_NODE is true, then the sheetview will scroll to make the row
9916 pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
9918 gboolean clear_and_select,
9919 gboolean clamp_node,
9920 PsppSheetSelectMode mode)
9924 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9926 GtkTreePath *cursor_path;
9927 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9928 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
9929 gtk_tree_path_free (cursor_path);
9932 gtk_tree_row_reference_free (tree_view->priv->cursor);
9933 tree_view->priv->cursor = NULL;
9935 _pspp_sheet_view_find_node (tree_view, path, &node);
9936 tree_view->priv->cursor =
9937 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
9938 tree_view->priv->model,
9941 if (tree_view->priv->row_count > 0)
9945 if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
9946 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
9950 /* We have to re-find tree and node here again, somebody might have
9951 * cleared the node or the whole tree in the PsppSheetSelection::changed
9952 * callback. If the nodes differ we bail out here.
9954 _pspp_sheet_view_find_node (tree_view, path, &new_node);
9956 if (node != new_node)
9961 pspp_sheet_view_clamp_node_visible (tree_view, node);
9962 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
9966 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
9970 * pspp_sheet_view_get_cursor:
9971 * @tree_view: A #PsppSheetView
9972 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
9973 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
9975 * Fills in @path and @focus_column with the current path and focus column. If
9976 * the cursor isn't currently set, then *@path will be %NULL. If no column
9977 * currently has focus, then *@focus_column will be %NULL.
9979 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
9980 * you are done with it.
9983 pspp_sheet_view_get_cursor (PsppSheetView *tree_view,
9985 PsppSheetViewColumn **focus_column)
9987 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9991 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9992 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9999 *focus_column = tree_view->priv->focus_column;
10004 * pspp_sheet_view_set_cursor:
10005 * @tree_view: A #PsppSheetView
10006 * @path: A #GtkTreePath
10007 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10008 * @start_editing: %TRUE if the specified cell should start being edited.
10010 * Sets the current keyboard focus to be at @path, and selects it. This is
10011 * useful when you want to focus the user's attention on a particular row. If
10012 * @focus_column is not %NULL, then focus is given to the column specified by
10013 * it. Additionally, if @focus_column is specified, and @start_editing is
10014 * %TRUE, then editing should be started in the specified cell.
10015 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10016 * in order to give keyboard focus to the widget. Please note that editing
10017 * can only happen when the widget is realized.
10019 * If @path is invalid for @model, the current cursor (if any) will be unset
10020 * and the function will return without failing.
10023 pspp_sheet_view_set_cursor (PsppSheetView *tree_view,
10025 PsppSheetViewColumn *focus_column,
10026 gboolean start_editing)
10028 pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10029 NULL, start_editing);
10033 * pspp_sheet_view_set_cursor_on_cell:
10034 * @tree_view: A #PsppSheetView
10035 * @path: A #GtkTreePath
10036 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10037 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10038 * @start_editing: %TRUE if the specified cell should start being edited.
10040 * Sets the current keyboard focus to be at @path, and selects it. This is
10041 * useful when you want to focus the user's attention on a particular row. If
10042 * @focus_column is not %NULL, then focus is given to the column specified by
10043 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10044 * contains 2 or more editable or activatable cells, then focus is given to
10045 * the cell specified by @focus_cell. Additionally, if @focus_column is
10046 * specified, and @start_editing is %TRUE, then editing should be started in
10047 * the specified cell. This function is often followed by
10048 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10049 * widget. Please note that editing can only happen when the widget is
10052 * If @path is invalid for @model, the current cursor (if any) will be unset
10053 * and the function will return without failing.
10058 pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view,
10060 PsppSheetViewColumn *focus_column,
10061 GtkCellRenderer *focus_cell,
10062 gboolean start_editing)
10064 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10065 g_return_if_fail (path != NULL);
10066 g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10068 if (!tree_view->priv->model)
10073 g_return_if_fail (focus_column);
10074 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10077 /* cancel the current editing, if it exists */
10078 if (tree_view->priv->edited_column &&
10079 tree_view->priv->edited_column->editable_widget)
10080 pspp_sheet_view_stop_editing (tree_view, TRUE);
10082 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
10084 if (focus_column && focus_column->visible)
10087 gboolean column_in_tree = FALSE;
10089 for (list = tree_view->priv->columns; list; list = list->next)
10090 if (list->data == focus_column)
10092 column_in_tree = TRUE;
10095 g_return_if_fail (column_in_tree);
10096 tree_view->priv->focus_column = focus_column;
10098 pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10100 pspp_sheet_view_start_editing (tree_view, path);
10102 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10103 pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10109 * pspp_sheet_view_get_bin_window:
10110 * @tree_view: A #PsppSheetView
10112 * Returns the window that @tree_view renders to. This is used primarily to
10113 * compare to <literal>event->window</literal> to confirm that the event on
10114 * @tree_view is on the right window.
10116 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10119 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10121 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10123 return tree_view->priv->bin_window;
10127 * pspp_sheet_view_get_path_at_pos:
10128 * @tree_view: A #PsppSheetView.
10129 * @x: The x position to be identified (relative to bin_window).
10130 * @y: The y position to be identified (relative to bin_window).
10131 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10132 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10133 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10134 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10136 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10137 * (please see pspp_sheet_view_get_bin_window()).
10138 * That is, @x and @y are relative to an events coordinates. @x and @y must
10139 * come from an event on the @tree_view only where <literal>event->window ==
10140 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10141 * things like popup menus. If @path is non-%NULL, then it will be filled
10142 * with the #GtkTreePath at that point. This path should be freed with
10143 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10144 * with the column at that point. @cell_x and @cell_y return the coordinates
10145 * relative to the cell background (i.e. the @background_area passed to
10146 * gtk_cell_renderer_render()). This function is only meaningful if
10147 * @tree_view is realized. Therefore this function will always return %FALSE
10148 * if @tree_view is not realized or does not have a model.
10150 * For converting widget coordinates (eg. the ones you get from
10151 * GtkWidget::query-tooltip), please see
10152 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10154 * Return value: %TRUE if a row exists at that coordinate.
10157 pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view,
10160 GtkTreePath **path,
10161 PsppSheetViewColumn **column,
10168 g_return_val_if_fail (tree_view != NULL, FALSE);
10175 if (tree_view->priv->bin_window == NULL)
10178 if (tree_view->priv->row_count == 0)
10181 if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment))
10184 if (x < 0 || y < 0)
10187 if (column || cell_x)
10189 PsppSheetViewColumn *tmp_column;
10190 PsppSheetViewColumn *last_column = NULL;
10192 gint remaining_x = x;
10193 gboolean found = FALSE;
10196 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10197 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10199 list = (rtl ? list->prev : list->next))
10201 tmp_column = list->data;
10203 if (tmp_column->visible == FALSE)
10206 last_column = tmp_column;
10207 if (remaining_x <= tmp_column->width)
10212 *column = tmp_column;
10215 *cell_x = remaining_x;
10219 remaining_x -= tmp_column->width;
10222 /* If found is FALSE and there is a last_column, then it the remainder
10223 * space is in that area
10230 *column = last_column;
10233 *cell_x = last_column->width + remaining_x;
10242 y_offset = pspp_sheet_view_find_offset (tree_view,
10243 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10250 *cell_y = y_offset;
10253 *path = _pspp_sheet_view_find_path (tree_view, node);
10258 /* Computes 'cell_area' from 'background_area', which must be the background
10259 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10260 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10261 the cell area as passed to _pspp_sheet_view_column_cell_render().
10263 'column' is required to properly adjust 'cell_area->x' and
10264 'cell_area->width'. It may be set to NULL if these values are not of
10265 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10268 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
10269 PsppSheetViewColumn *column,
10270 const GdkRectangle *background_area,
10271 gboolean subtract_focus_rect,
10272 GdkRectangle *cell_area)
10274 gint vertical_separator;
10275 gint horizontal_separator;
10277 *cell_area = *background_area;
10279 gtk_widget_style_get (GTK_WIDGET (tree_view),
10280 "vertical-separator", &vertical_separator,
10281 "horizontal-separator", &horizontal_separator,
10283 cell_area->x += horizontal_separator / 2;
10284 cell_area->y += vertical_separator / 2;
10285 cell_area->width -= horizontal_separator;
10286 cell_area->height -= vertical_separator;
10288 if (subtract_focus_rect)
10290 int focus_line_width;
10292 gtk_widget_style_get (GTK_WIDGET (tree_view),
10293 "focus-line-width", &focus_line_width,
10295 cell_area->x += focus_line_width;
10296 cell_area->y += focus_line_width;
10297 cell_area->width -= 2 * focus_line_width;
10298 cell_area->height -= 2 * focus_line_width;
10301 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10303 gint grid_line_width;
10304 gtk_widget_style_get (GTK_WIDGET (tree_view),
10305 "grid-line-width", &grid_line_width,
10308 if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10309 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10312 PsppSheetViewColumn *first_column, *last_column;
10315 /* Find the last visible column. */
10316 last_column = NULL;
10317 for (list = g_list_last (tree_view->priv->columns);
10321 PsppSheetViewColumn *c = list->data;
10329 /* Find the first visible column. */
10330 first_column = NULL;
10331 for (list = g_list_first (tree_view->priv->columns);
10335 PsppSheetViewColumn *c = list->data;
10343 if (column == first_column)
10345 cell_area->width -= grid_line_width / 2;
10347 else if (column == last_column)
10349 cell_area->x += grid_line_width / 2;
10350 cell_area->width -= grid_line_width / 2;
10354 cell_area->x += grid_line_width / 2;
10355 cell_area->width -= grid_line_width;
10359 if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10360 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10362 cell_area->y += grid_line_width / 2;
10363 cell_area->height -= grid_line_width;
10367 if (column == NULL)
10370 cell_area->width = 0;
10375 * pspp_sheet_view_get_cell_area:
10376 * @tree_view: a #PsppSheetView
10377 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10378 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10379 * @rect: rectangle to fill with cell rect
10381 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10382 * row specified by @path and the column specified by @column. If @path is
10383 * %NULL, or points to a path not currently displayed, the @y and @height fields
10384 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10385 * fields will be filled with 0. The sum of all cell rects does not cover the
10386 * entire tree; there are extra pixels in between rows, for example. The
10387 * returned rectangle is equivalent to the @cell_area passed to
10388 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10392 pspp_sheet_view_get_cell_area (PsppSheetView *tree_view,
10394 PsppSheetViewColumn *column,
10395 GdkRectangle *rect)
10397 GdkRectangle background_area;
10399 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10400 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10401 g_return_if_fail (rect != NULL);
10402 g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10403 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10405 pspp_sheet_view_get_background_area (tree_view, path, column,
10407 pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10412 * pspp_sheet_view_get_background_area:
10413 * @tree_view: a #PsppSheetView
10414 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10415 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10416 * @rect: rectangle to fill with cell background rect
10418 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10419 * row specified by @path and the column specified by @column. If @path is
10420 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10421 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10422 * fields will be filled with 0. The returned rectangle is equivalent to the
10423 * @background_area passed to gtk_cell_renderer_render(). These background
10424 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10425 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10426 * itself, excluding surrounding borders.
10430 pspp_sheet_view_get_background_area (PsppSheetView *tree_view,
10432 PsppSheetViewColumn *column,
10433 GdkRectangle *rect)
10437 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10438 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10439 g_return_if_fail (rect != NULL);
10448 /* Get vertical coords */
10450 _pspp_sheet_view_find_node (tree_view, path, &node);
10454 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10456 rect->height = ROW_HEIGHT (tree_view);
10463 pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10464 rect->width = x2 - rect->x;
10469 * pspp_sheet_view_get_visible_rect:
10470 * @tree_view: a #PsppSheetView
10471 * @visible_rect: rectangle to fill
10473 * Fills @visible_rect with the currently-visible region of the
10474 * buffer, in tree coordinates. Convert to bin_window coordinates with
10475 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10476 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10477 * scrollable area of the tree.
10480 pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view,
10481 GdkRectangle *visible_rect)
10485 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10487 widget = GTK_WIDGET (tree_view);
10491 GtkAllocation allocation;
10492 gtk_widget_get_allocation (widget, &allocation);
10493 visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
10494 visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
10495 visible_rect->width = allocation.width;
10496 visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10501 * pspp_sheet_view_widget_to_tree_coords:
10502 * @tree_view: a #PsppSheetView
10503 * @wx: X coordinate relative to bin_window
10504 * @wy: Y coordinate relative to bin_window
10505 * @tx: return location for tree X coordinate
10506 * @ty: return location for tree Y coordinate
10508 * Converts bin_window coordinates to coordinates for the
10509 * tree (the full scrollable area of the tree).
10511 * Deprecated: 2.12: Due to historial reasons the name of this function is
10512 * incorrect. For converting coordinates relative to the widget to
10513 * bin_window coordinates, please see
10514 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10518 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10524 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10527 *tx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10529 *ty = wy + tree_view->priv->dy;
10533 * pspp_sheet_view_tree_to_widget_coords:
10534 * @tree_view: a #PsppSheetView
10535 * @tx: tree X coordinate
10536 * @ty: tree Y coordinate
10537 * @wx: return location for X coordinate relative to bin_window
10538 * @wy: return location for Y coordinate relative to bin_window
10540 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10541 * to bin_window coordinates.
10543 * Deprecated: 2.12: Due to historial reasons the name of this function is
10544 * incorrect. For converting bin_window coordinates to coordinates relative
10545 * to bin_window, please see
10546 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10550 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10556 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10559 *wx = tx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10561 *wy = ty - tree_view->priv->dy;
10566 * pspp_sheet_view_convert_widget_to_tree_coords:
10567 * @tree_view: a #PsppSheetView
10568 * @wx: X coordinate relative to the widget
10569 * @wy: Y coordinate relative to the widget
10570 * @tx: return location for tree X coordinate
10571 * @ty: return location for tree Y coordinate
10573 * Converts widget coordinates to coordinates for the
10574 * tree (the full scrollable area of the tree).
10579 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10587 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10589 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10592 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10598 * pspp_sheet_view_convert_tree_to_widget_coords:
10599 * @tree_view: a #PsppSheetView
10600 * @tx: X coordinate relative to the tree
10601 * @ty: Y coordinate relative to the tree
10602 * @wx: return location for widget X coordinate
10603 * @wy: return location for widget Y coordinate
10605 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10606 * to widget coordinates.
10611 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10619 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10621 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10624 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10630 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10631 * @tree_view: a #PsppSheetView
10632 * @wx: X coordinate relative to the widget
10633 * @wy: Y coordinate relative to the widget
10634 * @bx: return location for bin_window X coordinate
10635 * @by: return location for bin_window Y coordinate
10637 * Converts widget coordinates to coordinates for the bin_window
10638 * (see pspp_sheet_view_get_bin_window()).
10643 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10649 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10652 *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10654 *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10658 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10659 * @tree_view: a #PsppSheetView
10660 * @bx: bin_window X coordinate
10661 * @by: bin_window Y coordinate
10662 * @wx: return location for widget X coordinate
10663 * @wy: return location for widget Y coordinate
10665 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10666 * to widget relative coordinates.
10671 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10677 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10680 *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10682 *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10686 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10687 * @tree_view: a #PsppSheetView
10688 * @tx: tree X coordinate
10689 * @ty: tree Y coordinate
10690 * @bx: return location for X coordinate relative to bin_window
10691 * @by: return location for Y coordinate relative to bin_window
10693 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10694 * to bin_window coordinates.
10699 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10705 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10710 *by = ty - tree_view->priv->dy;
10714 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10715 * @tree_view: a #PsppSheetView
10716 * @bx: X coordinate relative to bin_window
10717 * @by: Y coordinate relative to bin_window
10718 * @tx: return location for tree X coordinate
10719 * @ty: return location for tree Y coordinate
10721 * Converts bin_window coordinates to coordinates for the
10722 * tree (the full scrollable area of the tree).
10727 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10733 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10738 *ty = by + tree_view->priv->dy;
10744 * pspp_sheet_view_get_visible_range:
10745 * @tree_view: A #PsppSheetView
10746 * @start_path: (allow-none): Return location for start of region, or %NULL.
10747 * @end_path: (allow-none): Return location for end of region, or %NULL.
10749 * Sets @start_path and @end_path to be the first and last visible path.
10750 * Note that there may be invisible paths in between.
10752 * The paths should be freed with gtk_tree_path_free() after use.
10754 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
10759 pspp_sheet_view_get_visible_range (PsppSheetView *tree_view,
10760 GtkTreePath **start_path,
10761 GtkTreePath **end_path)
10766 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10768 if (!tree_view->priv->row_count)
10775 pspp_sheet_view_find_offset (tree_view,
10776 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
10779 *start_path = _pspp_sheet_view_find_path (tree_view, node);
10788 if (tree_view->priv->height < gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
10789 y = tree_view->priv->height - 1;
10791 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1;
10793 pspp_sheet_view_find_offset (tree_view, y, &node);
10795 *end_path = _pspp_sheet_view_find_path (tree_view, node);
10804 unset_reorderable (PsppSheetView *tree_view)
10806 if (tree_view->priv->reorderable)
10808 tree_view->priv->reorderable = FALSE;
10809 g_object_notify (G_OBJECT (tree_view), "reorderable");
10814 * pspp_sheet_view_enable_model_drag_source:
10815 * @tree_view: a #PsppSheetView
10816 * @start_button_mask: Mask of allowed buttons to start drag
10817 * @targets: the table of targets that the drag will support
10818 * @n_targets: the number of items in @targets
10819 * @actions: the bitmask of possible actions for a drag from this
10822 * Turns @tree_view into a drag source for automatic DND. Calling this
10823 * method sets #PsppSheetView:reorderable to %FALSE.
10826 pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view,
10827 GdkModifierType start_button_mask,
10828 const GtkTargetEntry *targets,
10830 GdkDragAction actions)
10832 TreeViewDragInfo *di;
10834 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10836 gtk_drag_source_set (GTK_WIDGET (tree_view),
10842 di = ensure_info (tree_view);
10844 di->start_button_mask = start_button_mask;
10845 di->source_actions = actions;
10846 di->source_set = TRUE;
10848 unset_reorderable (tree_view);
10852 * pspp_sheet_view_enable_model_drag_dest:
10853 * @tree_view: a #PsppSheetView
10854 * @targets: the table of targets that the drag will support
10855 * @n_targets: the number of items in @targets
10856 * @actions: the bitmask of possible actions for a drag from this
10859 * Turns @tree_view into a drop destination for automatic DND. Calling
10860 * this method sets #PsppSheetView:reorderable to %FALSE.
10863 pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view,
10864 const GtkTargetEntry *targets,
10866 GdkDragAction actions)
10868 TreeViewDragInfo *di;
10870 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10872 gtk_drag_dest_set (GTK_WIDGET (tree_view),
10878 di = ensure_info (tree_view);
10879 di->dest_set = TRUE;
10881 unset_reorderable (tree_view);
10885 * pspp_sheet_view_unset_rows_drag_source:
10886 * @tree_view: a #PsppSheetView
10888 * Undoes the effect of
10889 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
10890 * #PsppSheetView:reorderable to %FALSE.
10893 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
10895 TreeViewDragInfo *di;
10897 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10899 di = get_info (tree_view);
10903 if (di->source_set)
10905 gtk_drag_source_unset (GTK_WIDGET (tree_view));
10906 di->source_set = FALSE;
10909 if (!di->dest_set && !di->source_set)
10910 remove_info (tree_view);
10913 unset_reorderable (tree_view);
10917 * pspp_sheet_view_unset_rows_drag_dest:
10918 * @tree_view: a #PsppSheetView
10920 * Undoes the effect of
10921 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
10922 * #PsppSheetView:reorderable to %FALSE.
10925 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
10927 TreeViewDragInfo *di;
10929 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10931 di = get_info (tree_view);
10937 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
10938 di->dest_set = FALSE;
10941 if (!di->dest_set && !di->source_set)
10942 remove_info (tree_view);
10945 unset_reorderable (tree_view);
10949 * pspp_sheet_view_set_drag_dest_row:
10950 * @tree_view: a #PsppSheetView
10951 * @path: (allow-none): The path of the row to highlight, or %NULL.
10952 * @pos: Specifies whether to drop before, after or into the row
10954 * Sets the row that is highlighted for feedback.
10957 pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view,
10959 PsppSheetViewDropPosition pos)
10961 GtkTreePath *current_dest;
10963 /* Note; this function is exported to allow a custom DND
10964 * implementation, so it can't touch TreeViewDragInfo
10967 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10969 current_dest = NULL;
10971 if (tree_view->priv->drag_dest_row)
10973 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
10974 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
10977 /* special case a drop on an empty model */
10978 tree_view->priv->empty_view_drop = 0;
10980 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
10981 && gtk_tree_path_get_depth (path) == 1
10982 && gtk_tree_path_get_indices (path)[0] == 0)
10986 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
10990 tree_view->priv->empty_view_drop = 1;
10993 tree_view->priv->drag_dest_pos = pos;
10997 tree_view->priv->drag_dest_row =
10998 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
10999 pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
11002 tree_view->priv->drag_dest_row = NULL;
11006 int node, new_node;
11008 _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11009 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11013 new_node = pspp_sheet_view_node_next (tree_view, node);
11015 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11017 new_node = pspp_sheet_view_node_prev (tree_view, node);
11019 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11021 gtk_tree_path_free (current_dest);
11026 * pspp_sheet_view_get_drag_dest_row:
11027 * @tree_view: a #PsppSheetView
11028 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11029 * @pos: (allow-none): Return location for the drop position, or %NULL
11031 * Gets information about the row that is highlighted for feedback.
11034 pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view,
11035 GtkTreePath **path,
11036 PsppSheetViewDropPosition *pos)
11038 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11042 if (tree_view->priv->drag_dest_row)
11043 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11046 if (tree_view->priv->empty_view_drop)
11047 *path = gtk_tree_path_new_from_indices (0, -1);
11054 *pos = tree_view->priv->drag_dest_pos;
11058 * pspp_sheet_view_get_dest_row_at_pos:
11059 * @tree_view: a #PsppSheetView
11060 * @drag_x: the position to determine the destination row for
11061 * @drag_y: the position to determine the destination row for
11062 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11063 * @pos: (allow-none): Return location for the drop position, or %NULL
11065 * Determines the destination row for a given position. @drag_x and
11066 * @drag_y are expected to be in widget coordinates. This function is only
11067 * meaningful if @tree_view is realized. Therefore this function will always
11068 * return %FALSE if @tree_view is not realized or does not have a model.
11070 * Return value: whether there is a row at the given position, %TRUE if this
11071 * is indeed the case.
11074 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view,
11077 GtkTreePath **path,
11078 PsppSheetViewDropPosition *pos)
11082 gdouble offset_into_row;
11085 PsppSheetViewColumn *column = NULL;
11086 GtkTreePath *tmp_path = NULL;
11088 /* Note; this function is exported to allow a custom DND
11089 * implementation, so it can't touch TreeViewDragInfo
11092 g_return_val_if_fail (tree_view != NULL, FALSE);
11093 g_return_val_if_fail (drag_x >= 0, FALSE);
11094 g_return_val_if_fail (drag_y >= 0, FALSE);
11099 if (tree_view->priv->bin_window == NULL)
11102 if (tree_view->priv->row_count == 0)
11105 /* If in the top third of a row, we drop before that row; if
11106 * in the bottom third, drop after that row; if in the middle,
11107 * and the row has children, drop into the row.
11109 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11112 if (!pspp_sheet_view_get_path_at_pos (tree_view,
11121 pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11124 offset_into_row = cell_y;
11129 gtk_tree_path_free (tmp_path);
11133 third = cell.height / 3.0;
11137 if (offset_into_row < third)
11139 *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11141 else if (offset_into_row < (cell.height / 2.0))
11143 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11145 else if (offset_into_row < third * 2.0)
11147 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11151 *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11159 #if GTK3_TRANSITION
11160 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11162 * pspp_sheet_view_create_row_drag_icon:
11163 * @tree_view: a #PsppSheetView
11164 * @path: a #GtkTreePath in @tree_view
11166 * Creates a #GdkPixmap representation of the row at @path.
11167 * This image is used for a drag icon.
11169 * Return value: a newly-allocated pixmap of the drag icon.
11172 pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view,
11179 GdkRectangle background_area;
11180 GdkRectangle expose_area;
11182 /* start drawing inside the black outline */
11184 GdkDrawable *drawable;
11185 gint bin_window_width;
11188 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11189 g_return_val_if_fail (path != NULL, NULL);
11191 widget = GTK_WIDGET (tree_view);
11193 if (!gtk_widget_get_realized (widget))
11196 _pspp_sheet_view_find_node (tree_view,
11203 if (!gtk_tree_model_get_iter (tree_view->priv->model,
11210 background_area.y = y;
11211 background_area.height = ROW_HEIGHT (tree_view);
11213 bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
11215 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11216 bin_window_width + 2,
11217 background_area.height + 2,
11222 expose_area.width = bin_window_width + 2;
11223 expose_area.height = background_area.height + 2;
11225 #if GTK3_TRANSITION
11226 gdk_draw_rectangle (drawable,
11227 widget->style->base_gc [gtk_widget_get_state (widget)],
11230 bin_window_width + 2,
11231 background_area.height + 2);
11234 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11236 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11238 list = (rtl ? list->prev : list->next))
11240 PsppSheetViewColumn *column = list->data;
11241 GdkRectangle cell_area;
11242 gint vertical_separator;
11244 if (!column->visible)
11247 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11249 background_area.x = cell_offset;
11250 background_area.width = column->width;
11252 gtk_widget_style_get (widget,
11253 "vertical-separator", &vertical_separator,
11256 cell_area = background_area;
11258 cell_area.y += vertical_separator / 2;
11259 cell_area.height -= vertical_separator;
11261 if (pspp_sheet_view_column_cell_is_visible (column))
11262 _pspp_sheet_view_column_cell_render (column,
11268 cell_offset += column->width;
11271 #if GTK3_TRANSITION
11272 gdk_draw_rectangle (drawable,
11273 widget->style->black_gc,
11276 bin_window_width + 1,
11277 background_area.height + 1);
11285 * pspp_sheet_view_set_destroy_count_func:
11286 * @tree_view: A #PsppSheetView
11287 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11288 * @data: (allow-none): User data to be passed to @func, or %NULL
11289 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11291 * This function should almost never be used. It is meant for private use by
11292 * ATK for determining the number of visible children that are removed when a row is deleted.
11295 pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view,
11296 PsppSheetDestroyCountFunc func,
11298 GDestroyNotify destroy)
11300 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11302 if (tree_view->priv->destroy_count_destroy)
11303 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11305 tree_view->priv->destroy_count_func = func;
11306 tree_view->priv->destroy_count_data = data;
11307 tree_view->priv->destroy_count_destroy = destroy;
11312 * Interactive search
11316 * pspp_sheet_view_set_enable_search:
11317 * @tree_view: A #PsppSheetView
11318 * @enable_search: %TRUE, if the user can search interactively
11320 * If @enable_search is set, then the user can type in text to search through
11321 * the tree interactively (this is sometimes called "typeahead find").
11323 * Note that even if this is %FALSE, the user can still initiate a search
11324 * using the "start-interactive-search" key binding.
11327 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11328 gboolean enable_search)
11330 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11332 enable_search = !!enable_search;
11334 if (tree_view->priv->enable_search != enable_search)
11336 tree_view->priv->enable_search = enable_search;
11337 g_object_notify (G_OBJECT (tree_view), "enable-search");
11342 * pspp_sheet_view_get_enable_search:
11343 * @tree_view: A #PsppSheetView
11345 * Returns whether or not the tree allows to start interactive searching
11346 * by typing in text.
11348 * Return value: whether or not to let the user search interactively
11351 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11353 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11355 return tree_view->priv->enable_search;
11360 * pspp_sheet_view_get_search_column:
11361 * @tree_view: A #PsppSheetView
11363 * Gets the column searched on by the interactive search code.
11365 * Return value: the column the interactive search code searches in.
11368 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11370 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11372 return (tree_view->priv->search_column);
11376 * pspp_sheet_view_set_search_column:
11377 * @tree_view: A #PsppSheetView
11378 * @column: the column of the model to search in, or -1 to disable searching
11380 * Sets @column as the column where the interactive search code should
11381 * search in for the current model.
11383 * If the search column is set, users can use the "start-interactive-search"
11384 * key binding to bring up search popup. The enable-search property controls
11385 * whether simply typing text will also start an interactive search.
11387 * Note that @column refers to a column of the current model. The search
11388 * column is reset to -1 when the model is changed.
11391 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11394 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11395 g_return_if_fail (column >= -1);
11397 if (tree_view->priv->search_column == column)
11400 tree_view->priv->search_column = column;
11401 g_object_notify (G_OBJECT (tree_view), "search-column");
11405 * pspp_sheet_view_get_search_equal_func:
11406 * @tree_view: A #PsppSheetView
11408 * Returns the compare function currently in use.
11410 * Return value: the currently used compare function for the search code.
11413 PsppSheetViewSearchEqualFunc
11414 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11416 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11418 return tree_view->priv->search_equal_func;
11422 * pspp_sheet_view_set_search_equal_func:
11423 * @tree_view: A #PsppSheetView
11424 * @search_equal_func: the compare function to use during the search
11425 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11426 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11428 * Sets the compare function for the interactive search capabilities; note
11429 * that somewhat like strcmp() returning 0 for equality
11430 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11433 pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view,
11434 PsppSheetViewSearchEqualFunc search_equal_func,
11435 gpointer search_user_data,
11436 GDestroyNotify search_destroy)
11438 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11439 g_return_if_fail (search_equal_func != NULL);
11441 if (tree_view->priv->search_destroy)
11442 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11444 tree_view->priv->search_equal_func = search_equal_func;
11445 tree_view->priv->search_user_data = search_user_data;
11446 tree_view->priv->search_destroy = search_destroy;
11447 if (tree_view->priv->search_equal_func == NULL)
11448 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11452 * pspp_sheet_view_get_search_entry:
11453 * @tree_view: A #PsppSheetView
11455 * Returns the #GtkEntry which is currently in use as interactive search
11456 * entry for @tree_view. In case the built-in entry is being used, %NULL
11457 * will be returned.
11459 * Return value: the entry currently in use as search entry.
11464 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11466 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11468 if (tree_view->priv->search_custom_entry_set)
11469 return GTK_ENTRY (tree_view->priv->search_entry);
11475 * pspp_sheet_view_set_search_entry:
11476 * @tree_view: A #PsppSheetView
11477 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11479 * Sets the entry which the interactive search code will use for this
11480 * @tree_view. This is useful when you want to provide a search entry
11481 * in our interface at all time at a fixed position. Passing %NULL for
11482 * @entry will make the interactive search code use the built-in popup
11488 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11491 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11492 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11494 if (tree_view->priv->search_custom_entry_set)
11496 if (tree_view->priv->search_entry_changed_id)
11498 g_signal_handler_disconnect (tree_view->priv->search_entry,
11499 tree_view->priv->search_entry_changed_id);
11500 tree_view->priv->search_entry_changed_id = 0;
11502 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11503 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11506 g_object_unref (tree_view->priv->search_entry);
11508 else if (tree_view->priv->search_window)
11510 gtk_widget_destroy (tree_view->priv->search_window);
11512 tree_view->priv->search_window = NULL;
11517 tree_view->priv->search_entry = g_object_ref (entry);
11518 tree_view->priv->search_custom_entry_set = TRUE;
11520 if (tree_view->priv->search_entry_changed_id == 0)
11522 tree_view->priv->search_entry_changed_id =
11523 g_signal_connect (tree_view->priv->search_entry, "changed",
11524 G_CALLBACK (pspp_sheet_view_search_init),
11528 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11529 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11532 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11536 tree_view->priv->search_entry = NULL;
11537 tree_view->priv->search_custom_entry_set = FALSE;
11542 * pspp_sheet_view_set_search_position_func:
11543 * @tree_view: A #PsppSheetView
11544 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11545 * to use the default search position function
11546 * @data: (allow-none): user data to pass to @func, or %NULL
11547 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11549 * Sets the function to use when positioning the search dialog.
11554 pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view,
11555 PsppSheetViewSearchPositionFunc func,
11556 gpointer user_data,
11557 GDestroyNotify destroy)
11559 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11561 if (tree_view->priv->search_position_destroy)
11562 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11564 tree_view->priv->search_position_func = func;
11565 tree_view->priv->search_position_user_data = user_data;
11566 tree_view->priv->search_position_destroy = destroy;
11567 if (tree_view->priv->search_position_func == NULL)
11568 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11572 * pspp_sheet_view_get_search_position_func:
11573 * @tree_view: A #PsppSheetView
11575 * Returns the positioning function currently in use.
11577 * Return value: the currently used function for positioning the search dialog.
11581 PsppSheetViewSearchPositionFunc
11582 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11584 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11586 return tree_view->priv->search_position_func;
11591 pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
11592 PsppSheetView *tree_view)
11594 if (tree_view->priv->disable_popdown)
11597 if (tree_view->priv->search_entry_changed_id)
11599 g_signal_handler_disconnect (tree_view->priv->search_entry,
11600 tree_view->priv->search_entry_changed_id);
11601 tree_view->priv->search_entry_changed_id = 0;
11603 if (tree_view->priv->typeselect_flush_timeout)
11605 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11606 tree_view->priv->typeselect_flush_timeout = 0;
11609 if (gtk_widget_get_visible (search_dialog))
11611 /* send focus-in event */
11612 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11613 gtk_widget_hide (search_dialog);
11614 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11615 send_focus_change (GTK_WIDGET (tree_view), TRUE);
11620 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11621 GtkWidget *search_dialog,
11622 gpointer user_data)
11625 gint tree_x, tree_y;
11626 gint tree_width, tree_height;
11627 GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
11628 GdkScreen *screen = gdk_window_get_screen (tree_window);
11629 GtkRequisition requisition;
11631 GdkRectangle monitor;
11633 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11634 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11636 gtk_widget_realize (search_dialog);
11638 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11639 tree_width = gdk_window_get_width (tree_window);
11640 tree_height = gdk_window_get_height (tree_window);
11642 gtk_widget_size_request (search_dialog, &requisition);
11644 if (tree_x + tree_width > gdk_screen_get_width (screen))
11645 x = gdk_screen_get_width (screen) - requisition.width;
11646 else if (tree_x + tree_width - requisition.width < 0)
11649 x = tree_x + tree_width - requisition.width;
11651 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11652 y = gdk_screen_get_height (screen) - requisition.height;
11653 else if (tree_y + tree_height < 0) /* isn't really possible ... */
11656 y = tree_y + tree_height;
11658 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11662 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11666 PsppSheetView *tree_view = (PsppSheetView *)data;
11668 tree_view->priv->disable_popdown = 1;
11669 g_signal_connect (menu, "hide",
11670 G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11673 #if GTK3_TRANSITION
11674 /* Because we're visible but offscreen, we just set a flag in the preedit
11678 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11679 PsppSheetView *tree_view)
11681 tree_view->priv->imcontext_changed = 1;
11682 if (tree_view->priv->typeselect_flush_timeout)
11684 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11685 tree_view->priv->typeselect_flush_timeout =
11686 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11687 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11695 pspp_sheet_view_search_activate (GtkEntry *entry,
11696 PsppSheetView *tree_view)
11701 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11704 /* If we have a row selected and it's the cursor row, we activate
11706 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11708 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11710 _pspp_sheet_view_find_node (tree_view, path, &node);
11712 if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11713 pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11715 gtk_tree_path_free (path);
11720 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11722 PsppSheetView *tree_view = (PsppSheetView *)data;
11724 tree_view->priv->disable_popdown = 0;
11730 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11733 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11737 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11738 GdkEventAny *event,
11739 PsppSheetView *tree_view)
11741 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11743 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11749 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11750 GdkEventButton *event,
11751 PsppSheetView *tree_view)
11753 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11755 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11757 if (event->window == tree_view->priv->bin_window)
11758 pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
11764 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
11765 GdkEventScroll *event,
11766 PsppSheetView *tree_view)
11768 gboolean retval = FALSE;
11770 if (event->direction == GDK_SCROLL_UP)
11772 pspp_sheet_view_search_move (widget, tree_view, TRUE);
11775 else if (event->direction == GDK_SCROLL_DOWN)
11777 pspp_sheet_view_search_move (widget, tree_view, FALSE);
11781 /* renew the flush timeout */
11782 if (retval && tree_view->priv->typeselect_flush_timeout
11783 && !tree_view->priv->search_custom_entry_set)
11785 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11786 tree_view->priv->typeselect_flush_timeout =
11787 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11788 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11796 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
11797 GdkEventKey *event,
11798 PsppSheetView *tree_view)
11800 gboolean retval = FALSE;
11802 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11803 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11805 /* close window and cancel the search */
11806 if (!tree_view->priv->search_custom_entry_set
11807 && (event->keyval == GDK_Escape ||
11808 event->keyval == GDK_Tab ||
11809 event->keyval == GDK_KP_Tab ||
11810 event->keyval == GDK_ISO_Left_Tab))
11812 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11816 /* select previous matching iter */
11817 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
11819 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11820 gtk_widget_error_bell (widget);
11825 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
11826 && (event->keyval == GDK_g || event->keyval == GDK_G))
11828 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11829 gtk_widget_error_bell (widget);
11834 /* select next matching iter */
11835 if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
11837 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11838 gtk_widget_error_bell (widget);
11843 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
11844 && (event->keyval == GDK_g || event->keyval == GDK_G))
11846 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11847 gtk_widget_error_bell (widget);
11852 /* renew the flush timeout */
11853 if (retval && tree_view->priv->typeselect_flush_timeout
11854 && !tree_view->priv->search_custom_entry_set)
11856 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11857 tree_view->priv->typeselect_flush_timeout =
11858 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11859 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11866 /* this function returns FALSE if there is a search string but
11867 * nothing was found, and TRUE otherwise.
11870 pspp_sheet_view_search_move (GtkWidget *window,
11871 PsppSheetView *tree_view,
11879 GtkTreeModel *model;
11880 PsppSheetSelection *selection;
11882 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
11884 g_return_val_if_fail (text != NULL, FALSE);
11886 len = strlen (text);
11888 if (up && tree_view->priv->selected_iter == 1)
11889 return strlen (text) < 1;
11891 len = strlen (text);
11896 model = pspp_sheet_view_get_model (tree_view);
11897 selection = pspp_sheet_view_get_selection (tree_view);
11900 pspp_sheet_selection_unselect_all (selection);
11901 if (!gtk_tree_model_get_iter_first (model, &iter))
11904 ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
11905 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
11910 tree_view->priv->selected_iter += up?(-1):(1);
11915 /* return to old iter */
11917 gtk_tree_model_get_iter_first (model, &iter);
11918 pspp_sheet_view_search_iter (model, selection,
11920 &count, tree_view->priv->selected_iter);
11926 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
11930 gpointer search_data)
11932 gboolean retval = TRUE;
11934 gchar *normalized_string;
11935 gchar *normalized_key;
11936 gchar *case_normalized_string = NULL;
11937 gchar *case_normalized_key = NULL;
11938 GValue value = {0,};
11939 GValue transformed = {0,};
11941 gtk_tree_model_get_value (model, iter, column, &value);
11943 g_value_init (&transformed, G_TYPE_STRING);
11945 if (!g_value_transform (&value, &transformed))
11947 g_value_unset (&value);
11951 g_value_unset (&value);
11953 str = g_value_get_string (&transformed);
11956 g_value_unset (&transformed);
11960 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
11961 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
11963 if (normalized_string && normalized_key)
11965 case_normalized_string = g_utf8_casefold (normalized_string, -1);
11966 case_normalized_key = g_utf8_casefold (normalized_key, -1);
11968 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
11972 g_value_unset (&transformed);
11973 g_free (normalized_key);
11974 g_free (normalized_string);
11975 g_free (case_normalized_key);
11976 g_free (case_normalized_string);
11982 pspp_sheet_view_search_iter (GtkTreeModel *model,
11983 PsppSheetSelection *selection,
11992 PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
11994 path = gtk_tree_model_get_path (model, iter);
11995 _pspp_sheet_view_find_node (tree_view, path, &node);
11999 gboolean done = FALSE;
12001 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
12006 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
12008 pspp_sheet_selection_select_iter (selection, iter);
12009 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12012 gtk_tree_path_free (path);
12021 node = pspp_sheet_view_node_next (tree_view, node);
12027 has_next = gtk_tree_model_iter_next (model, iter);
12030 gtk_tree_path_next (path);
12033 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12038 gtk_tree_path_free (path);
12040 /* we've run out of tree, done with this func */
12052 pspp_sheet_view_search_init (GtkWidget *entry,
12053 PsppSheetView *tree_view)
12059 GtkTreeModel *model;
12060 PsppSheetSelection *selection;
12062 g_return_if_fail (GTK_IS_ENTRY (entry));
12063 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12065 text = gtk_entry_get_text (GTK_ENTRY (entry));
12067 model = pspp_sheet_view_get_model (tree_view);
12068 selection = pspp_sheet_view_get_selection (tree_view);
12071 pspp_sheet_selection_unselect_all (selection);
12072 if (tree_view->priv->typeselect_flush_timeout
12073 && !tree_view->priv->search_custom_entry_set)
12075 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12076 tree_view->priv->typeselect_flush_timeout =
12077 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12078 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12085 if (!gtk_tree_model_get_iter_first (model, &iter))
12088 ret = pspp_sheet_view_search_iter (model, selection,
12093 tree_view->priv->selected_iter = 1;
12097 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12098 PsppSheetView *tree_view)
12100 if (tree_view->priv->edited_column == NULL)
12103 _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12104 tree_view->priv->edited_column = NULL;
12106 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12107 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12109 g_signal_handlers_disconnect_by_func (cell_editable,
12110 pspp_sheet_view_remove_widget,
12112 g_signal_handlers_disconnect_by_func (cell_editable,
12113 pspp_sheet_view_editable_button_press_event,
12115 g_signal_handlers_disconnect_by_func (cell_editable,
12116 pspp_sheet_view_editable_clicked,
12119 gtk_container_remove (GTK_CONTAINER (tree_view),
12120 GTK_WIDGET (cell_editable));
12122 /* FIXME should only redraw a single node */
12123 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12127 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12128 GtkTreePath *cursor_path)
12131 GdkRectangle background_area;
12132 GdkRectangle cell_area;
12133 GtkCellEditable *editable_widget = NULL;
12134 gchar *path_string;
12135 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12136 gint retval = FALSE;
12139 g_assert (tree_view->priv->focus_column);
12141 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12144 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12145 if (cursor_node < 0)
12148 path_string = gtk_tree_path_to_string (cursor_path);
12149 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12151 pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12152 tree_view->priv->model,
12154 pspp_sheet_view_get_background_area (tree_view,
12156 tree_view->priv->focus_column,
12158 pspp_sheet_view_get_cell_area (tree_view,
12160 tree_view->priv->focus_column,
12163 if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12172 if (editable_widget != NULL)
12176 GtkCellRenderer *cell;
12179 cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12181 _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12184 area.width -= right + left;
12186 pspp_sheet_view_real_start_editing (tree_view,
12187 tree_view->priv->focus_column,
12196 g_free (path_string);
12201 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12202 GdkEventButton *event,
12203 PsppSheetView *sheet_view)
12207 node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12208 "pspp-sheet-view-node"));
12209 return pspp_sheet_view_row_head_clicked (sheet_view,
12211 sheet_view->priv->edited_column,
12216 pspp_sheet_view_editable_clicked (GtkButton *button,
12217 PsppSheetView *sheet_view)
12219 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12224 is_all_selected (GtkWidget *widget)
12226 GtkEntryBuffer *buffer;
12227 gint start_pos, end_pos;
12229 if (!GTK_IS_ENTRY (widget))
12232 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12233 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12234 &start_pos, &end_pos)
12236 && end_pos == gtk_entry_buffer_get_length (buffer));
12240 is_at_left (GtkWidget *widget)
12242 return (GTK_IS_ENTRY (widget)
12243 && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12247 is_at_right (GtkWidget *widget)
12249 GtkEntryBuffer *buffer;
12252 if (!GTK_IS_ENTRY (widget))
12255 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12256 length = gtk_entry_buffer_get_length (buffer);
12257 return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12261 pspp_sheet_view_event (GtkWidget *widget,
12262 GdkEventKey *event,
12263 PsppSheetView *tree_view)
12265 PsppSheetViewColumn *column;
12272 /* Intercept only key press events.
12273 It would make sense to use "key-press-event" instead of "event", but
12274 GtkEntry attaches its own signal handler to "key-press-event" that runs
12275 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12277 if (event->type != GDK_KEY_PRESS)
12280 keyval = event->keyval;
12282 switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12285 switch (event->keyval)
12287 case GDK_Left: case GDK_KP_Left:
12288 case GDK_Home: case GDK_KP_Home:
12289 if (!is_all_selected (widget) && !is_at_left (widget))
12293 case GDK_Right: case GDK_KP_Right:
12294 case GDK_End: case GDK_KP_End:
12295 if (!is_all_selected (widget) && !is_at_right (widget))
12299 case GDK_Up: case GDK_KP_Up:
12300 case GDK_Down: case GDK_KP_Down:
12303 case GDK_Page_Up: case GDK_KP_Page_Up:
12304 case GDK_Page_Down: case GDK_KP_Page_Down:
12315 case GDK_Tab: case GDK_KP_Tab:
12316 case GDK_ISO_Left_Tab:
12325 case GDK_SHIFT_MASK:
12326 switch (event->keyval)
12329 case GDK_ISO_Left_Tab:
12338 case GDK_CONTROL_MASK:
12339 switch (event->keyval)
12341 case GDK_Left: case GDK_KP_Left:
12342 if (!is_all_selected (widget) && !is_at_left (widget))
12346 case GDK_Right: case GDK_KP_Right:
12347 if (!is_all_selected (widget) && !is_at_right (widget))
12351 case GDK_Up: case GDK_KP_Up:
12352 case GDK_Down: case GDK_KP_Down:
12364 row = tree_view->priv->edited_row;
12365 column = tree_view->priv->edited_column;
12366 path = gtk_tree_path_new_from_indices (row, -1);
12368 pspp_sheet_view_stop_editing (tree_view, cancel);
12369 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12371 pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12372 gtk_tree_path_free (path);
12374 handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12375 G_OBJECT (tree_view));
12377 g_signal_stop_emission_by_name (widget, "event");
12379 pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12380 pspp_sheet_view_start_editing (tree_view, path);
12381 gtk_tree_path_free (path);
12387 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12390 PsppSheetView *sheet_view = data;
12392 g_signal_connect (widget, "event",
12393 G_CALLBACK (pspp_sheet_view_event),
12396 if (GTK_IS_CONTAINER (widget))
12397 gtk_container_foreach (GTK_CONTAINER (widget),
12398 pspp_sheet_view_override_cell_keypresses,
12403 pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
12404 PsppSheetViewColumn *column,
12406 GtkCellEditable *cell_editable,
12407 GdkRectangle *cell_area,
12411 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12412 gint pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
12415 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12417 tree_view->priv->edited_column = column;
12418 _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12420 row = gtk_tree_path_get_indices (path)[0];
12421 tree_view->priv->edited_row = row;
12422 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12423 cell_area->y += pre_val - (int)gtk_adjustment_get_value (tree_view->priv->vadjustment);
12425 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12426 pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12427 tree_view->priv->anchor_column = column;
12429 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12431 pspp_sheet_view_put (tree_view,
12432 GTK_WIDGET (cell_editable),
12436 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12437 (GdkEvent *)event);
12439 gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12440 g_signal_connect (cell_editable, "remove-widget",
12441 G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12442 if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12443 GTK_IS_BUTTON (cell_editable))
12445 g_signal_connect (cell_editable, "button-press-event",
12446 G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12448 g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12449 GINT_TO_POINTER (row));
12450 g_signal_connect (cell_editable, "clicked",
12451 G_CALLBACK (pspp_sheet_view_editable_clicked),
12455 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12460 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12461 gboolean cancel_editing)
12463 PsppSheetViewColumn *column;
12464 GtkCellRenderer *cell;
12466 if (tree_view->priv->edited_column == NULL)
12470 * This is very evil. We need to do this, because
12471 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12472 * later on. If pspp_sheet_view_row_changed notices
12473 * tree_view->priv->edited_column != NULL, it'll call
12474 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12476 * Please read that again if you intend to modify anything here.
12479 column = tree_view->priv->edited_column;
12480 tree_view->priv->edited_column = NULL;
12482 cell = _pspp_sheet_view_column_get_edited_cell (column);
12483 gtk_cell_renderer_stop_editing (cell, cancel_editing);
12485 if (!cancel_editing)
12486 gtk_cell_editable_editing_done (column->editable_widget);
12488 tree_view->priv->edited_column = column;
12490 gtk_cell_editable_remove_widget (column->editable_widget);
12495 * pspp_sheet_view_set_hover_selection:
12496 * @tree_view: a #PsppSheetView
12497 * @hover: %TRUE to enable hover selection mode
12499 * Enables of disables the hover selection mode of @tree_view.
12500 * Hover selection makes the selected row follow the pointer.
12501 * Currently, this works only for the selection modes
12502 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12507 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12510 hover = hover != FALSE;
12512 if (hover != tree_view->priv->hover_selection)
12514 tree_view->priv->hover_selection = hover;
12516 g_object_notify (G_OBJECT (tree_view), "hover-selection");
12521 * pspp_sheet_view_get_hover_selection:
12522 * @tree_view: a #PsppSheetView
12524 * Returns whether hover selection mode is turned on for @tree_view.
12526 * Return value: %TRUE if @tree_view is in hover selection mode
12531 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12533 return tree_view->priv->hover_selection;
12537 * pspp_sheet_view_set_rubber_banding:
12538 * @tree_view: a #PsppSheetView
12539 * @enable: %TRUE to enable rubber banding
12541 * Enables or disables rubber banding in @tree_view. If the selection mode is
12542 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12543 * banding will allow the user to select multiple rows by dragging the mouse.
12548 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12551 enable = enable != FALSE;
12553 if (enable != tree_view->priv->rubber_banding_enable)
12555 tree_view->priv->rubber_banding_enable = enable;
12557 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12562 * pspp_sheet_view_get_rubber_banding:
12563 * @tree_view: a #PsppSheetView
12565 * Returns whether rubber banding is turned on for @tree_view. If the
12566 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12567 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12568 * select multiple rows by dragging the mouse.
12570 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12575 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12577 return tree_view->priv->rubber_banding_enable;
12581 * pspp_sheet_view_is_rubber_banding_active:
12582 * @tree_view: a #PsppSheetView
12584 * Returns whether a rubber banding operation is currently being done
12587 * Return value: %TRUE if a rubber banding operation is currently being
12588 * done in @tree_view.
12593 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12595 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12597 if (tree_view->priv->rubber_banding_enable
12598 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12605 pspp_sheet_view_grab_notify (GtkWidget *widget,
12606 gboolean was_grabbed)
12608 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12610 tree_view->priv->in_grab = !was_grabbed;
12614 tree_view->priv->pressed_button = -1;
12616 if (tree_view->priv->rubber_band_status)
12617 pspp_sheet_view_stop_rubber_band (tree_view);
12622 pspp_sheet_view_state_changed (GtkWidget *widget,
12623 GtkStateType previous_state)
12625 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12627 if (gtk_widget_get_realized (widget))
12629 GtkStyle *style = gtk_widget_get_style (widget);
12630 gdk_window_set_background (tree_view->priv->bin_window, &style->base[gtk_widget_get_state (widget)]);
12633 gtk_widget_queue_draw (widget);
12637 * pspp_sheet_view_get_grid_lines:
12638 * @tree_view: a #PsppSheetView
12640 * Returns which grid lines are enabled in @tree_view.
12642 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12647 PsppSheetViewGridLines
12648 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12650 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12652 return tree_view->priv->grid_lines;
12656 * pspp_sheet_view_set_grid_lines:
12657 * @tree_view: a #PsppSheetView
12658 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12661 * Sets which grid lines to draw in @tree_view.
12666 pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view,
12667 PsppSheetViewGridLines grid_lines)
12669 PsppSheetViewPrivate *priv;
12670 PsppSheetViewGridLines old_grid_lines;
12672 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12674 priv = tree_view->priv;
12676 old_grid_lines = priv->grid_lines;
12677 priv->grid_lines = grid_lines;
12679 if (old_grid_lines != grid_lines)
12681 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12683 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12688 * pspp_sheet_view_get_special_cells:
12689 * @tree_view: a #PsppSheetView
12691 * Returns which grid lines are enabled in @tree_view.
12693 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12694 * the sheet view contain special cells.
12696 PsppSheetViewSpecialCells
12697 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12699 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12701 return tree_view->priv->special_cells;
12705 * pspp_sheet_view_set_special_cells:
12706 * @tree_view: a #PsppSheetView
12707 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12708 * the sheet view contain special cells.
12710 * Sets whether rows in the sheet view contain special cells, controlling the
12711 * rendering of row selections.
12714 pspp_sheet_view_set_special_cells (PsppSheetView *tree_view,
12715 PsppSheetViewSpecialCells special_cells)
12717 PsppSheetViewPrivate *priv;
12719 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12721 priv = tree_view->priv;
12723 if (priv->special_cells != special_cells)
12725 priv->special_cells = special_cells;
12726 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12727 g_object_notify (G_OBJECT (tree_view), "special-cells");
12732 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12734 /* XXX (re)calculate fixed_height if necessary */
12735 return tree_view->priv->fixed_height;
12739 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
12742 g_return_if_fail (fixed_height > 0);
12744 if (tree_view->priv->fixed_height != fixed_height)
12746 tree_view->priv->fixed_height = fixed_height;
12747 g_object_notify (G_OBJECT (tree_view), "fixed-height");
12749 if (!tree_view->priv->fixed_height_set)
12751 tree_view->priv->fixed_height_set = TRUE;
12752 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
12757 * pspp_sheet_view_set_tooltip_row:
12758 * @tree_view: a #PsppSheetView
12759 * @tooltip: a #GtkTooltip
12760 * @path: a #GtkTreePath
12762 * Sets the tip area of @tooltip to be the area covered by the row at @path.
12763 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12764 * See also gtk_tooltip_set_tip_area().
12769 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
12770 GtkTooltip *tooltip,
12773 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12774 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12776 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
12780 * pspp_sheet_view_set_tooltip_cell:
12781 * @tree_view: a #PsppSheetView
12782 * @tooltip: a #GtkTooltip
12783 * @path: (allow-none): a #GtkTreePath or %NULL
12784 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
12785 * @cell: (allow-none): a #GtkCellRenderer or %NULL
12787 * Sets the tip area of @tooltip to the area @path, @column and @cell have
12788 * in common. For example if @path is %NULL and @column is set, the tip
12789 * area will be set to the full area covered by @column. See also
12790 * gtk_tooltip_set_tip_area().
12792 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12797 pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view,
12798 GtkTooltip *tooltip,
12800 PsppSheetViewColumn *column,
12801 GtkCellRenderer *cell)
12805 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12806 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12807 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
12808 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
12810 /* Determine x values. */
12811 if (column && cell)
12816 pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
12817 pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
12819 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12822 rect.width = width;
12828 pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
12829 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12832 rect.width = tmp.width;
12836 GtkAllocation allocation;
12837 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
12839 rect.width = allocation.width;
12842 /* Determine y values. */
12847 pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
12848 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12851 rect.height = tmp.height;
12856 rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
12859 gtk_tooltip_set_tip_area (tooltip, &rect);
12863 * pspp_sheet_view_get_tooltip_context:
12864 * @tree_view: a #PsppSheetView
12865 * @x: the x coordinate (relative to widget coordinates)
12866 * @y: the y coordinate (relative to widget coordinates)
12867 * @keyboard_tip: whether this is a keyboard tooltip or not
12868 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
12869 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
12870 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
12872 * This function is supposed to be used in a #GtkWidget::query-tooltip
12873 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
12874 * which are received in the signal handler, should be passed to this
12875 * function without modification.
12877 * The return value indicates whether there is a tree view row at the given
12878 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
12879 * tooltips the row returned will be the cursor row. When %TRUE, then any of
12880 * @model, @path and @iter which have been provided will be set to point to
12881 * that row and the corresponding model. @x and @y will always be converted
12882 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
12884 * Return value: whether or not the given tooltip context points to a row.
12889 pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view,
12892 gboolean keyboard_tip,
12893 GtkTreeModel **model,
12894 GtkTreePath **path,
12897 GtkTreePath *tmppath = NULL;
12899 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12900 g_return_val_if_fail (x != NULL, FALSE);
12901 g_return_val_if_fail (y != NULL, FALSE);
12905 pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
12912 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
12915 if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
12916 &tmppath, NULL, NULL, NULL))
12921 *model = pspp_sheet_view_get_model (tree_view);
12924 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
12930 gtk_tree_path_free (tmppath);
12936 pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget,
12939 gboolean keyboard_tip,
12940 GtkTooltip *tooltip,
12943 GValue value = { 0, };
12944 GValue transformed = { 0, };
12947 GtkTreeModel *model;
12948 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12950 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
12953 &model, &path, &iter))
12956 gtk_tree_model_get_value (model, &iter,
12957 tree_view->priv->tooltip_column, &value);
12959 g_value_init (&transformed, G_TYPE_STRING);
12961 if (!g_value_transform (&value, &transformed))
12963 g_value_unset (&value);
12964 gtk_tree_path_free (path);
12969 g_value_unset (&value);
12971 if (!g_value_get_string (&transformed))
12973 g_value_unset (&transformed);
12974 gtk_tree_path_free (path);
12979 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
12980 pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
12982 gtk_tree_path_free (path);
12983 g_value_unset (&transformed);
12989 * pspp_sheet_view_set_tooltip_column:
12990 * @tree_view: a #PsppSheetView
12991 * @column: an integer, which is a valid column number for @tree_view's model
12993 * If you only plan to have simple (text-only) tooltips on full rows, you
12994 * can use this function to have #PsppSheetView handle these automatically
12995 * for you. @column should be set to the column in @tree_view's model
12996 * containing the tooltip texts, or -1 to disable this feature.
12998 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
12999 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
13001 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
13002 * so &, <, etc have to be escaped in the text.
13007 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
13010 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13012 if (column == tree_view->priv->tooltip_column)
13017 g_signal_handlers_disconnect_by_func (tree_view,
13018 pspp_sheet_view_set_tooltip_query_cb,
13020 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13024 if (tree_view->priv->tooltip_column == -1)
13026 g_signal_connect (tree_view, "query-tooltip",
13027 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13028 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13032 tree_view->priv->tooltip_column = column;
13033 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13037 * pspp_sheet_view_get_tooltip_column:
13038 * @tree_view: a #PsppSheetView
13040 * Returns the column of @tree_view's model which is being used for
13041 * displaying tooltips on @tree_view's rows.
13043 * Return value: the index of the tooltip column that is currently being
13044 * used, or -1 if this is disabled.
13049 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13051 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13053 return tree_view->priv->tooltip_column;
13057 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13058 GValue *return_accu,
13059 const GValue *handler_return,
13062 gboolean continue_emission;
13063 gboolean signal_handled;
13065 signal_handled = g_value_get_boolean (handler_return);
13066 g_value_set_boolean (return_accu, signal_handled);
13067 continue_emission = !signal_handled;
13069 return continue_emission;
13074 pspp_sheet_view_grid_lines_get_type (void)
13076 static GType etype = 0;
13077 if (G_UNLIKELY(etype == 0)) {
13078 static const GEnumValue values[] = {
13079 { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13080 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13081 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13082 { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13085 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13091 pspp_sheet_view_special_cells_get_type (void)
13093 static GType etype = 0;
13094 if (G_UNLIKELY(etype == 0)) {
13095 static const GEnumValue values[] = {
13096 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13097 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13098 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13101 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);