1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012, 2013, 2015 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Library General Public
22 * License as published by the Free Software Foundation; either
23 * version 2 of the License, or (at your option) any later version.
25 * This library is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Library General Public License for more details.
30 * You should have received a copy of the GNU Library General Public
31 * License along with this library; if not, write to the
32 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
33 * Boston, MA 02111-1307, USA.
38 #include "ui/gui/pspp-sheet-private.h"
42 #include <gdk/gdkkeysyms.h>
43 #include <gdk/gdkkeysyms-compat.h>
46 #include "ui/gui/psppire-marshal.h"
47 #include "ui/gui/pspp-sheet-selection.h"
49 #define P_(STRING) STRING
50 #define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
51 #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
53 /* Many keyboard shortcuts for Mac are the same as for X
54 * except they use Command key instead of Control (e.g. Cut,
55 * Copy, Paste). This symbol is for those simple cases. */
56 #ifndef GDK_WINDOWING_QUARTZ
57 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_CONTROL_MASK
59 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_META_MASK
62 #define PSPP_SHEET_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
63 #define PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC (PSPP_SHEET_VIEW_PRIORITY_VALIDATE + 2)
64 #define PSPP_SHEET_VIEW_TIME_MS_PER_IDLE 30
65 #define SCROLL_EDGE_SIZE 15
66 #define EXPANDER_EXTRA_PADDING 4
67 #define PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT 5000
69 /* The "background" areas of all rows/cells add up to cover the entire tree.
70 * The background includes all inter-row and inter-cell spacing.
71 * The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
72 * i.e. just the cells, no spacing.
75 #define BACKGROUND_HEIGHT(tree_view) (tree_view->priv->fixed_height)
76 #define CELL_HEIGHT(tree_view, separator) ((BACKGROUND_HEIGHT (tree_view)) - (separator))
78 /* Translate from bin_window coordinates to rbtree (tree coordinates) and
81 #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->priv->dy)
82 #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->priv->dy)
84 /* This is in bin_window coordinates */
85 #define BACKGROUND_FIRST_PIXEL(tree_view,node) (RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, pspp_sheet_view_node_find_offset (tree_view, (node))))
86 #define CELL_FIRST_PIXEL(tree_view,node,separator) (BACKGROUND_FIRST_PIXEL (tree_view,node) + separator/2)
88 #define ROW_HEIGHT(tree_view) \
89 ((tree_view->priv->fixed_height > 0) ? (tree_view->priv->fixed_height) : (tree_view)->priv->expander_size)
92 typedef struct _PsppSheetViewChild PsppSheetViewChild;
93 struct _PsppSheetViewChild
96 PsppSheetViewColumn *column;
101 typedef struct _TreeViewDragInfo TreeViewDragInfo;
102 struct _TreeViewDragInfo
104 GdkModifierType start_button_mask;
105 GtkTargetList *_unused_source_target_list;
106 GdkDragAction source_actions;
108 GtkTargetList *_unused_dest_target_list;
110 guint source_set : 1;
126 START_INTERACTIVE_SEARCH,
138 PROP_HEADERS_VISIBLE,
139 PROP_HEADERS_CLICKABLE,
144 PROP_HOVER_SELECTION,
146 PROP_ENABLE_GRID_LINES,
150 PROP_FIXED_HEIGHT_SET
154 static void pspp_sheet_view_finalize (GObject *object);
155 static void pspp_sheet_view_set_property (GObject *object,
159 static void pspp_sheet_view_get_property (GObject *object,
164 static void pspp_sheet_view_dispose (GObject *object);
166 /* gtkwidget signals */
167 static void pspp_sheet_view_realize (GtkWidget *widget);
168 static void pspp_sheet_view_unrealize (GtkWidget *widget);
169 static void pspp_sheet_view_map (GtkWidget *widget);
170 static void pspp_sheet_view_size_request (GtkWidget *widget,
171 GtkRequisition *requisition);
172 static void pspp_sheet_view_size_allocate (GtkWidget *widget,
173 GtkAllocation *allocation);
174 static gboolean pspp_sheet_view_draw (GtkWidget *widget,
176 static gboolean pspp_sheet_view_key_press (GtkWidget *widget,
178 static gboolean pspp_sheet_view_key_release (GtkWidget *widget,
180 static gboolean pspp_sheet_view_motion (GtkWidget *widget,
181 GdkEventMotion *event);
182 static gboolean pspp_sheet_view_enter_notify (GtkWidget *widget,
183 GdkEventCrossing *event);
184 static gboolean pspp_sheet_view_leave_notify (GtkWidget *widget,
185 GdkEventCrossing *event);
186 static gboolean pspp_sheet_view_button_press (GtkWidget *widget,
187 GdkEventButton *event);
188 static gboolean pspp_sheet_view_button_release (GtkWidget *widget,
189 GdkEventButton *event);
190 static gboolean pspp_sheet_view_grab_broken (GtkWidget *widget,
191 GdkEventGrabBroken *event);
193 static void pspp_sheet_view_set_focus_child (GtkContainer *container,
195 static gint pspp_sheet_view_focus_out (GtkWidget *widget,
196 GdkEventFocus *event);
197 static gint pspp_sheet_view_focus (GtkWidget *widget,
198 GtkDirectionType direction);
199 static void pspp_sheet_view_grab_focus (GtkWidget *widget);
200 static void pspp_sheet_view_style_updated (GtkWidget *widget);
201 static void pspp_sheet_view_grab_notify (GtkWidget *widget,
202 gboolean was_grabbed);
203 static void pspp_sheet_view_state_changed (GtkWidget *widget,
204 GtkStateType previous_state);
206 /* container signals */
207 static void pspp_sheet_view_remove (GtkContainer *container,
209 static void pspp_sheet_view_forall (GtkContainer *container,
210 gboolean include_internals,
211 GtkCallback callback,
212 gpointer callback_data);
214 /* Source side drag signals */
215 static void pspp_sheet_view_drag_begin (GtkWidget *widget,
216 GdkDragContext *context);
217 static void pspp_sheet_view_drag_end (GtkWidget *widget,
218 GdkDragContext *context);
219 static void pspp_sheet_view_drag_data_get (GtkWidget *widget,
220 GdkDragContext *context,
221 GtkSelectionData *selection_data,
224 static void pspp_sheet_view_drag_data_delete (GtkWidget *widget,
225 GdkDragContext *context);
227 /* Target side drag signals */
228 static void pspp_sheet_view_drag_leave (GtkWidget *widget,
229 GdkDragContext *context,
231 static gboolean pspp_sheet_view_drag_motion (GtkWidget *widget,
232 GdkDragContext *context,
236 static gboolean pspp_sheet_view_drag_drop (GtkWidget *widget,
237 GdkDragContext *context,
241 static void pspp_sheet_view_drag_data_received (GtkWidget *widget,
242 GdkDragContext *context,
245 GtkSelectionData *selection_data,
249 /* tree_model signals */
250 static void pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
252 GtkAdjustment *vadj);
253 static gboolean pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
254 GtkMovementStep step,
256 static gboolean pspp_sheet_view_real_select_all (PsppSheetView *tree_view);
257 static gboolean pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view);
258 static gboolean pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
259 gboolean start_editing,
260 PsppSheetSelectMode mode);
261 static gboolean pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view);
262 static void pspp_sheet_view_row_changed (GtkTreeModel *model,
266 static void pspp_sheet_view_row_inserted (GtkTreeModel *model,
270 static void pspp_sheet_view_row_deleted (GtkTreeModel *model,
273 static void pspp_sheet_view_rows_reordered (GtkTreeModel *model,
279 /* Incremental reflow */
280 static gint validate_row (PsppSheetView *tree_view,
284 static void validate_visible_area (PsppSheetView *tree_view);
285 static gboolean validate_rows_handler (PsppSheetView *tree_view);
286 static gboolean presize_handler_callback (gpointer data);
287 static void install_presize_handler (PsppSheetView *tree_view);
288 static void install_scroll_sync_handler (PsppSheetView *tree_view);
289 static void pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
292 static void pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view);
293 static void pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view);
294 static void invalidate_empty_focus (PsppSheetView *tree_view);
296 /* Internal functions */
297 static GtkAdjustment *pspp_sheet_view_do_get_hadjustment (PsppSheetView *);
298 static GtkAdjustment *pspp_sheet_view_do_get_vadjustment (PsppSheetView *);
299 static void pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
300 GtkAdjustment *adjustment);
301 static void pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
302 GtkAdjustment *adjustment);
303 static void pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
306 gboolean add_shifted_binding,
307 GtkMovementStep step,
309 static void pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
311 const GdkRectangle *clip_rect);
312 static gint pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
315 static void pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
316 PsppSheetView *tree_view);
317 static void pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
319 static void pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
320 PsppSheetViewColumn *column,
321 gboolean focus_to_cell);
322 static gboolean pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
323 GdkEventMotion *event);
324 static void pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view);
325 static gboolean pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
327 PsppSheetSelectMode mode);
328 static void pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
330 PsppSheetSelectMode mode);
331 static void pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
333 PsppSheetSelectMode mode);
334 static void pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
336 PsppSheetSelectMode mode);
337 static void pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
339 static void pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
341 PsppSheetSelectMode mode);
342 static void pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
344 gboolean clear_and_select,
346 PsppSheetSelectMode mode);
347 static gboolean pspp_sheet_view_has_special_cell (PsppSheetView *tree_view);
348 static void pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view);
349 static void update_prelight (PsppSheetView *tree_view,
352 static void initialize_fixed_height_mode (PsppSheetView *tree_view);
354 /* interactive search */
355 static void pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view);
356 static void pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
357 PsppSheetView *tree_view);
358 static void pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
359 GtkWidget *search_dialog,
361 static void pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
365 static void pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
366 PsppSheetView *tree_view);
368 static void pspp_sheet_view_search_activate (GtkEntry *entry,
369 PsppSheetView *tree_view);
370 static gboolean pspp_sheet_view_real_search_enable_popdown(gpointer data);
371 static void pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
373 static gboolean pspp_sheet_view_search_delete_event (GtkWidget *widget,
375 PsppSheetView *tree_view);
376 static gboolean pspp_sheet_view_search_button_press_event (GtkWidget *widget,
377 GdkEventButton *event,
378 PsppSheetView *tree_view);
379 static gboolean pspp_sheet_view_search_scroll_event (GtkWidget *entry,
380 GdkEventScroll *event,
381 PsppSheetView *tree_view);
382 static gboolean pspp_sheet_view_search_key_press_event (GtkWidget *entry,
384 PsppSheetView *tree_view);
385 static gboolean pspp_sheet_view_search_move (GtkWidget *window,
386 PsppSheetView *tree_view,
388 static gboolean pspp_sheet_view_search_equal_func (GtkTreeModel *model,
392 gpointer search_data);
393 static gboolean pspp_sheet_view_search_iter (GtkTreeModel *model,
394 PsppSheetSelection *selection,
399 static void pspp_sheet_view_search_init (GtkWidget *entry,
400 PsppSheetView *tree_view);
401 static void pspp_sheet_view_put (PsppSheetView *tree_view,
402 GtkWidget *child_widget,
404 PsppSheetViewColumn *column);
405 static gboolean pspp_sheet_view_start_editing (PsppSheetView *tree_view,
406 GtkTreePath *cursor_path);
407 static gboolean pspp_sheet_view_editable_button_press_event (GtkWidget *,
410 static void pspp_sheet_view_editable_clicked (GtkButton *, PsppSheetView *);
411 static void pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
412 PsppSheetViewColumn *column,
414 GtkCellEditable *cell_editable,
415 GdkRectangle *cell_area,
418 static gboolean pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
419 gboolean keybinding);
420 static gboolean pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view);
421 static PsppSheetViewColumn *pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
422 PsppSheetViewColumn *column,
425 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
426 PsppSheetViewColumn *column,
427 const GdkRectangle *background_area,
428 gboolean subtract_focus_rect,
429 GdkRectangle *cell_area);
430 static gint pspp_sheet_view_find_offset (PsppSheetView *tree_view,
435 static void pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
439 static void pspp_sheet_view_buildable_init (GtkBuildableIface *iface);
442 static gboolean scroll_row_timeout (gpointer data);
443 static void add_scroll_timeout (PsppSheetView *tree_view);
444 static void remove_scroll_timeout (PsppSheetView *tree_view);
446 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
448 static GtkBindingSet *edit_bindings;
455 G_DEFINE_TYPE_WITH_CODE (PsppSheetView, pspp_sheet_view, GTK_TYPE_CONTAINER,
456 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
457 pspp_sheet_view_buildable_init)
458 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
461 pspp_sheet_view_get_preferred_width (GtkWidget *widget,
465 GtkRequisition requisition;
467 pspp_sheet_view_size_request (widget, &requisition);
469 *minimal_width = *natural_width = requisition.width;
473 pspp_sheet_view_get_preferred_height (GtkWidget *widget,
474 gint *minimal_height,
475 gint *natural_height)
477 GtkRequisition requisition;
479 pspp_sheet_view_size_request (widget, &requisition);
481 *minimal_height = *natural_height = requisition.height;
485 pspp_sheet_view_class_init (PsppSheetViewClass *class)
487 GObjectClass *o_class;
488 GtkWidgetClass *widget_class;
489 GtkContainerClass *container_class;
490 GtkBindingSet *binding_set[2];
493 binding_set[0] = gtk_binding_set_by_class (class);
495 binding_set[1] = gtk_binding_set_new ("PsppSheetViewEditing");
496 edit_bindings = binding_set[1];
498 o_class = (GObjectClass *) class;
499 widget_class = (GtkWidgetClass *) class;
500 container_class = (GtkContainerClass *) class;
502 /* GObject signals */
503 o_class->set_property = pspp_sheet_view_set_property;
504 o_class->get_property = pspp_sheet_view_get_property;
505 o_class->finalize = pspp_sheet_view_finalize;
506 o_class->dispose = pspp_sheet_view_dispose;
508 /* GtkWidget signals */
509 widget_class->map = pspp_sheet_view_map;
510 widget_class->realize = pspp_sheet_view_realize;
511 widget_class->unrealize = pspp_sheet_view_unrealize;
512 widget_class->get_preferred_width = pspp_sheet_view_get_preferred_width;
513 widget_class->get_preferred_height = pspp_sheet_view_get_preferred_height;
514 widget_class->size_allocate = pspp_sheet_view_size_allocate;
515 widget_class->button_press_event = pspp_sheet_view_button_press;
516 widget_class->button_release_event = pspp_sheet_view_button_release;
517 widget_class->grab_broken_event = pspp_sheet_view_grab_broken;
518 /*widget_class->configure_event = pspp_sheet_view_configure;*/
519 widget_class->motion_notify_event = pspp_sheet_view_motion;
520 widget_class->draw = pspp_sheet_view_draw;
521 widget_class->key_press_event = pspp_sheet_view_key_press;
522 widget_class->key_release_event = pspp_sheet_view_key_release;
523 widget_class->enter_notify_event = pspp_sheet_view_enter_notify;
524 widget_class->leave_notify_event = pspp_sheet_view_leave_notify;
525 widget_class->focus_out_event = pspp_sheet_view_focus_out;
526 widget_class->drag_begin = pspp_sheet_view_drag_begin;
527 widget_class->drag_end = pspp_sheet_view_drag_end;
528 widget_class->drag_data_get = pspp_sheet_view_drag_data_get;
529 widget_class->drag_data_delete = pspp_sheet_view_drag_data_delete;
530 widget_class->drag_leave = pspp_sheet_view_drag_leave;
531 widget_class->drag_motion = pspp_sheet_view_drag_motion;
532 widget_class->drag_drop = pspp_sheet_view_drag_drop;
533 widget_class->drag_data_received = pspp_sheet_view_drag_data_received;
534 widget_class->focus = pspp_sheet_view_focus;
535 widget_class->grab_focus = pspp_sheet_view_grab_focus;
536 widget_class->style_updated = pspp_sheet_view_style_updated;
537 widget_class->grab_notify = pspp_sheet_view_grab_notify;
538 widget_class->state_changed = pspp_sheet_view_state_changed;
540 /* GtkContainer signals */
541 container_class->remove = pspp_sheet_view_remove;
542 container_class->forall = pspp_sheet_view_forall;
543 container_class->set_focus_child = pspp_sheet_view_set_focus_child;
545 class->set_scroll_adjustments = pspp_sheet_view_set_adjustments;
546 class->move_cursor = pspp_sheet_view_real_move_cursor;
547 class->select_all = pspp_sheet_view_real_select_all;
548 class->unselect_all = pspp_sheet_view_real_unselect_all;
549 class->select_cursor_row = pspp_sheet_view_real_select_cursor_row;
550 class->toggle_cursor_row = pspp_sheet_view_real_toggle_cursor_row;
551 class->start_interactive_search = pspp_sheet_view_start_interactive_search;
555 g_object_class_install_property (o_class,
557 g_param_spec_object ("model",
558 P_("TreeView Model"),
559 P_("The model for the tree view"),
561 GTK_PARAM_READWRITE));
563 g_object_class_override_property (o_class, PROP_HADJUSTMENT, "hadjustment");
564 g_object_class_override_property (o_class, PROP_VADJUSTMENT, "vadjustment");
565 g_object_class_override_property (o_class, PROP_HSCROLL_POLICY, "hscroll-policy");
566 g_object_class_override_property (o_class, PROP_VSCROLL_POLICY, "vscroll-policy");
568 g_object_class_install_property (o_class,
569 PROP_HEADERS_VISIBLE,
570 g_param_spec_boolean ("headers-visible",
571 P_("Headers Visible"),
572 P_("Show the column header buttons"),
574 GTK_PARAM_READWRITE));
576 g_object_class_install_property (o_class,
577 PROP_HEADERS_CLICKABLE,
578 g_param_spec_boolean ("headers-clickable",
579 P_("Headers Clickable"),
580 P_("Column headers respond to click events"),
582 GTK_PARAM_READWRITE));
584 g_object_class_install_property (o_class,
586 g_param_spec_boolean ("reorderable",
588 P_("View is reorderable"),
590 GTK_PARAM_READWRITE));
592 g_object_class_install_property (o_class,
594 g_param_spec_boolean ("rules-hint",
596 P_("Set a hint to the theme engine to draw rows in alternating colors"),
598 GTK_PARAM_READWRITE));
600 g_object_class_install_property (o_class,
602 g_param_spec_boolean ("enable-search",
604 P_("View allows user to search through columns interactively"),
606 GTK_PARAM_READWRITE));
608 g_object_class_install_property (o_class,
610 g_param_spec_int ("search-column",
612 P_("Model column to search through during interactive search"),
616 GTK_PARAM_READWRITE));
619 * PsppSheetView:hover-selection:
621 * Enables of disables the hover selection mode of @tree_view.
622 * Hover selection makes the selected row follow the pointer.
623 * Currently, this works only for the selection modes
624 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
626 * This mode is primarily intended for treeviews in popups, e.g.
627 * in #GtkComboBox or #GtkEntryCompletion.
631 g_object_class_install_property (o_class,
632 PROP_HOVER_SELECTION,
633 g_param_spec_boolean ("hover-selection",
634 P_("Hover Selection"),
635 P_("Whether the selection should follow the pointer"),
637 GTK_PARAM_READWRITE));
639 g_object_class_install_property (o_class,
641 g_param_spec_boolean ("rubber-banding",
642 P_("Rubber Banding"),
643 P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
645 GTK_PARAM_READWRITE));
647 g_object_class_install_property (o_class,
648 PROP_ENABLE_GRID_LINES,
649 g_param_spec_enum ("enable-grid-lines",
650 P_("Enable Grid Lines"),
651 P_("Whether grid lines should be drawn in the tree view"),
652 PSPP_TYPE_SHEET_VIEW_GRID_LINES,
653 PSPP_SHEET_VIEW_GRID_LINES_NONE,
654 GTK_PARAM_READWRITE));
656 g_object_class_install_property (o_class,
658 g_param_spec_int ("tooltip-column",
659 P_("Tooltip Column"),
660 P_("The column in the model containing the tooltip texts for the rows"),
664 GTK_PARAM_READWRITE));
666 g_object_class_install_property (o_class,
668 g_param_spec_enum ("special-cells",
670 P_("Whether rows have special cells."),
671 PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS,
672 PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT,
673 GTK_PARAM_READWRITE));
675 g_object_class_install_property (o_class,
677 g_param_spec_int ("fixed-height",
679 P_("Height of a single row. Normally the height of a row is determined automatically. Writing this property sets fixed-height-set to true, preventing this property's value from changing."),
683 GTK_PARAM_READWRITE));
685 g_object_class_install_property (o_class,
686 PROP_FIXED_HEIGHT_SET,
687 g_param_spec_boolean ("fixed-height-set",
688 P_("Fixed Height Set"),
689 P_("Whether fixed-height was set externally."),
691 GTK_PARAM_READWRITE));
693 /* Style properties */
694 #define _TREE_VIEW_EXPANDER_SIZE 12
695 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
696 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
698 gtk_widget_class_install_style_property (widget_class,
699 g_param_spec_int ("expander-size",
701 P_("Size of the expander arrow"),
704 _TREE_VIEW_EXPANDER_SIZE,
705 GTK_PARAM_READABLE));
707 gtk_widget_class_install_style_property (widget_class,
708 g_param_spec_int ("vertical-separator",
709 P_("Vertical Separator Width"),
710 P_("Vertical space between cells. Must be an even number"),
713 _TREE_VIEW_VERTICAL_SEPARATOR,
714 GTK_PARAM_READABLE));
716 gtk_widget_class_install_style_property (widget_class,
717 g_param_spec_int ("horizontal-separator",
718 P_("Horizontal Separator Width"),
719 P_("Horizontal space between cells. Must be an even number"),
722 _TREE_VIEW_HORIZONTAL_SEPARATOR,
723 GTK_PARAM_READABLE));
725 gtk_widget_class_install_style_property (widget_class,
726 g_param_spec_boolean ("allow-rules",
728 P_("Allow drawing of alternating color rows"),
730 GTK_PARAM_READABLE));
732 gtk_widget_class_install_style_property (widget_class,
733 g_param_spec_boxed ("even-row-color",
734 P_("Even Row Color"),
735 P_("Color to use for even rows"),
737 GTK_PARAM_READABLE));
739 gtk_widget_class_install_style_property (widget_class,
740 g_param_spec_boxed ("odd-row-color",
742 P_("Color to use for odd rows"),
744 GTK_PARAM_READABLE));
746 gtk_widget_class_install_style_property (widget_class,
747 g_param_spec_boolean ("row-ending-details",
748 P_("Row Ending details"),
749 P_("Enable extended row background theming"),
751 GTK_PARAM_READABLE));
753 gtk_widget_class_install_style_property (widget_class,
754 g_param_spec_int ("grid-line-width",
755 P_("Grid line width"),
756 P_("Width, in pixels, of the tree view grid lines"),
758 GTK_PARAM_READABLE));
760 gtk_widget_class_install_style_property (widget_class,
761 g_param_spec_int ("tree-line-width",
762 P_("Tree line width"),
763 P_("Width, in pixels, of the tree view lines"),
765 GTK_PARAM_READABLE));
767 gtk_widget_class_install_style_property (widget_class,
768 g_param_spec_string ("tree-line-pattern",
769 P_("Tree line pattern"),
770 P_("Dash pattern used to draw the tree view lines"),
772 GTK_PARAM_READABLE));
777 * PsppSheetView::set-scroll-adjustments
778 * @horizontal: the horizontal #GtkAdjustment
779 * @vertical: the vertical #GtkAdjustment
781 * Set the scroll adjustments for the tree view. Usually scrolled containers
782 * like #GtkScrolledWindow will emit this signal to connect two instances
783 * of #GtkScrollbar to the scroll directions of the #PsppSheetView.
785 widget_class->set_scroll_adjustments_signal =
786 g_signal_new ("set-scroll-adjustments",
787 G_TYPE_FROM_CLASS (o_class),
788 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
789 G_STRUCT_OFFSET (PsppSheetViewClass, set_scroll_adjustments),
791 psppire_marshal_VOID__OBJECT_OBJECT,
794 GTK_TYPE_ADJUSTMENT);
798 * PsppSheetView::row-activated:
799 * @tree_view: the object on which the signal is emitted
800 * @path: the #GtkTreePath for the activated row
801 * @column: the #PsppSheetViewColumn in which the activation occurred
803 * The "row-activated" signal is emitted when the method
804 * pspp_sheet_view_row_activated() is called or the user double clicks
805 * a treeview row. It is also emitted when a non-editable row is
806 * selected and one of the keys: Space, Shift+Space, Return or
809 * For selection handling refer to the <link linkend="TreeWidget">tree
810 * widget conceptual overview</link> as well as #PsppSheetSelection.
812 tree_view_signals[ROW_ACTIVATED] =
813 g_signal_new ("row-activated",
814 G_TYPE_FROM_CLASS (o_class),
815 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
816 G_STRUCT_OFFSET (PsppSheetViewClass, row_activated),
818 psppire_marshal_VOID__BOXED_OBJECT,
821 PSPP_TYPE_SHEET_VIEW_COLUMN);
824 * PsppSheetView::columns-changed:
825 * @tree_view: the object on which the signal is emitted
827 * The number of columns of the treeview has changed.
829 tree_view_signals[COLUMNS_CHANGED] =
830 g_signal_new ("columns-changed",
831 G_TYPE_FROM_CLASS (o_class),
833 G_STRUCT_OFFSET (PsppSheetViewClass, columns_changed),
835 g_cclosure_marshal_VOID__VOID,
839 * PsppSheetView::cursor-changed:
840 * @tree_view: the object on which the signal is emitted
842 * The position of the cursor (focused cell) has changed.
844 tree_view_signals[CURSOR_CHANGED] =
845 g_signal_new ("cursor-changed",
846 G_TYPE_FROM_CLASS (o_class),
848 G_STRUCT_OFFSET (PsppSheetViewClass, cursor_changed),
850 g_cclosure_marshal_VOID__VOID,
853 tree_view_signals[MOVE_CURSOR] =
854 g_signal_new ("move-cursor",
855 G_TYPE_FROM_CLASS (o_class),
856 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
857 G_STRUCT_OFFSET (PsppSheetViewClass, move_cursor),
859 psppire_marshal_BOOLEAN__ENUM_INT,
861 GTK_TYPE_MOVEMENT_STEP,
864 tree_view_signals[SELECT_ALL] =
865 g_signal_new ("select-all",
866 G_TYPE_FROM_CLASS (o_class),
867 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
868 G_STRUCT_OFFSET (PsppSheetViewClass, select_all),
870 psppire_marshal_BOOLEAN__VOID,
873 tree_view_signals[UNSELECT_ALL] =
874 g_signal_new ("unselect-all",
875 G_TYPE_FROM_CLASS (o_class),
876 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
877 G_STRUCT_OFFSET (PsppSheetViewClass, unselect_all),
879 psppire_marshal_BOOLEAN__VOID,
882 tree_view_signals[SELECT_CURSOR_ROW] =
883 g_signal_new ("select-cursor-row",
884 G_TYPE_FROM_CLASS (o_class),
885 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
886 G_STRUCT_OFFSET (PsppSheetViewClass, select_cursor_row),
888 psppire_marshal_BOOLEAN__BOOLEAN,
890 G_TYPE_BOOLEAN, G_TYPE_INT);
892 tree_view_signals[TOGGLE_CURSOR_ROW] =
893 g_signal_new ("toggle-cursor-row",
894 G_TYPE_FROM_CLASS (o_class),
895 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
896 G_STRUCT_OFFSET (PsppSheetViewClass, toggle_cursor_row),
898 psppire_marshal_BOOLEAN__VOID,
901 tree_view_signals[START_INTERACTIVE_SEARCH] =
902 g_signal_new ("start-interactive-search",
903 G_TYPE_FROM_CLASS (o_class),
904 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
905 G_STRUCT_OFFSET (PsppSheetViewClass, start_interactive_search),
907 psppire_marshal_BOOLEAN__VOID,
911 for (i = 0; i < 2; i++)
913 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Up, 0, TRUE,
914 GTK_MOVEMENT_DISPLAY_LINES, -1);
915 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Up, 0, TRUE,
916 GTK_MOVEMENT_DISPLAY_LINES, -1);
918 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Down, 0, TRUE,
919 GTK_MOVEMENT_DISPLAY_LINES, 1);
920 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Down, 0, TRUE,
921 GTK_MOVEMENT_DISPLAY_LINES, 1);
923 pspp_sheet_view_add_move_binding (binding_set[i], GDK_p, GDK_CONTROL_MASK, FALSE,
924 GTK_MOVEMENT_DISPLAY_LINES, -1);
926 pspp_sheet_view_add_move_binding (binding_set[i], GDK_n, GDK_CONTROL_MASK, FALSE,
927 GTK_MOVEMENT_DISPLAY_LINES, 1);
929 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Home, 0, TRUE,
930 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
931 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Home, 0, TRUE,
932 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
934 pspp_sheet_view_add_move_binding (binding_set[i], GDK_End, 0, TRUE,
935 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
936 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_End, 0, TRUE,
937 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
939 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Up, 0, TRUE,
940 GTK_MOVEMENT_PAGES, -1);
941 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Up, 0, TRUE,
942 GTK_MOVEMENT_PAGES, -1);
944 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Down, 0, TRUE,
945 GTK_MOVEMENT_PAGES, 1);
946 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Down, 0, TRUE,
947 GTK_MOVEMENT_PAGES, 1);
950 gtk_binding_entry_add_signal (binding_set[i], GDK_Up, GDK_CONTROL_MASK, "move-cursor", 2,
951 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
954 gtk_binding_entry_add_signal (binding_set[i], GDK_Down, GDK_CONTROL_MASK, "move-cursor", 2,
955 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
958 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, 0, "move-cursor", 2,
959 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
962 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, 0, "move-cursor", 2,
963 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
966 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, 0, "move-cursor", 2,
967 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
970 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, GDK_SHIFT_MASK, "move-cursor", 2,
971 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
974 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, 0, "move-cursor", 2,
975 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
978 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, 0, "move-cursor", 2,
979 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
982 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, GDK_CONTROL_MASK,
984 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
987 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, GDK_CONTROL_MASK,
989 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
992 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, GDK_CONTROL_MASK,
994 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
997 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, GDK_CONTROL_MASK,
999 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1002 gtk_binding_entry_add_signal (binding_set[i], GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
1004 gtk_binding_entry_add_signal (binding_set[i], GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
1007 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1008 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1010 gtk_binding_entry_add_signal (binding_set[0], GDK_a, GDK_CONTROL_MASK, "select-all", 0);
1011 gtk_binding_entry_add_signal (binding_set[0], GDK_slash, GDK_CONTROL_MASK, "select-all", 0);
1013 gtk_binding_entry_add_signal (binding_set[0], GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
1014 gtk_binding_entry_add_signal (binding_set[0], GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0);
1016 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1017 G_TYPE_BOOLEAN, TRUE,
1018 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1019 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1020 G_TYPE_BOOLEAN, TRUE,
1021 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1023 gtk_binding_entry_add_signal (binding_set[0], GDK_space, 0, "select-cursor-row", 1,
1024 G_TYPE_BOOLEAN, TRUE,
1026 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, 0, "select-cursor-row", 1,
1027 G_TYPE_BOOLEAN, TRUE,
1029 gtk_binding_entry_add_signal (binding_set[0], GDK_Return, 0, "select-cursor-row", 1,
1030 G_TYPE_BOOLEAN, TRUE,
1032 gtk_binding_entry_add_signal (binding_set[0], GDK_ISO_Enter, 0, "select-cursor-row", 1,
1033 G_TYPE_BOOLEAN, TRUE,
1035 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Enter, 0, "select-cursor-row", 1,
1036 G_TYPE_BOOLEAN, TRUE,
1039 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, 0, "select-cursor-parent", 0);
1040 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0);
1042 g_type_class_add_private (o_class, sizeof (PsppSheetViewPrivate));
1046 pspp_sheet_view_buildable_init (GtkBuildableIface *iface)
1048 iface->add_child = pspp_sheet_view_buildable_add_child;
1052 pspp_sheet_view_init (PsppSheetView *tree_view)
1054 tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, PSPP_TYPE_SHEET_VIEW, PsppSheetViewPrivate);
1056 gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
1057 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE);
1059 tree_view->priv->flags = PSPP_SHEET_VIEW_DRAW_KEYFOCUS
1060 | PSPP_SHEET_VIEW_HEADERS_VISIBLE;
1062 /* We need some padding */
1063 tree_view->priv->selected = range_tower_create ();
1064 tree_view->priv->dy = 0;
1065 tree_view->priv->cursor_offset = 0;
1066 tree_view->priv->n_columns = 0;
1067 tree_view->priv->header_height = 1;
1068 tree_view->priv->x_drag = 0;
1069 tree_view->priv->drag_pos = -1;
1070 tree_view->priv->header_has_focus = FALSE;
1071 tree_view->priv->pressed_button = -1;
1072 tree_view->priv->press_start_x = -1;
1073 tree_view->priv->press_start_y = -1;
1074 tree_view->priv->reorderable = FALSE;
1075 tree_view->priv->presize_handler_timer = 0;
1076 tree_view->priv->scroll_sync_timer = 0;
1077 tree_view->priv->fixed_height = -1;
1078 tree_view->priv->fixed_height_set = FALSE;
1079 pspp_sheet_view_set_adjustments (tree_view, NULL, NULL);
1080 tree_view->priv->selection = _pspp_sheet_selection_new_with_tree_view (tree_view);
1081 tree_view->priv->enable_search = TRUE;
1082 tree_view->priv->search_column = -1;
1083 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
1084 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
1085 tree_view->priv->search_custom_entry_set = FALSE;
1086 tree_view->priv->typeselect_flush_timeout = 0;
1087 tree_view->priv->init_hadjust_value = TRUE;
1088 tree_view->priv->width = 0;
1090 tree_view->priv->hover_selection = FALSE;
1092 tree_view->priv->rubber_banding_enable = FALSE;
1094 tree_view->priv->grid_lines = PSPP_SHEET_VIEW_GRID_LINES_NONE;
1096 tree_view->priv->tooltip_column = -1;
1098 tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT;
1100 tree_view->priv->post_validation_flag = FALSE;
1102 tree_view->priv->last_button_x = -1;
1103 tree_view->priv->last_button_y = -1;
1105 tree_view->priv->event_last_x = -10000;
1106 tree_view->priv->event_last_y = -10000;
1108 tree_view->priv->prelight_node = -1;
1109 tree_view->priv->rubber_band_start_node = -1;
1110 tree_view->priv->rubber_band_end_node = -1;
1112 tree_view->priv->anchor_column = NULL;
1114 tree_view->priv->button_style = NULL;
1116 tree_view->dispose_has_run = FALSE;
1118 pspp_sheet_view_do_set_vadjustment (tree_view, NULL);
1119 pspp_sheet_view_do_set_hadjustment (tree_view, NULL);
1120 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (tree_view)),
1121 GTK_STYLE_CLASS_VIEW);
1130 pspp_sheet_view_set_property (GObject *object,
1132 const GValue *value,
1135 PsppSheetView *tree_view;
1137 tree_view = PSPP_SHEET_VIEW (object);
1142 pspp_sheet_view_set_model (tree_view, g_value_get_object (value));
1144 case PROP_HADJUSTMENT:
1145 pspp_sheet_view_do_set_hadjustment (tree_view, g_value_get_object (value));
1147 case PROP_VADJUSTMENT:
1148 pspp_sheet_view_do_set_vadjustment (tree_view, g_value_get_object (value));
1150 case PROP_HSCROLL_POLICY:
1151 tree_view->priv->hscroll_policy = g_value_get_enum (value);
1152 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1154 case PROP_VSCROLL_POLICY:
1155 tree_view->priv->vscroll_policy = g_value_get_enum (value);
1156 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1158 case PROP_HEADERS_VISIBLE:
1159 pspp_sheet_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1161 case PROP_HEADERS_CLICKABLE:
1162 pspp_sheet_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1164 case PROP_REORDERABLE:
1165 pspp_sheet_view_set_reorderable (tree_view, g_value_get_boolean (value));
1167 case PROP_RULES_HINT:
1168 pspp_sheet_view_set_rules_hint (tree_view, g_value_get_boolean (value));
1170 case PROP_ENABLE_SEARCH:
1171 pspp_sheet_view_set_enable_search (tree_view, g_value_get_boolean (value));
1173 case PROP_SEARCH_COLUMN:
1174 pspp_sheet_view_set_search_column (tree_view, g_value_get_int (value));
1176 case PROP_HOVER_SELECTION:
1177 tree_view->priv->hover_selection = g_value_get_boolean (value);
1179 case PROP_RUBBER_BANDING:
1180 tree_view->priv->rubber_banding_enable = g_value_get_boolean (value);
1182 case PROP_ENABLE_GRID_LINES:
1183 pspp_sheet_view_set_grid_lines (tree_view, g_value_get_enum (value));
1185 case PROP_TOOLTIP_COLUMN:
1186 pspp_sheet_view_set_tooltip_column (tree_view, g_value_get_int (value));
1188 case PROP_SPECIAL_CELLS:
1189 pspp_sheet_view_set_special_cells (tree_view, g_value_get_enum (value));
1191 case PROP_FIXED_HEIGHT:
1192 pspp_sheet_view_set_fixed_height (tree_view, g_value_get_int (value));
1194 case PROP_FIXED_HEIGHT_SET:
1195 if (g_value_get_boolean (value))
1197 if (!tree_view->priv->fixed_height_set
1198 && tree_view->priv->fixed_height >= 0)
1200 tree_view->priv->fixed_height_set = true;
1201 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1206 if (tree_view->priv->fixed_height_set)
1208 tree_view->priv->fixed_height_set = false;
1209 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1210 install_presize_handler (tree_view);
1215 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1221 pspp_sheet_view_get_property (GObject *object,
1226 PsppSheetView *tree_view;
1228 tree_view = PSPP_SHEET_VIEW (object);
1233 g_value_set_object (value, tree_view->priv->model);
1235 case PROP_HADJUSTMENT:
1236 g_value_set_object (value, tree_view->priv->hadjustment);
1238 case PROP_VADJUSTMENT:
1239 g_value_set_object (value, tree_view->priv->vadjustment);
1241 case PROP_HSCROLL_POLICY:
1242 g_value_set_enum (value, tree_view->priv->hscroll_policy);
1244 case PROP_VSCROLL_POLICY:
1245 g_value_set_enum (value, tree_view->priv->vscroll_policy);
1247 case PROP_HEADERS_VISIBLE:
1248 g_value_set_boolean (value, pspp_sheet_view_get_headers_visible (tree_view));
1250 case PROP_HEADERS_CLICKABLE:
1251 g_value_set_boolean (value, pspp_sheet_view_get_headers_clickable (tree_view));
1253 case PROP_REORDERABLE:
1254 g_value_set_boolean (value, tree_view->priv->reorderable);
1256 case PROP_RULES_HINT:
1257 g_value_set_boolean (value, tree_view->priv->has_rules);
1259 case PROP_ENABLE_SEARCH:
1260 g_value_set_boolean (value, tree_view->priv->enable_search);
1262 case PROP_SEARCH_COLUMN:
1263 g_value_set_int (value, tree_view->priv->search_column);
1265 case PROP_HOVER_SELECTION:
1266 g_value_set_boolean (value, tree_view->priv->hover_selection);
1268 case PROP_RUBBER_BANDING:
1269 g_value_set_boolean (value, tree_view->priv->rubber_banding_enable);
1271 case PROP_ENABLE_GRID_LINES:
1272 g_value_set_enum (value, tree_view->priv->grid_lines);
1274 case PROP_TOOLTIP_COLUMN:
1275 g_value_set_int (value, tree_view->priv->tooltip_column);
1277 case PROP_SPECIAL_CELLS:
1278 g_value_set_enum (value, tree_view->priv->special_cells);
1280 case PROP_FIXED_HEIGHT:
1281 g_value_set_int (value, pspp_sheet_view_get_fixed_height (tree_view));
1283 case PROP_FIXED_HEIGHT_SET:
1284 g_value_set_boolean (value, tree_view->priv->fixed_height_set);
1287 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1293 pspp_sheet_view_dispose (GObject *object)
1295 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1297 if (tree_view->dispose_has_run)
1300 tree_view->dispose_has_run = TRUE;
1302 if (tree_view->priv->selection != NULL)
1304 _pspp_sheet_selection_set_tree_view (tree_view->priv->selection, NULL);
1305 g_object_unref (tree_view->priv->selection);
1306 tree_view->priv->selection = NULL;
1309 if (tree_view->priv->hadjustment)
1311 g_object_unref (tree_view->priv->hadjustment);
1312 tree_view->priv->hadjustment = NULL;
1314 if (tree_view->priv->vadjustment)
1316 g_object_unref (tree_view->priv->vadjustment);
1317 tree_view->priv->vadjustment = NULL;
1320 if (tree_view->priv->button_style)
1322 g_object_unref (tree_view->priv->button_style);
1323 tree_view->priv->button_style = NULL;
1327 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->dispose (object);
1333 pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
1334 GtkBuilder *builder,
1338 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), PSPP_SHEET_VIEW_COLUMN (child));
1342 pspp_sheet_view_finalize (GObject *object)
1344 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1346 pspp_sheet_view_stop_editing (tree_view, TRUE);
1348 if (tree_view->priv->selected != NULL)
1350 range_tower_destroy (tree_view->priv->selected);
1351 tree_view->priv->selected = NULL;
1355 tree_view->priv->prelight_node = -1;
1358 if (tree_view->priv->scroll_to_path != NULL)
1360 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
1361 tree_view->priv->scroll_to_path = NULL;
1364 if (tree_view->priv->drag_dest_row != NULL)
1366 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
1367 tree_view->priv->drag_dest_row = NULL;
1370 if (tree_view->priv->top_row != NULL)
1372 gtk_tree_row_reference_free (tree_view->priv->top_row);
1373 tree_view->priv->top_row = NULL;
1376 if (tree_view->priv->column_drop_func_data &&
1377 tree_view->priv->column_drop_func_data_destroy)
1379 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
1380 tree_view->priv->column_drop_func_data = NULL;
1383 if (tree_view->priv->destroy_count_destroy &&
1384 tree_view->priv->destroy_count_data)
1386 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
1387 tree_view->priv->destroy_count_data = NULL;
1390 gtk_tree_row_reference_free (tree_view->priv->cursor);
1391 tree_view->priv->cursor = NULL;
1393 gtk_tree_row_reference_free (tree_view->priv->anchor);
1394 tree_view->priv->anchor = NULL;
1396 /* destroy interactive search dialog */
1397 if (tree_view->priv->search_window)
1399 gtk_widget_destroy (tree_view->priv->search_window);
1400 tree_view->priv->search_window = NULL;
1401 tree_view->priv->search_entry = NULL;
1402 if (tree_view->priv->typeselect_flush_timeout)
1404 g_source_remove (tree_view->priv->typeselect_flush_timeout);
1405 tree_view->priv->typeselect_flush_timeout = 0;
1409 if (tree_view->priv->search_destroy && tree_view->priv->search_user_data)
1411 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
1412 tree_view->priv->search_user_data = NULL;
1415 if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data)
1417 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
1418 tree_view->priv->search_position_user_data = NULL;
1421 pspp_sheet_view_set_model (tree_view, NULL);
1424 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->finalize (object);
1429 /* GtkWidget Methods
1432 /* GtkWidget::map helper */
1434 pspp_sheet_view_map_buttons (PsppSheetView *tree_view)
1438 g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
1440 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
1442 PsppSheetViewColumn *column;
1444 for (list = tree_view->priv->columns; list; list = list->next)
1446 column = list->data;
1447 if (column->button != NULL &&
1448 gtk_widget_get_visible (column->button) &&
1449 !gtk_widget_get_mapped (column->button))
1450 gtk_widget_map (column->button);
1452 for (list = tree_view->priv->columns; list; list = list->next)
1454 column = list->data;
1455 if (column->visible == FALSE || column->window == NULL)
1457 if (column->resizable)
1459 gdk_window_raise (column->window);
1460 gdk_window_show (column->window);
1463 gdk_window_hide (column->window);
1465 gdk_window_show (tree_view->priv->header_window);
1470 pspp_sheet_view_map (GtkWidget *widget)
1472 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1475 gtk_widget_set_mapped (widget, TRUE);
1477 tmp_list = tree_view->priv->children;
1480 PsppSheetViewChild *child = tmp_list->data;
1481 tmp_list = tmp_list->next;
1483 if (gtk_widget_get_visible (child->widget))
1485 if (!gtk_widget_get_mapped (child->widget))
1486 gtk_widget_map (child->widget);
1489 gdk_window_show (tree_view->priv->bin_window);
1491 pspp_sheet_view_map_buttons (tree_view);
1493 gdk_window_show (gtk_widget_get_window (widget));
1497 pspp_sheet_view_realize (GtkWidget *widget)
1499 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1502 GdkWindowAttr attributes;
1503 gint attributes_mask;
1504 GtkAllocation allocation;
1506 gtk_widget_set_realized (widget, TRUE);
1508 gtk_widget_get_allocation (widget, &allocation);
1510 /* Make the main, clipping window */
1511 attributes.window_type = GDK_WINDOW_CHILD;
1512 attributes.x = allocation.x;
1513 attributes.y = allocation.y;
1514 attributes.width = allocation.width;
1515 attributes.height = allocation.height;
1516 attributes.wclass = GDK_INPUT_OUTPUT;
1517 attributes.visual = gtk_widget_get_visual (widget);
1518 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1520 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
1522 window = gdk_window_new (gtk_widget_get_parent_window (widget),
1523 &attributes, attributes_mask);
1524 gtk_widget_set_window (widget, window);
1526 gtk_widget_register_window (widget, window);
1527 gtk_widget_get_allocation (widget, &allocation);
1529 /* Make the window for the tree */
1531 attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
1532 attributes.width = MAX (tree_view->priv->width, allocation.width);
1533 attributes.height = allocation.height;
1534 attributes.event_mask = (GDK_EXPOSURE_MASK |
1536 GDK_POINTER_MOTION_MASK |
1537 GDK_ENTER_NOTIFY_MASK |
1538 GDK_LEAVE_NOTIFY_MASK |
1539 GDK_BUTTON_PRESS_MASK |
1540 GDK_BUTTON_RELEASE_MASK |
1541 gtk_widget_get_events (widget));
1543 tree_view->priv->bin_window = gdk_window_new (window,
1544 &attributes, attributes_mask);
1545 gtk_widget_register_window (widget, tree_view->priv->bin_window);
1546 gtk_widget_get_allocation (widget, &allocation);
1548 /* Make the column header window */
1551 attributes.width = MAX (tree_view->priv->width, allocation.width);
1552 attributes.height = tree_view->priv->header_height;
1553 attributes.event_mask = (GDK_EXPOSURE_MASK |
1555 GDK_BUTTON_PRESS_MASK |
1556 GDK_BUTTON_RELEASE_MASK |
1557 GDK_KEY_PRESS_MASK |
1558 GDK_KEY_RELEASE_MASK |
1559 gtk_widget_get_events (widget));
1561 tree_view->priv->header_window = gdk_window_new (window,
1562 &attributes, attributes_mask);
1563 gtk_widget_register_window (widget, tree_view->priv->header_window);
1565 { /* Ensure Background */
1566 GtkStyleContext *context;
1568 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
1570 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
1571 gtk_style_context_set_background (context, tree_view->priv->header_window);
1574 tmp_list = tree_view->priv->children;
1577 PsppSheetViewChild *child = tmp_list->data;
1578 tmp_list = tmp_list->next;
1580 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
1583 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
1584 _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list->data));
1586 /* Need to call those here, since they create GCs */
1587 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
1589 install_presize_handler (tree_view);
1593 pspp_sheet_view_unrealize (GtkWidget *widget)
1595 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1596 PsppSheetViewPrivate *priv = tree_view->priv;
1599 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget);
1601 if (priv->scroll_timeout != 0)
1603 g_source_remove (priv->scroll_timeout);
1604 priv->scroll_timeout = 0;
1607 if (priv->open_dest_timeout != 0)
1609 g_source_remove (priv->open_dest_timeout);
1610 priv->open_dest_timeout = 0;
1613 if (priv->presize_handler_timer != 0)
1615 g_source_remove (priv->presize_handler_timer);
1616 priv->presize_handler_timer = 0;
1619 if (priv->validate_rows_timer != 0)
1621 g_source_remove (priv->validate_rows_timer);
1622 priv->validate_rows_timer = 0;
1625 if (priv->scroll_sync_timer != 0)
1627 g_source_remove (priv->scroll_sync_timer);
1628 priv->scroll_sync_timer = 0;
1631 if (priv->typeselect_flush_timeout)
1633 g_source_remove (priv->typeselect_flush_timeout);
1634 priv->typeselect_flush_timeout = 0;
1637 for (list = priv->columns; list; list = list->next)
1638 _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list->data));
1640 gdk_window_set_user_data (priv->bin_window, NULL);
1641 gdk_window_destroy (priv->bin_window);
1642 priv->bin_window = NULL;
1644 gdk_window_set_user_data (priv->header_window, NULL);
1645 gdk_window_destroy (priv->header_window);
1646 priv->header_window = NULL;
1648 if (priv->drag_window)
1650 gdk_window_set_user_data (priv->drag_window, NULL);
1651 gdk_window_destroy (priv->drag_window);
1652 priv->drag_window = NULL;
1655 if (priv->drag_highlight_window)
1657 gdk_window_set_user_data (priv->drag_highlight_window, NULL);
1658 gdk_window_destroy (priv->drag_highlight_window);
1659 priv->drag_highlight_window = NULL;
1662 if (tree_view->priv->columns != NULL)
1664 list = tree_view->priv->columns;
1667 PsppSheetViewColumn *column;
1668 column = PSPP_SHEET_VIEW_COLUMN (list->data);
1670 pspp_sheet_view_remove_column (tree_view, column);
1672 tree_view->priv->columns = NULL;
1676 /* GtkWidget::size_request helper */
1678 pspp_sheet_view_size_request_columns (PsppSheetView *tree_view)
1682 tree_view->priv->header_height = 0;
1684 if (tree_view->priv->model)
1686 for (list = tree_view->priv->columns; list; list = list->next)
1688 GtkRequisition requisition;
1689 PsppSheetViewColumn *column = list->data;
1691 pspp_sheet_view_column_size_request (column, &requisition);
1692 column->button_request = requisition.width;
1693 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
1699 /* Called only by ::size_request */
1701 pspp_sheet_view_update_size (PsppSheetView *tree_view)
1704 PsppSheetViewColumn *column;
1707 if (tree_view->priv->model == NULL)
1709 tree_view->priv->width = 0;
1710 tree_view->priv->prev_width = 0;
1711 tree_view->priv->height = 0;
1715 tree_view->priv->prev_width = tree_view->priv->width;
1716 tree_view->priv->width = 0;
1718 /* keep this in sync with size_allocate below */
1719 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
1721 gint real_requested_width = 0;
1722 column = list->data;
1723 if (!column->visible)
1726 if (column->use_resized_width)
1728 real_requested_width = column->resized_width;
1732 real_requested_width = column->fixed_width;
1735 if (column->min_width != -1)
1736 real_requested_width = MAX (real_requested_width, column->min_width);
1737 if (column->max_width != -1)
1738 real_requested_width = MIN (real_requested_width, column->max_width);
1740 tree_view->priv->width += real_requested_width;
1743 tree_view->priv->height = tree_view->priv->fixed_height * tree_view->priv->row_count;
1747 pspp_sheet_view_size_request (GtkWidget *widget,
1748 GtkRequisition *requisition)
1750 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1753 /* we validate some rows initially just to make sure we have some size.
1754 * In practice, with a lot of static lists, this should get a good width.
1756 initialize_fixed_height_mode (tree_view);
1757 pspp_sheet_view_size_request_columns (tree_view);
1758 pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget));
1760 requisition->width = tree_view->priv->width;
1761 requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
1763 tmp_list = tree_view->priv->children;
1767 PsppSheetViewChild *child = tmp_list->data;
1768 GtkRequisition child_requisition;
1770 tmp_list = tmp_list->next;
1772 if (gtk_widget_get_visible (child->widget))
1774 gtk_widget_get_preferred_size (child->widget, NULL, &child_requisition);
1780 invalidate_column (PsppSheetView *tree_view,
1781 PsppSheetViewColumn *column)
1783 gint column_offset = 0;
1785 GtkWidget *widget = GTK_WIDGET (tree_view);
1788 if (!gtk_widget_get_realized (widget))
1791 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1792 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
1794 list = (rtl ? list->prev : list->next))
1796 PsppSheetViewColumn *tmpcolumn = list->data;
1797 if (tmpcolumn == column)
1799 GdkRectangle invalid_rect;
1800 GtkAllocation allocation;
1802 gtk_widget_get_allocation (widget, &allocation);
1803 invalid_rect.x = column_offset;
1805 invalid_rect.width = column->width;
1806 invalid_rect.height = allocation.height;
1808 gdk_window_invalidate_rect (gtk_widget_get_window (widget), &invalid_rect, TRUE);
1812 column_offset += tmpcolumn->width;
1817 invalidate_last_column (PsppSheetView *tree_view)
1822 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1824 for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
1826 last_column = (rtl ? last_column->next : last_column->prev))
1828 if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible)
1830 invalidate_column (tree_view, last_column->data);
1837 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_view,
1838 PsppSheetViewColumn *column)
1840 gint real_requested_width;
1842 if (column->use_resized_width)
1844 real_requested_width = column->resized_width;
1848 real_requested_width = column->fixed_width;
1851 if (column->min_width != -1)
1852 real_requested_width = MAX (real_requested_width, column->min_width);
1853 if (column->max_width != -1)
1854 real_requested_width = MIN (real_requested_width, column->max_width);
1856 return real_requested_width;
1860 span_intersects (int a0, int a_width,
1861 int b0, int b_width)
1863 int a1 = a0 + a_width;
1864 int b1 = b0 + b_width;
1865 return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1);
1868 /* GtkWidget::size_allocate helper */
1870 pspp_sheet_view_size_allocate_columns (GtkWidget *widget,
1871 gboolean *width_changed)
1873 PsppSheetView *tree_view;
1874 GList *list, *first_column, *last_column;
1875 PsppSheetViewColumn *column;
1876 GtkAllocation col_allocation;
1877 GtkAllocation allocation;
1879 gint extra, extra_per_column;
1880 gint full_requested_width = 0;
1881 gint number_of_expand_columns = 0;
1882 gboolean column_changed = FALSE;
1885 tree_view = PSPP_SHEET_VIEW (widget);
1887 for (last_column = g_list_last (tree_view->priv->columns);
1888 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
1889 last_column = last_column->prev)
1892 if (last_column == NULL)
1895 for (first_column = g_list_first (tree_view->priv->columns);
1896 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
1897 first_column = first_column->next)
1900 col_allocation.y = 0;
1901 col_allocation.height = tree_view->priv->header_height;
1903 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1905 /* find out how many extra space and expandable columns we have */
1906 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
1908 column = (PsppSheetViewColumn *)list->data;
1910 if (!column->visible)
1913 full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1916 number_of_expand_columns++;
1919 gtk_widget_get_allocation (widget, &allocation);
1920 extra = MAX (allocation.width - full_requested_width, 0);
1921 if (number_of_expand_columns > 0)
1922 extra_per_column = extra/number_of_expand_columns;
1924 extra_per_column = 0;
1926 for (list = (rtl ? last_column : first_column);
1927 list != (rtl ? first_column->prev : last_column->next);
1928 list = (rtl ? list->prev : list->next))
1930 gint real_requested_width = 0;
1933 column = list->data;
1934 old_width = column->width;
1936 if (!column->visible)
1939 /* We need to handle the dragged button specially.
1941 if (column == tree_view->priv->drag_column)
1943 GtkAllocation drag_allocation;
1944 drag_allocation.width = gdk_window_get_width (tree_view->priv->drag_window);
1945 drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window);
1946 drag_allocation.x = 0;
1947 drag_allocation.y = 0;
1948 pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column,
1950 width += drag_allocation.width;
1954 real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1956 col_allocation.x = width;
1957 column->width = real_requested_width;
1961 if (number_of_expand_columns == 1)
1963 /* We add the remander to the last column as
1965 column->width += extra;
1969 column->width += extra_per_column;
1970 extra -= extra_per_column;
1971 number_of_expand_columns --;
1975 if (column->width != old_width)
1976 g_object_notify (G_OBJECT (column), "width");
1978 col_allocation.width = column->width;
1979 width += column->width;
1981 if (column->width > old_width)
1982 column_changed = TRUE;
1984 pspp_sheet_view_column_size_allocate (column, &col_allocation);
1987 gdk_window_move_resize (column->window,
1988 col_allocation.x + (rtl ? 0 : col_allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
1990 TREE_VIEW_DRAG_WIDTH, col_allocation.height);
1993 /* We change the width here. The user might have been resizing columns,
1994 * so the total width of the tree view changes.
1996 tree_view->priv->width = width;
1998 *width_changed = TRUE;
2001 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2005 update_childrens_allocation (PsppSheetView *tree_view)
2008 for (tmp_list = tree_view->priv->children; tmp_list; tmp_list = tmp_list->next)
2010 PsppSheetViewChild *child = tmp_list->data;
2011 GtkAllocation allocation;
2014 /* totally ignore our child's requisition */
2015 path = _pspp_sheet_view_find_path (tree_view, child->node);
2016 pspp_sheet_view_get_cell_area (tree_view, path, child->column, &allocation);
2017 gtk_tree_path_free (path);
2018 gtk_widget_size_allocate (child->widget, &allocation);
2023 pspp_sheet_view_size_allocate (GtkWidget *widget,
2024 GtkAllocation *allocation)
2026 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2028 gboolean width_changed = FALSE;
2029 GtkAllocation old_allocation;
2030 gtk_widget_get_allocation (widget, &old_allocation);
2032 if (allocation->width != old_allocation.width)
2033 width_changed = TRUE;
2035 gtk_widget_set_allocation (widget, allocation);
2037 /* We size-allocate the columns first because the width of the
2038 * tree view (used in updating the adjustments below) might change.
2040 pspp_sheet_view_size_allocate_columns (widget, &width_changed);
2042 gtk_adjustment_set_page_size (tree_view->priv->hadjustment, allocation->width);
2043 gtk_adjustment_set_page_increment (tree_view->priv->hadjustment, allocation->width * 0.9);
2044 gtk_adjustment_set_step_increment (tree_view->priv->hadjustment, allocation->width * 0.1);
2045 gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0);
2046 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->hadjustment), tree_view->priv->width));
2048 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2050 if (allocation->width < tree_view->priv->width)
2052 if (tree_view->priv->init_hadjust_value)
2054 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2055 tree_view->priv->init_hadjust_value = FALSE;
2057 else if (allocation->width != old_allocation.width)
2059 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));
2062 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));
2066 gtk_adjustment_set_value (tree_view->priv->hadjustment, 0);
2067 tree_view->priv->init_hadjust_value = TRUE;
2071 if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + allocation->width > tree_view->priv->width)
2072 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2074 gtk_adjustment_changed (tree_view->priv->hadjustment);
2076 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2077 gtk_adjustment_set_step_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.1);
2078 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.9);
2079 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
2080 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->vadjustment), tree_view->priv->height));
2082 gtk_adjustment_changed (tree_view->priv->vadjustment);
2084 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2085 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
2086 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2087 else if (gtk_adjustment_get_value (tree_view->priv->vadjustment) + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
2088 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2089 tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
2090 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2091 pspp_sheet_view_top_row_to_dy (tree_view);
2093 pspp_sheet_view_dy_to_top_row (tree_view);
2095 if (gtk_widget_get_realized (widget))
2097 gdk_window_move_resize (gtk_widget_get_window (widget),
2098 allocation->x, allocation->y,
2099 allocation->width, allocation->height);
2100 gdk_window_move_resize (tree_view->priv->header_window,
2101 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2103 MAX (tree_view->priv->width, allocation->width),
2104 tree_view->priv->header_height);
2105 gdk_window_move_resize (tree_view->priv->bin_window,
2106 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2107 TREE_VIEW_HEADER_HEIGHT (tree_view),
2108 MAX (tree_view->priv->width, allocation->width),
2109 allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2112 if (tree_view->priv->row_count == 0)
2113 invalidate_empty_focus (tree_view);
2115 if (gtk_widget_get_realized (widget))
2117 gboolean has_expand_column = FALSE;
2118 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2120 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2122 has_expand_column = TRUE;
2127 /* This little hack only works if we have an LTR locale, and no column has the */
2130 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2131 ! has_expand_column)
2132 invalidate_last_column (tree_view);
2134 gtk_widget_queue_draw (widget);
2136 update_childrens_allocation(tree_view);
2140 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2142 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2144 GtkWidget *widget = GTK_WIDGET (tree_view);
2146 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2147 gtk_widget_grab_focus (widget);
2148 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2152 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2155 return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2159 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2162 range_tower_set1 (tree_view->priv->selected, node, 1);
2166 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2169 range_tower_set0 (tree_view->priv->selected, node, 1);
2173 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2176 return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2180 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2183 return node > 0 ? node - 1 : -1;
2187 all_columns_selected (PsppSheetView *tree_view)
2191 for (list = tree_view->priv->columns; list; list = list->next)
2193 PsppSheetViewColumn *column = list->data;
2194 if (column->selectable && !column->selected)
2202 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2204 PsppSheetViewColumn *column,
2205 GdkEventButton *event)
2207 PsppSheetSelection *selection;
2208 PsppSheetSelectionMode mode;
2210 gboolean update_anchor;
2214 g_return_val_if_fail (tree_view != NULL, FALSE);
2215 g_return_val_if_fail (column != NULL, FALSE);
2217 selection = tree_view->priv->selection;
2218 mode = pspp_sheet_selection_get_mode (selection);
2219 if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2222 if (!column->row_head)
2227 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2228 if (event->type != GDK_BUTTON_PRESS
2229 || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2235 path = gtk_tree_path_new_from_indices (node, -1);
2238 pspp_sheet_selection_unselect_all (selection);
2239 pspp_sheet_selection_select_path (selection, path);
2240 pspp_sheet_selection_select_all_columns (selection);
2241 update_anchor = TRUE;
2244 else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2246 if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2247 || !all_columns_selected (tree_view))
2249 pspp_sheet_selection_unselect_all (selection);
2250 pspp_sheet_selection_select_path (selection, path);
2251 pspp_sheet_selection_select_all_columns (selection);
2252 update_anchor = TRUE;
2256 update_anchor = handled = FALSE;
2258 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2259 && modifiers == GDK_CONTROL_MASK)
2261 if (!all_columns_selected (tree_view))
2263 pspp_sheet_selection_unselect_all (selection);
2264 pspp_sheet_selection_select_all_columns (selection);
2267 if (pspp_sheet_selection_path_is_selected (selection, path))
2268 pspp_sheet_selection_unselect_path (selection, path);
2270 pspp_sheet_selection_select_path (selection, path);
2271 update_anchor = TRUE;
2274 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2275 && modifiers == GDK_SHIFT_MASK)
2277 GtkTreeRowReference *anchor = tree_view->priv->anchor;
2278 GtkTreePath *anchor_path;
2280 if (all_columns_selected (tree_view)
2281 && gtk_tree_row_reference_valid (anchor))
2283 update_anchor = FALSE;
2284 anchor_path = gtk_tree_row_reference_get_path (anchor);
2288 update_anchor = TRUE;
2289 anchor_path = gtk_tree_path_copy (path);
2292 pspp_sheet_selection_unselect_all (selection);
2293 pspp_sheet_selection_select_range (selection, anchor_path, path);
2294 pspp_sheet_selection_select_all_columns (selection);
2296 gtk_tree_path_free (anchor_path);
2301 update_anchor = handled = FALSE;
2305 if (tree_view->priv->anchor)
2306 gtk_tree_row_reference_free (tree_view->priv->anchor);
2307 tree_view->priv->anchor =
2308 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2309 tree_view->priv->model,
2313 gtk_tree_path_free (path);
2318 find_click (PsppSheetView *tree_view,
2321 PsppSheetViewColumn **column,
2322 GdkRectangle *background_area,
2323 GdkRectangle *cell_area)
2330 /* find the node that was clicked */
2331 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2334 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2339 background_area->y = y_offset + y;
2340 background_area->height = ROW_HEIGHT (tree_view);
2341 background_area->x = 0;
2343 /* Let the column have a chance at selecting it. */
2344 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2345 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2346 list; list = (rtl ? list->prev : list->next))
2348 PsppSheetViewColumn *candidate = list->data;
2350 if (!candidate->visible)
2353 background_area->width = candidate->width;
2354 if ((background_area->x > x) ||
2355 (background_area->x + background_area->width <= x))
2357 background_area->x += background_area->width;
2361 /* we found the focus column */
2363 pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2365 *column = candidate;
2373 pspp_sheet_view_button_press (GtkWidget *widget,
2374 GdkEventButton *event)
2376 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2378 PsppSheetViewColumn *column = NULL;
2380 GdkRectangle background_area;
2381 GdkRectangle cell_area;
2384 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2385 pspp_sheet_view_stop_editing (tree_view, FALSE);
2388 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2389 * we're done handling the button press.
2392 if (event->window == tree_view->priv->bin_window)
2397 gint pre_val, aft_val;
2398 PsppSheetViewColumn *column = NULL;
2399 GtkCellRenderer *focus_cell = NULL;
2400 gboolean row_double_click = FALSE;
2403 if (tree_view->priv->row_count == 0)
2405 grab_focus_and_unset_draw_keyfocus (tree_view);
2409 if (!find_click (tree_view, event->x, event->y, &node, &column,
2410 &background_area, &cell_area))
2412 grab_focus_and_unset_draw_keyfocus (tree_view);
2416 tree_view->priv->focus_column = column;
2418 if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2422 pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2424 path = _pspp_sheet_view_find_path (tree_view, node);
2426 /* we only handle selection modifications on the first button press
2428 if (event->type == GDK_BUTTON_PRESS)
2430 PsppSheetSelectionMode mode = 0;
2432 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2433 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
2434 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2435 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
2437 focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2439 pspp_sheet_view_column_focus_cell (column, focus_cell);
2441 if (event->state & GDK_CONTROL_MASK)
2443 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, mode);
2444 pspp_sheet_view_real_toggle_cursor_row (tree_view);
2446 else if (event->state & GDK_SHIFT_MASK)
2448 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
2449 pspp_sheet_view_real_select_cursor_row (tree_view, FALSE, mode);
2453 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
2456 if (tree_view->priv->anchor_column == NULL ||
2457 !(event->state & GDK_SHIFT_MASK))
2458 tree_view->priv->anchor_column = column;
2459 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2460 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2461 tree_view->priv->anchor_column,
2465 /* the treeview may have been scrolled because of _set_cursor,
2469 aft_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2470 dval = pre_val - aft_val;
2472 cell_area.y += dval;
2473 background_area.y += dval;
2475 /* Save press to possibly begin a drag
2477 if (!tree_view->priv->in_grab &&
2478 tree_view->priv->pressed_button < 0)
2480 tree_view->priv->pressed_button = event->button;
2481 tree_view->priv->press_start_x = event->x;
2482 tree_view->priv->press_start_y = event->y;
2483 tree_view->priv->press_start_node = node;
2485 if (tree_view->priv->rubber_banding_enable
2486 && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2487 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2489 tree_view->priv->press_start_y += tree_view->priv->dy;
2490 tree_view->priv->rubber_band_x = event->x;
2491 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2492 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2494 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2495 tree_view->priv->rubber_band_ctrl = TRUE;
2496 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2497 tree_view->priv->rubber_band_shift = TRUE;
2502 /* Test if a double click happened on the same row. */
2503 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2505 int double_click_time, double_click_distance;
2507 g_object_get (gtk_settings_get_for_screen (
2508 gtk_widget_get_screen (widget)),
2509 "gtk-double-click-time", &double_click_time,
2510 "gtk-double-click-distance", &double_click_distance,
2513 /* Same conditions as _gdk_event_button_generate */
2514 if (tree_view->priv->last_button_x != -1 &&
2515 (event->time < tree_view->priv->last_button_time + double_click_time) &&
2516 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2517 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2519 /* We do no longer compare paths of this row and the
2520 * row clicked previously. We use the double click
2521 * distance to decide whether this is a valid click,
2522 * allowing the mouse to slightly move over another row.
2524 row_double_click = TRUE;
2526 tree_view->priv->last_button_time = 0;
2527 tree_view->priv->last_button_x = -1;
2528 tree_view->priv->last_button_y = -1;
2532 tree_view->priv->last_button_time = event->time;
2533 tree_view->priv->last_button_x = event->x;
2534 tree_view->priv->last_button_y = event->y;
2538 if (row_double_click)
2540 gtk_grab_remove (widget);
2541 pspp_sheet_view_row_activated (tree_view, path, column);
2543 if (tree_view->priv->pressed_button == event->button)
2544 tree_view->priv->pressed_button = -1;
2547 gtk_tree_path_free (path);
2549 /* If we activated the row through a double click we don't want to grab
2550 * focus back, as moving focus to another widget is pretty common.
2552 if (!row_double_click)
2553 grab_focus_and_unset_draw_keyfocus (tree_view);
2558 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2560 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2562 column = list->data;
2563 if (event->window == column->window &&
2564 column->resizable &&
2569 if (GDK_GRAB_SUCCESS != gdk_device_grab (event->device,
2573 GDK_POINTER_MOTION_HINT_MASK |
2574 GDK_BUTTON1_MOTION_MASK |
2575 GDK_BUTTON_RELEASE_MASK,
2579 gtk_grab_add (widget);
2580 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2581 column->resized_width = column->width;
2583 /* block attached dnd signal handler */
2584 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2586 g_signal_handlers_block_matched (widget,
2587 G_SIGNAL_MATCH_DATA,
2591 tree_view->priv->drag_pos = i;
2592 tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2594 if (!gtk_widget_has_focus (widget))
2595 gtk_widget_grab_focus (widget);
2603 /* GtkWidget::button_release_event helper */
2605 pspp_sheet_view_button_release_drag_column (GtkWidget *widget,
2606 GdkEventButton *event)
2608 PsppSheetView *tree_view;
2612 tree_view = PSPP_SHEET_VIEW (widget);
2614 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2615 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2616 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2618 /* Move the button back */
2619 g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2621 g_object_ref (tree_view->priv->drag_column->button);
2622 gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2623 gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2624 gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2625 g_object_unref (tree_view->priv->drag_column->button);
2626 gtk_widget_queue_resize (widget);
2627 if (tree_view->priv->drag_column->resizable)
2629 gdk_window_raise (tree_view->priv->drag_column->window);
2630 gdk_window_show (tree_view->priv->drag_column->window);
2633 gdk_window_hide (tree_view->priv->drag_column->window);
2635 gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2639 if (tree_view->priv->cur_reorder &&
2640 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2641 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2642 tree_view->priv->cur_reorder->right_column);
2646 if (tree_view->priv->cur_reorder &&
2647 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2648 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2649 tree_view->priv->cur_reorder->left_column);
2651 tree_view->priv->drag_column = NULL;
2652 gdk_window_hide (tree_view->priv->drag_window);
2654 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2655 g_slice_free (PsppSheetViewColumnReorder, l->data);
2656 g_list_free (tree_view->priv->column_drag_info);
2657 tree_view->priv->column_drag_info = NULL;
2658 tree_view->priv->cur_reorder = NULL;
2660 if (tree_view->priv->drag_highlight_window)
2661 gdk_window_hide (tree_view->priv->drag_highlight_window);
2663 /* Reset our flags */
2664 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2665 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2670 /* GtkWidget::button_release_event helper */
2672 pspp_sheet_view_button_release_column_resize (GtkWidget *widget,
2673 GdkEventButton *event)
2675 PsppSheetView *tree_view;
2678 tree_view = PSPP_SHEET_VIEW (widget);
2680 tree_view->priv->drag_pos = -1;
2682 /* unblock attached dnd signal handler */
2683 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2685 g_signal_handlers_unblock_matched (widget,
2686 G_SIGNAL_MATCH_DATA,
2690 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2691 gtk_grab_remove (widget);
2692 gdk_device_ungrab (event->device, event->time);
2698 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2699 GdkEventButton *event)
2701 GtkCellEditable *cell_editable;
2706 PsppSheetViewColumn *column;
2707 GdkRectangle background_area;
2708 GdkRectangle cell_area;
2714 if (event->window != tree_view->priv->bin_window)
2717 /* Ignore a released button, if that button wasn't depressed */
2718 if (tree_view->priv->pressed_button != event->button)
2721 if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2725 /* decide if we edit */
2726 path = _pspp_sheet_view_find_path (tree_view, node);
2727 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2728 if (event->button != 1 || modifiers)
2731 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2732 pspp_sheet_view_column_cell_set_cell_data (column,
2733 tree_view->priv->model,
2736 if (!pspp_sheet_view_column_get_quick_edit (column)
2737 && _pspp_sheet_view_column_has_editable_cell (column))
2740 flags = 0; /* FIXME: get the right flags */
2741 path_string = gtk_tree_path_to_string (path);
2743 if (!_pspp_sheet_view_column_cell_event (column,
2751 if (cell_editable == NULL)
2754 pspp_sheet_view_real_set_cursor (tree_view, path,
2755 TRUE, TRUE, 0); /* XXX mode? */
2756 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2759 _pspp_sheet_view_column_get_neighbor_sizes (
2760 column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2763 area.width -= right + left;
2765 pspp_sheet_view_real_start_editing (tree_view,
2772 g_free (path_string);
2773 gtk_tree_path_free (path);
2778 pspp_sheet_view_button_release (GtkWidget *widget,
2779 GdkEventButton *event)
2781 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2783 pspp_sheet_view_stop_editing (tree_view, FALSE);
2784 if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2785 && pspp_sheet_view_button_release_edit (tree_view, event))
2787 if (tree_view->priv->pressed_button == event->button)
2788 tree_view->priv->pressed_button = -1;
2790 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2794 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2795 return pspp_sheet_view_button_release_drag_column (widget, event);
2797 if (tree_view->priv->rubber_band_status)
2798 pspp_sheet_view_stop_rubber_band (tree_view);
2800 if (tree_view->priv->pressed_button == event->button)
2801 tree_view->priv->pressed_button = -1;
2803 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2804 return pspp_sheet_view_button_release_column_resize (widget, event);
2810 pspp_sheet_view_grab_broken (GtkWidget *widget,
2811 GdkEventGrabBroken *event)
2813 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2815 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2816 pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2818 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2819 pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2824 /* GtkWidget::motion_event function set.
2828 do_prelight (PsppSheetView *tree_view,
2830 /* these are in bin_window coords */
2834 int prev_node = tree_view->priv->prelight_node;
2836 if (prev_node != node)
2838 tree_view->priv->prelight_node = node;
2841 _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2844 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2850 prelight_or_select (PsppSheetView *tree_view,
2852 /* these are in bin_window coords */
2856 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2858 if (tree_view->priv->hover_selection &&
2859 (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2860 !(tree_view->priv->edited_column &&
2861 tree_view->priv->edited_column->editable_widget))
2865 if (!pspp_sheet_view_node_is_selected (tree_view, node))
2869 path = _pspp_sheet_view_find_path (tree_view, node);
2870 pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2871 if (pspp_sheet_view_node_is_selected (tree_view, node))
2873 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2874 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE, 0); /* XXX mode? */
2876 gtk_tree_path_free (path);
2880 else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2881 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2884 do_prelight (tree_view, node, x, y);
2888 ensure_unprelighted (PsppSheetView *tree_view)
2890 do_prelight (tree_view,
2892 -1000, -1000); /* coords not possibly over an arrow */
2894 g_assert (tree_view->priv->prelight_node < 0);
2898 update_prelight (PsppSheetView *tree_view,
2905 if (tree_view->priv->row_count == 0)
2910 ensure_unprelighted (tree_view);
2914 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2918 pspp_sheet_view_find_offset (tree_view, new_y, &node);
2921 prelight_or_select (tree_view, node, x, y);
2927 /* Our motion arrow is either a box (in the case of the original spot)
2928 * or an arrow. It is expander_size wide.
2951 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2954 PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2955 GtkWidget *widget = GTK_WIDGET (tree_view);
2956 GdkBitmap *mask = NULL;
2961 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2962 GdkWindowAttr attributes;
2963 guint attributes_mask;
2966 reorder->left_column == tree_view->priv->drag_column ||
2967 reorder->right_column == tree_view->priv->drag_column)
2968 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2969 else if (reorder->left_column || reorder->right_column)
2971 GdkRectangle visible_rect;
2972 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2973 if (reorder->left_column)
2974 x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2976 x = reorder->right_column->allocation.x;
2978 if (x < visible_rect.x)
2979 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2980 else if (x > visible_rect.x + visible_rect.width)
2981 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2983 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2986 /* We want to draw the rectangle over the initial location. */
2987 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2992 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2994 if (tree_view->priv->drag_highlight_window)
2996 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2998 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3001 attributes.window_type = GDK_WINDOW_CHILD;
3002 attributes.wclass = GDK_INPUT_OUTPUT;
3003 attributes.x = tree_view->priv->drag_column_x;
3005 width = attributes.width = tree_view->priv->drag_column->allocation.width;
3006 height = attributes.height = tree_view->priv->drag_column->allocation.height;
3007 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3008 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3009 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3010 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3011 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
3012 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3014 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3015 gc = gdk_gc_new (mask);
3017 gdk_gc_set_foreground (gc, &col);
3018 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3020 gdk_gc_set_foreground(gc, &col);
3021 gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
3022 g_object_unref (gc);
3024 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3026 if (mask) g_object_unref (mask);
3027 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3030 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
3036 width = tree_view->priv->expander_size;
3038 /* Get x, y, width, height of arrow */
3039 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
3040 if (reorder->left_column)
3042 x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
3043 height = reorder->left_column->allocation.height;
3047 x += reorder->right_column->allocation.x - width/2;
3048 height = reorder->right_column->allocation.height;
3050 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3051 height += tree_view->priv->expander_size;
3053 /* Create the new window */
3054 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3056 if (tree_view->priv->drag_highlight_window)
3058 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3060 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3063 attributes.window_type = GDK_WINDOW_TEMP;
3064 attributes.wclass = GDK_INPUT_OUTPUT;
3065 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3066 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3067 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3068 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3071 attributes.width = width;
3072 attributes.height = height;
3073 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3074 &attributes, attributes_mask);
3075 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3077 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3078 gc = gdk_gc_new (mask);
3080 gdk_gc_set_foreground (gc, &col);
3081 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3083 /* Draw the 2 arrows as per above */
3085 gdk_gc_set_foreground (gc, &col);
3086 for (i = 0; i < width; i ++)
3088 if (i == (width/2 - 1))
3090 gdk_draw_line (mask, gc, i, j, i, height - j);
3091 if (i < (width/2 - 1))
3096 g_object_unref (gc);
3097 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3099 if (mask) g_object_unref (mask);
3102 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3103 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3105 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3106 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3112 width = tree_view->priv->expander_size;
3114 /* Get x, y, width, height of arrow */
3115 width = width/2; /* remember, the arrow only takes half the available width */
3116 gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
3117 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3118 x += widget->allocation.width - width;
3120 if (reorder->left_column)
3121 height = reorder->left_column->allocation.height;
3123 height = reorder->right_column->allocation.height;
3125 y -= tree_view->priv->expander_size;
3126 height += 2*tree_view->priv->expander_size;
3128 /* Create the new window */
3129 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3130 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3132 if (tree_view->priv->drag_highlight_window)
3134 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3136 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3139 attributes.window_type = GDK_WINDOW_TEMP;
3140 attributes.wclass = GDK_INPUT_OUTPUT;
3141 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3142 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3143 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3144 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3147 attributes.width = width;
3148 attributes.height = height;
3149 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3150 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3152 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3153 gc = gdk_gc_new (mask);
3155 gdk_gc_set_foreground (gc, &col);
3156 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3158 /* Draw the 2 arrows as per above */
3160 gdk_gc_set_foreground (gc, &col);
3161 j = tree_view->priv->expander_size;
3162 for (i = 0; i < width; i ++)
3165 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3169 gdk_draw_line (mask, gc, k, j, k, height - j);
3170 gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3171 gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3174 g_object_unref (gc);
3175 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3177 if (mask) g_object_unref (mask);
3180 tree_view->priv->drag_column_window_state = arrow_type;
3181 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3185 g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3186 gdk_window_hide (tree_view->priv->drag_highlight_window);
3190 gdk_window_show (tree_view->priv->drag_highlight_window);
3191 gdk_window_raise (tree_view->priv->drag_highlight_window);
3196 pspp_sheet_view_motion_resize_column (GtkWidget *widget,
3197 GdkEventMotion *event)
3201 PsppSheetViewColumn *column;
3202 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3204 column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3206 if (event->is_hint || event->window != gtk_widget_get_window (widget))
3207 gtk_widget_get_pointer (widget, &x, NULL);
3211 if (tree_view->priv->hadjustment)
3212 x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
3214 new_width = pspp_sheet_view_new_column_width (tree_view,
3215 tree_view->priv->drag_pos, &x);
3216 if (x != tree_view->priv->x_drag &&
3217 (new_width != column->fixed_width))
3219 column->use_resized_width = TRUE;
3220 column->resized_width = new_width;
3223 column->resized_width -= tree_view->priv->last_extra_space_per_column;
3225 gtk_widget_queue_resize (widget);
3233 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3235 PsppSheetViewColumnReorder *reorder = NULL;
3239 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3240 for (list = tree_view->priv->column_drag_info; list; list = list->next)
3242 reorder = (PsppSheetViewColumnReorder *) list->data;
3243 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3248 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3251 tree_view->priv->cur_reorder = reorder;
3252 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3256 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3258 GdkRectangle visible_rect;
3263 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3264 y += tree_view->priv->dy;
3266 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3268 /* see if we are near the edge. */
3269 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3272 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3277 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->vadjustment) + offset, 0.0,
3278 gtk_adjustment_get_upper (tree_view->priv->vadjustment) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
3279 gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3283 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3285 GdkRectangle visible_rect;
3290 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3292 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3294 /* See if we are near the edge. */
3295 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3298 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3304 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) + offset,
3305 0.0, gtk_adjustment_get_upper (tree_view->priv->hadjustment) - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
3306 gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3313 pspp_sheet_view_motion_drag_column (GtkWidget *widget,
3314 GdkEventMotion *event)
3316 PsppSheetView *tree_view = (PsppSheetView *) widget;
3317 PsppSheetViewColumn *column = tree_view->priv->drag_column;
3319 GtkAllocation allocation;
3322 if ((column == NULL) ||
3323 (event->window != tree_view->priv->drag_window))
3326 /* Handle moving the header */
3327 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3328 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
3329 x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3330 MAX (tree_view->priv->width, allocation.width) - column->allocation.width);
3331 gdk_window_move (tree_view->priv->drag_window, x, y);
3333 /* autoscroll, if needed */
3334 pspp_sheet_view_horizontal_autoscroll (tree_view);
3335 /* Update the current reorder position and arrow; */
3336 pspp_sheet_view_update_current_reorder (tree_view);
3342 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3344 remove_scroll_timeout (tree_view);
3345 gtk_grab_remove (GTK_WIDGET (tree_view));
3347 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3349 GtkTreePath *tmp_path;
3351 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3353 /* The anchor path should be set to the start path */
3354 tmp_path = _pspp_sheet_view_find_path (tree_view,
3355 tree_view->priv->rubber_band_start_node);
3357 if (tree_view->priv->anchor)
3358 gtk_tree_row_reference_free (tree_view->priv->anchor);
3360 tree_view->priv->anchor =
3361 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3362 tree_view->priv->model,
3365 gtk_tree_path_free (tmp_path);
3367 /* ... and the cursor to the end path */
3368 tmp_path = _pspp_sheet_view_find_path (tree_view,
3369 tree_view->priv->rubber_band_end_node);
3370 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE, 0); /* XXX mode? */
3371 gtk_tree_path_free (tmp_path);
3373 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3376 /* Clear status variables */
3377 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3378 tree_view->priv->rubber_band_shift = 0;
3379 tree_view->priv->rubber_band_ctrl = 0;
3381 tree_view->priv->rubber_band_start_node = -1;
3382 tree_view->priv->rubber_band_end_node = -1;
3386 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3390 gboolean skip_start,
3393 if (start_node == end_node)
3396 /* We skip the first node and jump inside the loop */
3402 /* Small optimization by assuming insensitive nodes are never
3407 if (tree_view->priv->rubber_band_shift)
3408 pspp_sheet_view_node_select (tree_view, start_node);
3409 else if (tree_view->priv->rubber_band_ctrl)
3411 /* Toggle the selection state */
3412 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3413 pspp_sheet_view_node_unselect (tree_view, start_node);
3415 pspp_sheet_view_node_select (tree_view, start_node);
3418 pspp_sheet_view_node_select (tree_view, start_node);
3422 /* Mirror the above */
3423 if (tree_view->priv->rubber_band_shift)
3424 pspp_sheet_view_node_unselect (tree_view, start_node);
3425 else if (tree_view->priv->rubber_band_ctrl)
3427 /* Toggle the selection state */
3428 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3429 pspp_sheet_view_node_unselect (tree_view, start_node);
3431 pspp_sheet_view_node_select (tree_view, start_node);
3434 pspp_sheet_view_node_unselect (tree_view, start_node);
3437 _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3439 if (start_node == end_node)
3444 start_node = pspp_sheet_view_node_next (tree_view, start_node);
3447 /* Ran out of tree */
3450 if (skip_end && start_node == end_node)
3457 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3460 return node * tree_view->priv->fixed_height;
3464 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3468 int fixed_height = tree_view->priv->fixed_height;
3469 if (fixed_height <= 0
3471 || height >= tree_view->priv->row_count * fixed_height)
3478 *new_node = height / fixed_height;
3479 return height % fixed_height;
3484 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3489 pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3490 pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3492 /* Handle the start area first */
3493 if (tree_view->priv->rubber_band_start_node < 0)
3495 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3502 else if (start_node < tree_view->priv->rubber_band_start_node)
3504 /* New node is above the old one; selection became bigger */
3505 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3507 tree_view->priv->rubber_band_start_node,
3512 else if (start_node > tree_view->priv->rubber_band_start_node)
3514 /* New node is below the old one; selection became smaller */
3515 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3516 tree_view->priv->rubber_band_start_node,
3523 tree_view->priv->rubber_band_start_node = start_node;
3525 /* Next, handle the end area */
3526 if (tree_view->priv->rubber_band_end_node < 0)
3528 /* In the event this happens, start_node was also -1; this case is
3532 else if (end_node < 0)
3534 /* Find the last node in the tree */
3535 pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3538 /* Selection reached end of the tree */
3539 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3540 tree_view->priv->rubber_band_end_node,
3546 else if (end_node > tree_view->priv->rubber_band_end_node)
3548 /* New node is below the old one; selection became bigger */
3549 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3550 tree_view->priv->rubber_band_end_node,
3556 else if (end_node < tree_view->priv->rubber_band_end_node)
3558 /* New node is above the old one; selection became smaller */
3559 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3561 tree_view->priv->rubber_band_end_node,
3567 tree_view->priv->rubber_band_end_node = end_node;
3570 #define GDK_RECTANGLE_PTR(X) ((GdkRectangle *)(X))
3573 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3576 cairo_rectangle_int_t old_area;
3577 cairo_rectangle_int_t new_area;
3578 cairo_rectangle_int_t common;
3579 cairo_region_t *invalid_region;
3580 PsppSheetViewColumn *column;
3582 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3583 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3584 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3585 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3587 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3590 y = MAX (y, 0) + tree_view->priv->dy;
3592 new_area.x = MIN (tree_view->priv->press_start_x, x);
3593 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3594 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3595 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3597 invalid_region = cairo_region_create_rectangle (&old_area);
3598 cairo_region_union_rectangle (invalid_region, &new_area);
3600 gdk_rectangle_intersect (GDK_RECTANGLE_PTR (&old_area),
3601 GDK_RECTANGLE_PTR (&new_area), GDK_RECTANGLE_PTR (&common));
3602 if (common.width > 2 && common.height > 2)
3604 cairo_region_t *common_region;
3606 /* make sure the border is invalidated */
3612 common_region = cairo_region_create_rectangle (&common);
3614 cairo_region_subtract (invalid_region, common_region);
3615 cairo_region_destroy (common_region);
3618 #if GTK_MAJOR_VERSION == 3
3619 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
3622 cairo_rectangle_int_t extents;
3624 cairo_region_get_extents (invalid_region, &extents);
3625 ereg = gdk_region_rectangle (GDK_RECTANGLE_PTR (&extents));
3626 gdk_window_invalidate_region (tree_view->priv->bin_window, ereg, TRUE);
3627 gdk_region_destroy (ereg);
3631 cairo_region_destroy (invalid_region);
3633 tree_view->priv->rubber_band_x = x;
3634 tree_view->priv->rubber_band_y = y;
3635 pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3637 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3638 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3639 tree_view->priv->anchor_column,
3642 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3644 pspp_sheet_view_update_rubber_band_selection (tree_view);
3649 pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view,
3654 GdkRectangle rubber_rect;
3658 rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3659 rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3660 rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3661 rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3663 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3666 cr = gdk_cairo_create (tree_view->priv->bin_window);
3667 cairo_set_line_width (cr, 1.0);
3669 style = gtk_widget_get_style (GTK_WIDGET (tree_view));
3670 cairo_set_source_rgba (cr,
3671 style->fg[GTK_STATE_NORMAL].red / 65535.,
3672 style->fg[GTK_STATE_NORMAL].green / 65535.,
3673 style->fg[GTK_STATE_NORMAL].blue / 65535.,
3676 gdk_cairo_rectangle (cr, &rect);
3680 cairo_set_source_rgb (cr,
3681 style->fg[GTK_STATE_NORMAL].red / 65535.,
3682 style->fg[GTK_STATE_NORMAL].green / 65535.,
3683 style->fg[GTK_STATE_NORMAL].blue / 65535.);
3685 cairo_rectangle (cr,
3686 rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3687 rubber_rect.width - 1, rubber_rect.height - 1);
3696 pspp_sheet_view_motion_bin_window (GtkWidget *widget,
3697 GdkEventMotion *event)
3699 PsppSheetView *tree_view;
3703 tree_view = (PsppSheetView *) widget;
3705 if (tree_view->priv->row_count == 0)
3708 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3710 GdkRectangle background_area, cell_area;
3711 PsppSheetViewColumn *column;
3713 if (find_click (tree_view, event->x, event->y, &node, &column,
3714 &background_area, &cell_area)
3715 && tree_view->priv->focus_column == column
3716 && tree_view->priv->press_start_node == node)
3719 gtk_grab_add (GTK_WIDGET (tree_view));
3720 pspp_sheet_view_update_rubber_band (tree_view);
3722 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3724 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3726 pspp_sheet_view_update_rubber_band (tree_view);
3728 add_scroll_timeout (tree_view);
3731 /* only check for an initiated drag when a button is pressed */
3732 if (tree_view->priv->pressed_button >= 0
3733 && !tree_view->priv->rubber_band_status)
3734 pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3736 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3740 pspp_sheet_view_find_offset (tree_view, new_y, &node);
3742 tree_view->priv->event_last_x = event->x;
3743 tree_view->priv->event_last_y = event->y;
3745 prelight_or_select (tree_view, node, event->x, event->y);
3751 pspp_sheet_view_motion (GtkWidget *widget,
3752 GdkEventMotion *event)
3754 PsppSheetView *tree_view;
3756 tree_view = (PsppSheetView *) widget;
3758 /* Resizing a column */
3759 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3760 return pspp_sheet_view_motion_resize_column (widget, event);
3763 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3764 return pspp_sheet_view_motion_drag_column (widget, event);
3766 /* Sanity check it */
3767 if (event->window == tree_view->priv->bin_window)
3768 return pspp_sheet_view_motion_bin_window (widget, event);
3773 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3774 * the tree is empty.
3777 invalidate_empty_focus (PsppSheetView *tree_view)
3781 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3786 area.width = gdk_window_get_width (tree_view->priv->bin_window);
3787 area.height = gdk_window_get_height (tree_view->priv->bin_window);
3788 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3791 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3795 draw_empty_focus (PsppSheetView *tree_view)
3797 GtkWidget *widget = GTK_WIDGET (tree_view);
3799 cairo_t *cr = gdk_cairo_create (tree_view->priv->bin_window);
3801 if (!gtk_widget_has_focus (widget))
3804 w = gdk_window_get_width (tree_view->priv->bin_window);
3805 h = gdk_window_get_height (tree_view->priv->bin_window);
3811 gtk_paint_focus (gtk_widget_get_style (widget),
3813 gtk_widget_get_state (widget),
3821 pspp_sheet_view_draw_vertical_grid_lines (PsppSheetView *tree_view,
3823 gint n_visible_columns,
3827 GList *list = tree_view->priv->columns;
3831 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3832 && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3835 /* Only draw the lines for visible rows and columns */
3836 for (list = tree_view->priv->columns; list; list = list->next, i++)
3838 PsppSheetViewColumn *column = list->data;
3841 if (! column->visible)
3844 current_x += column->width;
3846 /* Generally the grid lines should fit within the column, but for the
3847 last visible column we put it just past the end of the column.
3848 (Otherwise horizontal grid lines sometimes stick out by one pixel.) */
3850 if (i != n_visible_columns - 1)
3853 cairo_set_line_width (cr, 1.0);
3854 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
3855 cairo_move_to (cr, x + 0.5, min_y);
3856 cairo_line_to (cr, x + 0.5, max_y - min_y);
3861 /* Warning: Very scary function.
3862 * Modify at your own risk
3864 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3865 * FIXME: It's not...
3868 pspp_sheet_view_draw_bin (GtkWidget *widget,
3871 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3876 int drag_highlight = -1;
3879 gint y_offset, cell_offset;
3881 GdkRectangle background_area;
3882 GdkRectangle cell_area;
3884 gint bin_window_width;
3885 gint bin_window_height;
3886 GtkTreePath *cursor_path;
3887 GtkTreePath *drag_dest_path;
3888 GList *first_column, *last_column;
3889 gint vertical_separator;
3890 gint horizontal_separator;
3891 gint focus_line_width;
3892 gboolean allow_rules;
3894 gint n_visible_columns;
3895 gint grid_line_width;
3896 gboolean row_ending_details;
3897 gboolean draw_vgrid_lines, draw_hgrid_lines;
3899 GtkStyleContext *context;
3900 context = gtk_widget_get_style_context (widget);
3903 GtkAllocation allocation;
3904 gtk_widget_get_allocation (widget, &allocation);
3906 GdkRectangle exposed_rect;
3907 gdk_cairo_get_clip_rectangle (cr, &exposed_rect);
3911 Zarea.height = allocation.height;
3913 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3915 gtk_widget_style_get (widget,
3916 "horizontal-separator", &horizontal_separator,
3917 "vertical-separator", &vertical_separator,
3918 "allow-rules", &allow_rules,
3919 "focus-line-width", &focus_line_width,
3920 "row-ending-details", &row_ending_details,
3923 if (tree_view->priv->row_count == 0)
3925 draw_empty_focus (tree_view);
3930 /* clip event->area to the visible area */
3931 if (Zarea.height < 0.5)
3935 validate_visible_area (tree_view);
3937 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, Zarea.y);
3941 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3943 gdk_window_get_width (tree_view->priv->bin_window);
3946 gdk_window_get_height (tree_view->priv->bin_window);
3949 if (tree_view->priv->height < bin_window_height)
3951 gtk_paint_flat_box (gtk_widget_get_style (widget),
3953 gtk_widget_get_state (widget),
3957 0, tree_view->priv->height,
3959 bin_window_height - tree_view->priv->height);
3965 /* find the path for the node */
3966 path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3967 gtk_tree_model_get_iter (tree_view->priv->model,
3970 gtk_tree_path_free (path);
3973 drag_dest_path = NULL;
3975 if (tree_view->priv->cursor)
3976 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3979 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3981 if (tree_view->priv->drag_dest_row)
3982 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3985 _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3989 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3990 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3992 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
3993 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3995 if (draw_vgrid_lines || draw_hgrid_lines)
3996 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
3998 n_visible_columns = 0;
3999 for (list = tree_view->priv->columns; list; list = list->next)
4001 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4003 n_visible_columns ++;
4006 /* Find the last column */
4007 for (last_column = g_list_last (tree_view->priv->columns);
4008 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
4009 last_column = last_column->prev)
4013 for (first_column = g_list_first (tree_view->priv->columns);
4014 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
4015 first_column = first_column->next)
4018 /* Actually process the expose event. To do this, we want to
4019 * start at the first node of the event, and walk the tree in
4020 * order, drawing each successive node.
4027 gboolean is_first = FALSE;
4028 gboolean is_last = FALSE;
4029 gboolean done = FALSE;
4032 max_height = ROW_HEIGHT (tree_view);
4036 background_area.y = y_offset + Zarea.y;
4037 background_area.height = max_height;
4038 max_y = background_area.y + max_height;
4042 if (node == tree_view->priv->prelight_node)
4043 flags |= GTK_CELL_RENDERER_PRELIT;
4045 selected = pspp_sheet_view_node_is_selected (tree_view, node);
4049 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4051 list = (rtl ? list->prev : list->next))
4053 PsppSheetViewColumn *column = list->data;
4054 const gchar *detail = NULL;
4055 gboolean selected_column;
4058 if (!column->visible)
4061 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
4062 selected_column = column->selected && column->selectable;
4064 selected_column = TRUE;
4067 if (cell_offset > Zarea.x + Zarea.width ||
4068 cell_offset + column->width < Zarea.x)
4070 cell_offset += column->width;
4075 if (selected && selected_column)
4076 flags |= GTK_CELL_RENDERER_SELECTED;
4078 flags &= ~GTK_CELL_RENDERER_SELECTED;
4080 if (column->show_sort_indicator)
4081 flags |= GTK_CELL_RENDERER_SORTED;
4083 flags &= ~GTK_CELL_RENDERER_SORTED;
4086 flags |= GTK_CELL_RENDERER_FOCUSED;
4088 flags &= ~GTK_CELL_RENDERER_FOCUSED;
4090 background_area.x = cell_offset;
4091 background_area.width = column->width;
4093 cell_area = background_area;
4094 cell_area.y += vertical_separator / 2;
4095 cell_area.x += horizontal_separator / 2;
4096 cell_area.height -= vertical_separator;
4097 cell_area.width -= horizontal_separator;
4099 if (draw_vgrid_lines)
4101 if (list == first_column)
4103 cell_area.width -= grid_line_width / 2;
4105 else if (list == last_column)
4107 cell_area.x += grid_line_width / 2;
4108 cell_area.width -= grid_line_width / 2;
4112 cell_area.x += grid_line_width / 2;
4113 cell_area.width -= grid_line_width;
4117 if (draw_hgrid_lines)
4119 cell_area.y += grid_line_width / 2;
4120 cell_area.height -= grid_line_width;
4124 if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4126 if (!gdk_rectangle_intersect (&background_area, &exposed_rect, NULL))
4129 cell_offset += column->width;
4134 pspp_sheet_view_column_cell_set_cell_data (column,
4135 tree_view->priv->model,
4138 /* Select the detail for drawing the cell. relevant
4139 * factors are parity, sortedness, and whether to
4142 if (allow_rules && tree_view->priv->has_rules)
4144 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4145 n_visible_columns >= 3)
4148 detail = "cell_odd_ruled_sorted";
4150 detail = "cell_even_ruled_sorted";
4155 detail = "cell_odd_ruled";
4157 detail = "cell_even_ruled";
4162 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4163 n_visible_columns >= 3)
4166 detail = "cell_odd_sorted";
4168 detail = "cell_even_sorted";
4173 detail = "cell_odd";
4175 detail = "cell_even";
4181 gtk_style_context_save (context);
4182 state = gtk_cell_renderer_get_state (NULL, widget, flags);
4183 gtk_style_context_set_state (context, state);
4184 gtk_style_context_add_class (context, GTK_STYLE_CLASS_CELL);
4186 /* Draw background */
4187 gtk_render_background (context, cr,
4190 background_area.width,
4191 background_area.height);
4194 gtk_render_frame (context, cr,
4197 background_area.width,
4198 background_area.height);
4200 if (draw_hgrid_lines)
4202 cairo_set_line_width (cr, 1.0);
4203 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
4205 if (background_area.y >= 0)
4208 gdk_draw_line (event->window,
4209 tree_view->priv->grid_line_gc[widget->state],
4210 background_area.x, background_area.y,
4211 background_area.x + background_area.width,
4214 cairo_move_to (cr, background_area.x, background_area.y - 0.5);
4215 cairo_line_to (cr, background_area.x + background_area.width,
4216 background_area.y - 0.5);
4220 if (y_offset + max_height <= Zarea.height - 0.5)
4223 gdk_draw_line (event->window,
4224 tree_view->priv->grid_line_gc[widget->state],
4225 background_area.x, background_area.y + max_height,
4226 background_area.x + background_area.width,
4227 background_area.y + max_height);
4230 cairo_move_to (cr, background_area.x, background_area.y + max_height - 0.5);
4231 cairo_line_to (cr, background_area.x + background_area.width,
4232 background_area.y + max_height - 0.5);
4238 _pspp_sheet_view_column_cell_render (column,
4245 cell_offset += column->width;
4246 gtk_style_context_restore (context);
4249 if (node == drag_highlight)
4251 /* Draw indicator for the drop
4253 gint highlight_y = -1;
4257 switch (tree_view->priv->drag_dest_pos)
4259 case PSPP_SHEET_VIEW_DROP_BEFORE:
4260 highlight_y = background_area.y - 1;
4261 if (highlight_y < 0)
4265 case PSPP_SHEET_VIEW_DROP_AFTER:
4266 highlight_y = background_area.y + background_area.height - 1;
4269 case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE:
4270 case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER:
4271 _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node);
4275 width = gdk_window_get_width (tree_view->priv->bin_window);
4277 if (row_ending_details)
4278 gtk_paint_focus (gtk_widget_get_style (widget),
4280 gtk_widget_get_state (widget),
4283 ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4284 : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4285 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4286 - focus_line_width / 2,
4287 width, ROW_HEIGHT (tree_view)
4288 - focus_line_width + 1);
4290 gtk_paint_focus (gtk_widget_get_style (widget),
4292 gtk_widget_get_state (widget),
4294 "treeview-drop-indicator",
4295 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4296 - focus_line_width / 2,
4297 width, ROW_HEIGHT (tree_view)
4298 - focus_line_width + 1);
4303 if (highlight_y >= 0)
4305 gdk_draw_line (event->window,
4306 widget->style->fg_gc[gtk_widget_get_state (widget)],
4309 rtl ? 0 : bin_window_width,
4315 y_offset += max_height;
4319 node = pspp_sheet_view_node_next (tree_view, node);
4322 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
4326 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
4333 while (y_offset < Zarea.height);
4336 pspp_sheet_view_draw_vertical_grid_lines (tree_view, cr, n_visible_columns,
4340 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4342 GdkRectangle *rectangles;
4345 gdk_region_get_rectangles (event->region,
4349 while (n_rectangles--)
4350 pspp_sheet_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
4352 g_free (rectangles);
4357 gtk_tree_path_free (cursor_path);
4360 gtk_tree_path_free (drag_dest_path);
4367 pspp_sheet_view_draw (GtkWidget *widget,
4370 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4371 GtkStyleContext *context;
4373 context = gtk_widget_get_style_context (widget);
4375 if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window))
4380 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->bin_window);
4381 pspp_sheet_view_draw_bin (widget, cr);
4384 /* We can't just chain up to Container::expose as it will try to send the
4385 * event to the headers, so we handle propagating it to our children
4386 * (eg. widgets being edited) ourselves.
4388 tmp_list = tree_view->priv->children;
4391 PsppSheetViewChild *child = tmp_list->data;
4392 tmp_list = tmp_list->next;
4394 gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
4399 gtk_render_background (context, cr,
4401 gtk_widget_get_allocated_width (widget),
4402 gtk_widget_get_allocated_height (widget));
4405 gtk_style_context_save (context);
4406 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_VIEW);
4408 if (gtk_cairo_should_draw_window (cr, tree_view->priv->header_window))
4410 gint n_visible_columns;
4413 for (list = tree_view->priv->columns; list != NULL; list = list->next)
4415 PsppSheetViewColumn *column = list->data;
4417 if (column == tree_view->priv->drag_column || !column->visible)
4420 if (span_intersects (column->allocation.x, column->allocation.width,
4421 (int) gtk_adjustment_get_value (tree_view->priv->hadjustment),
4422 (int) gtk_widget_get_allocated_width (widget))
4423 && column->button != NULL)
4424 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4425 column->button, cr);
4428 n_visible_columns = 0;
4429 for (list = tree_view->priv->columns; list; list = list->next)
4431 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4433 n_visible_columns ++;
4436 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->header_window);
4437 pspp_sheet_view_draw_vertical_grid_lines (tree_view,
4441 TREE_VIEW_HEADER_HEIGHT (tree_view));
4444 if (tree_view->priv->drag_window &&
4445 gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window))
4447 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4448 tree_view->priv->drag_column->button,
4452 gtk_style_context_restore (context);
4464 /* returns 0x1 when no column has been found -- yes it's hackish */
4465 static PsppSheetViewColumn *
4466 pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
4467 PsppSheetViewColumn *column,
4470 PsppSheetViewColumn *left_column = NULL;
4471 PsppSheetViewColumn *cur_column = NULL;
4474 if (!column->reorderable)
4475 return (PsppSheetViewColumn *)0x1;
4477 switch (drop_position)
4480 /* find first column where we can drop */
4481 tmp_list = tree_view->priv->columns;
4482 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4483 return (PsppSheetViewColumn *)0x1;
4487 g_assert (tmp_list);
4489 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4490 tmp_list = tmp_list->next;
4492 if (left_column && left_column->visible == FALSE)
4495 if (!tree_view->priv->column_drop_func)
4498 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4500 left_column = cur_column;
4507 if (!tree_view->priv->column_drop_func)
4510 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4513 return (PsppSheetViewColumn *)0x1;
4517 /* find first column after column where we can drop */
4518 tmp_list = tree_view->priv->columns;
4520 for (; tmp_list; tmp_list = tmp_list->next)
4521 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4524 if (!tmp_list || !tmp_list->next)
4525 return (PsppSheetViewColumn *)0x1;
4527 tmp_list = tmp_list->next;
4528 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4529 tmp_list = tmp_list->next;
4533 g_assert (tmp_list);
4535 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4536 tmp_list = tmp_list->next;
4538 if (left_column && left_column->visible == FALSE)
4540 left_column = cur_column;
4542 tmp_list = tmp_list->next;
4546 if (!tree_view->priv->column_drop_func)
4549 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4551 left_column = cur_column;
4558 if (!tree_view->priv->column_drop_func)
4561 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4564 return (PsppSheetViewColumn *)0x1;
4568 /* find first column before column where we can drop */
4569 tmp_list = tree_view->priv->columns;
4571 for (; tmp_list; tmp_list = tmp_list->next)
4572 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4575 if (!tmp_list || !tmp_list->prev)
4576 return (PsppSheetViewColumn *)0x1;
4578 tmp_list = tmp_list->prev;
4579 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4580 tmp_list = tmp_list->prev;
4584 g_assert (tmp_list);
4586 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4588 if (left_column && !left_column->visible)
4590 /*if (!tmp_list->prev)
4591 return (PsppSheetViewColumn *)0x1;
4594 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4595 tmp_list = tmp_list->prev->prev;
4598 cur_column = left_column;
4600 tmp_list = tmp_list->prev;
4604 if (!tree_view->priv->column_drop_func)
4607 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4610 cur_column = left_column;
4611 tmp_list = tmp_list->prev;
4614 if (!tree_view->priv->column_drop_func)
4617 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4620 return (PsppSheetViewColumn *)0x1;
4624 /* same as DROP_HOME case, but doing it backwards */
4625 tmp_list = g_list_last (tree_view->priv->columns);
4628 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4629 return (PsppSheetViewColumn *)0x1;
4633 g_assert (tmp_list);
4635 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4637 if (left_column && !left_column->visible)
4639 cur_column = left_column;
4640 tmp_list = tmp_list->prev;
4643 if (!tree_view->priv->column_drop_func)
4646 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4649 cur_column = left_column;
4650 tmp_list = tmp_list->prev;
4653 if (!tree_view->priv->column_drop_func)
4656 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4659 return (PsppSheetViewColumn *)0x1;
4663 return (PsppSheetViewColumn *)0x1;
4667 pspp_sheet_view_key_press (GtkWidget *widget,
4670 PsppSheetView *tree_view = (PsppSheetView *) widget;
4672 if (tree_view->priv->rubber_band_status)
4674 if (event->keyval == GDK_Escape)
4675 pspp_sheet_view_stop_rubber_band (tree_view);
4680 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4682 if (event->keyval == GDK_Escape)
4684 tree_view->priv->cur_reorder = NULL;
4685 pspp_sheet_view_button_release_drag_column (widget, NULL);
4690 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4692 GList *focus_column;
4695 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4697 for (focus_column = tree_view->priv->columns;
4699 focus_column = focus_column->next)
4701 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4703 if (column->button && gtk_widget_has_focus (column->button))
4708 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4709 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4710 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4712 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4714 if (!column->resizable)
4716 gtk_widget_error_bell (widget);
4720 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4721 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4723 gint old_width = column->resized_width;
4725 column->resized_width = MAX (column->resized_width,
4727 column->resized_width -= 2;
4728 if (column->resized_width < 0)
4729 column->resized_width = 0;
4731 if (column->min_width == -1)
4732 column->resized_width = MAX (column->button_request,
4733 column->resized_width);
4735 column->resized_width = MAX (column->min_width,
4736 column->resized_width);
4738 if (column->max_width != -1)
4739 column->resized_width = MIN (column->resized_width,
4742 column->use_resized_width = TRUE;
4744 if (column->resized_width != old_width)
4745 gtk_widget_queue_resize (widget);
4747 gtk_widget_error_bell (widget);
4749 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4750 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4752 gint old_width = column->resized_width;
4754 column->resized_width = MAX (column->resized_width,
4756 column->resized_width += 2;
4758 if (column->max_width != -1)
4759 column->resized_width = MIN (column->resized_width,
4762 column->use_resized_width = TRUE;
4764 if (column->resized_width != old_width)
4765 gtk_widget_queue_resize (widget);
4767 gtk_widget_error_bell (widget);
4774 (event->state & GDK_MOD1_MASK) &&
4775 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4776 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4777 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4778 || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4780 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4782 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4783 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4785 PsppSheetViewColumn *col;
4786 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
4787 if (col != (PsppSheetViewColumn *)0x1)
4788 pspp_sheet_view_move_column_after (tree_view, column, col);
4790 gtk_widget_error_bell (widget);
4792 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4793 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4795 PsppSheetViewColumn *col;
4796 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4797 if (col != (PsppSheetViewColumn *)0x1)
4798 pspp_sheet_view_move_column_after (tree_view, column, col);
4800 gtk_widget_error_bell (widget);
4802 else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4804 PsppSheetViewColumn *col;
4805 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4806 if (col != (PsppSheetViewColumn *)0x1)
4807 pspp_sheet_view_move_column_after (tree_view, column, col);
4809 gtk_widget_error_bell (widget);
4811 else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4813 PsppSheetViewColumn *col;
4814 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4815 if (col != (PsppSheetViewColumn *)0x1)
4816 pspp_sheet_view_move_column_after (tree_view, column, col);
4818 gtk_widget_error_bell (widget);
4825 /* Chain up to the parent class. It handles the keybindings. */
4826 if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4829 if (tree_view->priv->search_entry_avoid_unhandled_binding)
4831 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4835 /* We pass the event to the search_entry. If its text changes, then we start
4836 * the typeahead find capabilities. */
4837 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4838 && tree_view->priv->enable_search
4839 && !tree_view->priv->search_custom_entry_set)
4841 GdkEvent *new_event;
4843 const char *new_text;
4846 gboolean text_modified;
4847 gulong popup_menu_id;
4849 pspp_sheet_view_ensure_interactive_directory (tree_view);
4851 /* Make a copy of the current text */
4852 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4853 new_event = gdk_event_copy ((GdkEvent *) event);
4854 g_object_unref (((GdkEventKey *) new_event)->window);
4855 ((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window));
4856 gtk_widget_realize (tree_view->priv->search_window);
4858 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
4859 "popup-menu", G_CALLBACK (gtk_true),
4862 /* Move the entry off screen */
4863 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4864 gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4865 gdk_screen_get_width (screen) + 1,
4866 gdk_screen_get_height (screen) + 1);
4867 gtk_widget_show (tree_view->priv->search_window);
4869 /* Send the event to the window. If the preedit_changed signal is emitted
4870 * during this event, we will set priv->imcontext_changed */
4871 tree_view->priv->imcontext_changed = FALSE;
4872 retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4873 gdk_event_free (new_event);
4874 gtk_widget_hide (tree_view->priv->search_window);
4876 g_signal_handler_disconnect (tree_view->priv->search_entry,
4879 /* We check to make sure that the entry tried to handle the text, and that
4880 * the text has changed.
4882 new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
4883 text_modified = strcmp (old_text, new_text) != 0;
4885 if (tree_view->priv->imcontext_changed || /* we're in a preedit */
4886 (retval && text_modified)) /* ...or the text was modified */
4888 if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
4890 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
4895 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
4905 pspp_sheet_view_key_release (GtkWidget *widget,
4908 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4910 if (tree_view->priv->rubber_band_status)
4913 return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
4916 /* FIXME Is this function necessary? Can I get an enter_notify event
4917 * w/o either an expose event or a mouse motion event?
4920 pspp_sheet_view_enter_notify (GtkWidget *widget,
4921 GdkEventCrossing *event)
4923 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4927 /* Sanity check it */
4928 if (event->window != tree_view->priv->bin_window)
4931 if (tree_view->priv->row_count == 0)
4934 if (event->mode == GDK_CROSSING_GRAB ||
4935 event->mode == GDK_CROSSING_GTK_GRAB ||
4936 event->mode == GDK_CROSSING_GTK_UNGRAB ||
4937 event->mode == GDK_CROSSING_STATE_CHANGED)
4940 /* find the node internally */
4941 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
4944 pspp_sheet_view_find_offset (tree_view, new_y, &node);
4946 tree_view->priv->event_last_x = event->x;
4947 tree_view->priv->event_last_y = event->y;
4949 prelight_or_select (tree_view, node, event->x, event->y);
4955 pspp_sheet_view_leave_notify (GtkWidget *widget,
4956 GdkEventCrossing *event)
4958 PsppSheetView *tree_view;
4960 if (event->mode == GDK_CROSSING_GRAB)
4963 tree_view = PSPP_SHEET_VIEW (widget);
4965 if (tree_view->priv->prelight_node >= 0)
4966 _pspp_sheet_view_queue_draw_node (tree_view,
4967 tree_view->priv->prelight_node,
4970 tree_view->priv->event_last_x = -10000;
4971 tree_view->priv->event_last_y = -10000;
4973 prelight_or_select (tree_view,
4975 -1000, -1000); /* coords not possibly over an arrow */
4982 pspp_sheet_view_focus_out (GtkWidget *widget,
4983 GdkEventFocus *event)
4985 PsppSheetView *tree_view;
4987 tree_view = PSPP_SHEET_VIEW (widget);
4989 gtk_widget_queue_draw (widget);
4991 /* destroy interactive search dialog */
4992 if (tree_view->priv->search_window)
4993 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
4999 /* Incremental Reflow
5003 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
5006 GtkAllocation allocation;
5007 gint y = pspp_sheet_view_node_find_offset (tree_view, node)
5008 - gtk_adjustment_get_value (tree_view->priv->vadjustment)
5009 + TREE_VIEW_HEADER_HEIGHT (tree_view);
5011 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5013 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
5016 tree_view->priv->fixed_height);
5020 node_is_visible (PsppSheetView *tree_view,
5026 y = pspp_sheet_view_node_find_offset (tree_view, node);
5027 height = ROW_HEIGHT (tree_view);
5029 if (y >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5030 y + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5031 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5037 /* Returns the row height. */
5039 validate_row (PsppSheetView *tree_view,
5044 PsppSheetViewColumn *column;
5045 GList *list, *first_column, *last_column;
5047 gint horizontal_separator;
5048 gint vertical_separator;
5049 gint focus_line_width;
5050 gboolean draw_vgrid_lines, draw_hgrid_lines;
5052 gint grid_line_width;
5053 gboolean wide_separators;
5054 gint separator_height;
5056 gtk_widget_style_get (GTK_WIDGET (tree_view),
5057 "focus-padding", &focus_pad,
5058 "focus-line-width", &focus_line_width,
5059 "horizontal-separator", &horizontal_separator,
5060 "vertical-separator", &vertical_separator,
5061 "grid-line-width", &grid_line_width,
5062 "wide-separators", &wide_separators,
5063 "separator-height", &separator_height,
5067 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5068 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5070 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5071 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5073 for (last_column = g_list_last (tree_view->priv->columns);
5074 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5075 last_column = last_column->prev)
5078 for (first_column = g_list_first (tree_view->priv->columns);
5079 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5080 first_column = first_column->next)
5083 for (list = tree_view->priv->columns; list; list = list->next)
5088 column = list->data;
5090 if (! column->visible)
5093 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5094 pspp_sheet_view_column_cell_get_size (column,
5096 &tmp_width, &tmp_height);
5098 tmp_height += vertical_separator;
5099 height = MAX (height, tmp_height);
5101 tmp_width = tmp_width + horizontal_separator;
5103 if (draw_vgrid_lines)
5105 if (list->data == first_column || list->data == last_column)
5106 tmp_width += grid_line_width / 2.0;
5108 tmp_width += grid_line_width;
5111 if (tmp_width > column->requested_width)
5112 column->requested_width = tmp_width;
5115 if (draw_hgrid_lines)
5116 height += grid_line_width;
5118 tree_view->priv->post_validation_flag = TRUE;
5124 validate_visible_area (PsppSheetView *tree_view)
5126 GtkTreePath *path = NULL;
5127 GtkTreePath *above_path = NULL;
5130 gboolean size_changed = FALSE;
5132 gint area_above = 0;
5133 gint area_below = 0;
5134 GtkAllocation allocation;
5136 if (tree_view->priv->row_count == 0)
5139 if (tree_view->priv->scroll_to_path == NULL)
5142 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5144 total_height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5146 if (total_height == 0)
5149 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5152 /* we are going to scroll, and will update dy */
5153 _pspp_sheet_view_find_node (tree_view, path, &node);
5154 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5156 if (tree_view->priv->scroll_to_use_align)
5158 gint height = ROW_HEIGHT (tree_view);
5159 area_above = (total_height - height) *
5160 tree_view->priv->scroll_to_row_align;
5161 area_below = total_height - area_above - height;
5162 area_above = MAX (area_above, 0);
5163 area_below = MAX (area_below, 0);
5168 * 1) row not visible
5172 gint height = ROW_HEIGHT (tree_view);
5174 dy = pspp_sheet_view_node_find_offset (tree_view, node);
5176 if (dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5177 dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5178 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5180 /* row visible: keep the row at the same position */
5181 area_above = dy - gtk_adjustment_get_value (tree_view->priv->vadjustment);
5182 area_below = (gtk_adjustment_get_value (tree_view->priv->vadjustment) +
5183 gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5188 /* row not visible */
5190 && dy + height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5192 /* row at the beginning -- fixed */
5194 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment)
5195 - area_above - height;
5197 else if (dy >= (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5198 gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5200 /* row at the end -- fixed */
5201 area_above = dy - (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5202 gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
5203 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) -
5204 area_above - height;
5208 area_above = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - height;
5214 /* row somewhere in the middle, bring it to the top
5218 area_below = total_height - height;
5224 /* the scroll to isn't valid; ignore it.
5227 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5228 tree_view->priv->scroll_to_path = NULL;
5232 above_path = gtk_tree_path_copy (path);
5234 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5235 * backwards is much slower then forward, as there is no iter_prev function.
5236 * We go forwards first in case we run out of tree. Then we go backwards to
5239 while (node >= 0 && area_below > 0)
5241 gboolean done = FALSE;
5244 node = pspp_sheet_view_node_next (tree_view, node);
5247 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5249 gtk_tree_path_next (path);
5252 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5262 area_below -= ROW_HEIGHT (tree_view);
5264 gtk_tree_path_free (path);
5266 /* If we ran out of tree, and have extra area_below left, we need to add it
5269 area_above += area_below;
5271 _pspp_sheet_view_find_node (tree_view, above_path, &node);
5273 /* We walk backwards */
5274 while (area_above > 0)
5276 node = pspp_sheet_view_node_prev (tree_view, node);
5278 /* Always find the new path in the tree. We cannot just assume
5279 * a gtk_tree_path_prev() is enough here, as there might be children
5280 * in between this node and the previous sibling node. If this
5281 * appears to be a performance hotspot in profiles, we can look into
5282 * intrigate logic for keeping path, node and iter in sync like
5283 * we do for forward walks. (Which will be hard because of the lacking
5290 gtk_tree_path_free (above_path);
5291 above_path = _pspp_sheet_view_find_path (tree_view, node);
5293 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5295 area_above -= ROW_HEIGHT (tree_view);
5298 /* set the dy here to scroll to the path,
5299 * and sync the top row accordingly
5301 pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5302 pspp_sheet_view_top_row_to_dy (tree_view);
5304 /* update width/height and queue a resize */
5307 GtkRequisition requisition;
5309 /* We temporarily guess a size, under the assumption that it will be the
5310 * same when we get our next size_allocate. If we don't do this, we'll be
5311 * in an inconsistent state if we call top_row_to_dy. */
5313 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5314 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5315 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5316 gtk_adjustment_changed (tree_view->priv->hadjustment);
5317 gtk_adjustment_changed (tree_view->priv->vadjustment);
5318 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5321 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5322 tree_view->priv->scroll_to_path = NULL;
5325 gtk_tree_path_free (above_path);
5327 if (tree_view->priv->scroll_to_column)
5329 tree_view->priv->scroll_to_column = NULL;
5331 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5335 initialize_fixed_height_mode (PsppSheetView *tree_view)
5337 if (!tree_view->priv->row_count)
5340 if (tree_view->priv->fixed_height_set)
5343 if (tree_view->priv->fixed_height < 0)
5350 path = _pspp_sheet_view_find_path (tree_view, node);
5351 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5353 tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5355 gtk_tree_path_free (path);
5357 g_object_notify (G_OBJECT (tree_view), "fixed-height");
5361 /* Our strategy for finding nodes to validate is a little convoluted. We find
5362 * the left-most uninvalidated node. We then try walking right, validating
5363 * nodes. Once we find a valid node, we repeat the previous process of finding
5364 * the first invalid node.
5368 validate_rows_handler (PsppSheetView *tree_view)
5370 initialize_fixed_height_mode (tree_view);
5371 if (tree_view->priv->validate_rows_timer)
5373 g_source_remove (tree_view->priv->validate_rows_timer);
5374 tree_view->priv->validate_rows_timer = 0;
5381 do_presize_handler (PsppSheetView *tree_view)
5383 GtkRequisition requisition;
5385 validate_visible_area (tree_view);
5386 tree_view->priv->presize_handler_timer = 0;
5388 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5391 gtk_widget_get_preferred_size (GTK_WIDGET (tree_view), NULL, &requisition);
5393 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5394 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5395 gtk_adjustment_changed (tree_view->priv->hadjustment);
5396 gtk_adjustment_changed (tree_view->priv->vadjustment);
5397 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5403 presize_handler_callback (gpointer data)
5405 do_presize_handler (PSPP_SHEET_VIEW (data));
5411 install_presize_handler (PsppSheetView *tree_view)
5413 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5416 if (! tree_view->priv->presize_handler_timer)
5418 tree_view->priv->presize_handler_timer =
5419 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5421 if (! tree_view->priv->validate_rows_timer)
5423 tree_view->priv->validate_rows_timer =
5424 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5429 scroll_sync_handler (PsppSheetView *tree_view)
5431 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5432 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5433 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5434 pspp_sheet_view_top_row_to_dy (tree_view);
5436 pspp_sheet_view_dy_to_top_row (tree_view);
5438 tree_view->priv->scroll_sync_timer = 0;
5444 install_scroll_sync_handler (PsppSheetView *tree_view)
5446 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5449 if (!tree_view->priv->scroll_sync_timer)
5451 tree_view->priv->scroll_sync_timer =
5452 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5457 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5461 gtk_tree_row_reference_free (tree_view->priv->top_row);
5465 tree_view->priv->top_row = NULL;
5466 tree_view->priv->top_row_dy = 0;
5470 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5471 tree_view->priv->top_row_dy = offset;
5475 /* Always call this iff dy is in the visible range. If the tree is empty, then
5476 * it's set to be NULL, and top_row_dy is 0;
5479 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5485 if (tree_view->priv->row_count == 0)
5487 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5491 offset = pspp_sheet_view_find_offset (tree_view,
5492 tree_view->priv->dy,
5497 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5501 path = _pspp_sheet_view_find_path (tree_view, node);
5502 pspp_sheet_view_set_top_row (tree_view, path, offset);
5503 gtk_tree_path_free (path);
5509 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5515 /* Avoid recursive calls */
5516 if (tree_view->priv->in_top_row_to_dy)
5519 if (tree_view->priv->top_row)
5520 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5527 _pspp_sheet_view_find_node (tree_view, path, &node);
5530 gtk_tree_path_free (path);
5534 /* keep dy and set new toprow */
5535 gtk_tree_row_reference_free (tree_view->priv->top_row);
5536 tree_view->priv->top_row = NULL;
5537 tree_view->priv->top_row_dy = 0;
5538 /* DO NOT install the idle handler */
5539 pspp_sheet_view_dy_to_top_row (tree_view);
5543 if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5545 /* new top row -- do NOT install the idle handler */
5546 pspp_sheet_view_dy_to_top_row (tree_view);
5550 new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5551 new_dy += tree_view->priv->top_row_dy;
5553 if (new_dy + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
5554 new_dy = tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
5556 new_dy = MAX (0, new_dy);
5558 tree_view->priv->in_top_row_to_dy = TRUE;
5559 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5560 tree_view->priv->in_top_row_to_dy = FALSE;
5565 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5567 install_presize_handler (tree_view);
5573 set_source_row (GdkDragContext *context,
5574 GtkTreeModel *model,
5575 GtkTreePath *source_row)
5577 g_object_set_data_full (G_OBJECT (context),
5578 "gtk-tree-view-source-row",
5579 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5580 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5584 get_source_row (GdkDragContext *context)
5586 GtkTreeRowReference *ref =
5587 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5590 return gtk_tree_row_reference_get_path (ref);
5597 GtkTreeRowReference *dest_row;
5598 guint path_down_mode : 1;
5599 guint empty_view_drop : 1;
5600 guint drop_append_mode : 1;
5605 dest_row_free (gpointer data)
5607 DestRow *dr = (DestRow *)data;
5609 gtk_tree_row_reference_free (dr->dest_row);
5610 g_slice_free (DestRow, dr);
5614 set_dest_row (GdkDragContext *context,
5615 GtkTreeModel *model,
5616 GtkTreePath *dest_row,
5617 gboolean path_down_mode,
5618 gboolean empty_view_drop,
5619 gboolean drop_append_mode)
5625 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5630 dr = g_slice_new (DestRow);
5632 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5633 dr->path_down_mode = path_down_mode != FALSE;
5634 dr->empty_view_drop = empty_view_drop != FALSE;
5635 dr->drop_append_mode = drop_append_mode != FALSE;
5637 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5638 dr, (GDestroyNotify) dest_row_free);
5642 get_dest_row (GdkDragContext *context,
5643 gboolean *path_down_mode)
5646 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5650 GtkTreePath *path = NULL;
5653 *path_down_mode = dr->path_down_mode;
5656 path = gtk_tree_row_reference_get_path (dr->dest_row);
5657 else if (dr->empty_view_drop)
5658 path = gtk_tree_path_new_from_indices (0, -1);
5662 if (path && dr->drop_append_mode)
5663 gtk_tree_path_next (path);
5671 /* Get/set whether drag_motion requested the drag data and
5672 * drag_data_received should thus not actually insert the data,
5673 * since the data doesn't result from a drop.
5676 set_status_pending (GdkDragContext *context,
5677 GdkDragAction suggested_action)
5679 g_object_set_data (G_OBJECT (context),
5680 "gtk-tree-view-status-pending",
5681 GINT_TO_POINTER (suggested_action));
5684 static GdkDragAction
5685 get_status_pending (GdkDragContext *context)
5687 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5688 "gtk-tree-view-status-pending"));
5691 static TreeViewDragInfo*
5692 get_info (PsppSheetView *tree_view)
5694 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5698 destroy_info (TreeViewDragInfo *di)
5700 g_slice_free (TreeViewDragInfo, di);
5703 static TreeViewDragInfo*
5704 ensure_info (PsppSheetView *tree_view)
5706 TreeViewDragInfo *di;
5708 di = get_info (tree_view);
5712 di = g_slice_new0 (TreeViewDragInfo);
5714 g_object_set_data_full (G_OBJECT (tree_view),
5715 "gtk-tree-view-drag-info",
5717 (GDestroyNotify) destroy_info);
5724 remove_info (PsppSheetView *tree_view)
5726 g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5731 drag_scan_timeout (gpointer data)
5733 PsppSheetView *tree_view;
5735 GdkModifierType state;
5736 GtkTreePath *path = NULL;
5737 PsppSheetViewColumn *column = NULL;
5738 GdkRectangle visible_rect;
5740 GDK_THREADS_ENTER ();
5742 tree_view = PSPP_SHEET_VIEW (data);
5744 gdk_window_get_pointer (tree_view->priv->bin_window,
5747 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5749 /* See if we are near the edge. */
5750 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5751 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5752 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5753 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5755 pspp_sheet_view_get_path_at_pos (tree_view,
5756 tree_view->priv->bin_window,
5765 pspp_sheet_view_scroll_to_cell (tree_view,
5771 gtk_tree_path_free (path);
5775 GDK_THREADS_LEAVE ();
5782 add_scroll_timeout (PsppSheetView *tree_view)
5784 if (tree_view->priv->scroll_timeout == 0)
5786 tree_view->priv->scroll_timeout =
5787 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5792 remove_scroll_timeout (PsppSheetView *tree_view)
5794 if (tree_view->priv->scroll_timeout != 0)
5796 g_source_remove (tree_view->priv->scroll_timeout);
5797 tree_view->priv->scroll_timeout = 0;
5802 check_model_dnd (GtkTreeModel *model,
5803 GType required_iface,
5804 const gchar *signal)
5806 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5808 g_warning ("You must override the default '%s' handler "
5809 "on PsppSheetView when using models that don't support "
5810 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5811 "is to connect to '%s' and call "
5812 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5813 "the default handler from running. Look at the source code "
5814 "for the default handler in gtktreeview.c to get an idea what "
5815 "your handler should do. (gtktreeview.c is in the GTK source "
5816 "code.) If you're using GTK from a language other than C, "
5817 "there may be a more natural way to override default handlers, e.g. via derivation.",
5818 signal, g_type_name (required_iface), signal);
5826 scroll_row_timeout (gpointer data)
5828 PsppSheetView *tree_view = data;
5830 pspp_sheet_view_horizontal_autoscroll (tree_view);
5831 pspp_sheet_view_vertical_autoscroll (tree_view);
5833 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5834 pspp_sheet_view_update_rubber_band (tree_view);
5839 /* Returns TRUE if event should not be propagated to parent widgets */
5841 set_destination_row (PsppSheetView *tree_view,
5842 GdkDragContext *context,
5843 /* coordinates relative to the widget */
5846 GdkDragAction *suggested_action,
5849 GtkTreePath *path = NULL;
5850 PsppSheetViewDropPosition pos;
5851 PsppSheetViewDropPosition old_pos;
5852 TreeViewDragInfo *di;
5854 GtkTreePath *old_dest_path = NULL;
5855 gboolean can_drop = FALSE;
5857 *suggested_action = 0;
5860 widget = GTK_WIDGET (tree_view);
5862 di = get_info (tree_view);
5864 if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5866 /* someone unset us as a drag dest, note that if
5867 * we return FALSE drag_leave isn't called
5870 pspp_sheet_view_set_drag_dest_row (tree_view,
5872 PSPP_SHEET_VIEW_DROP_BEFORE);
5874 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5876 return FALSE; /* no longer a drop site */
5879 *target = gtk_drag_dest_find_target (widget, context,
5880 gtk_drag_dest_get_target_list (widget));
5881 if (*target == GDK_NONE)
5886 if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
5892 GtkTreeModel *model;
5894 /* the row got dropped on empty space, let's setup a special case
5898 gtk_tree_path_free (path);
5900 model = pspp_sheet_view_get_model (tree_view);
5902 n_children = gtk_tree_model_iter_n_children (model, NULL);
5905 pos = PSPP_SHEET_VIEW_DROP_AFTER;
5906 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
5910 pos = PSPP_SHEET_VIEW_DROP_BEFORE;
5911 path = gtk_tree_path_new_from_indices (0, -1);
5921 /* If we left the current row's "open" zone, unset the timeout for
5924 pspp_sheet_view_get_drag_dest_row (tree_view,
5929 gtk_tree_path_free (old_dest_path);
5931 if (TRUE /* FIXME if the location droppable predicate */)
5939 GtkWidget *source_widget;
5941 *suggested_action = gdk_drag_context_get_suggested_action (context);
5942 source_widget = gtk_drag_get_source_widget (context);
5944 if (source_widget == widget)
5946 /* Default to MOVE, unless the user has
5947 * pressed ctrl or shift to affect available actions
5949 if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
5950 *suggested_action = GDK_ACTION_MOVE;
5953 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5958 /* can't drop here */
5959 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5961 PSPP_SHEET_VIEW_DROP_BEFORE);
5965 gtk_tree_path_free (path);
5971 get_logical_dest_row (PsppSheetView *tree_view,
5972 gboolean *path_down_mode,
5973 gboolean *drop_append_mode)
5975 /* adjust path to point to the row the drop goes in front of */
5976 GtkTreePath *path = NULL;
5977 PsppSheetViewDropPosition pos;
5979 g_return_val_if_fail (path_down_mode != NULL, NULL);
5980 g_return_val_if_fail (drop_append_mode != NULL, NULL);
5982 *path_down_mode = FALSE;
5983 *drop_append_mode = 0;
5985 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
5990 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
5992 else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
5993 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
5994 *path_down_mode = TRUE;
5998 GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
6000 g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
6002 if (!gtk_tree_model_get_iter (model, &iter, path) ||
6003 !gtk_tree_model_iter_next (model, &iter))
6004 *drop_append_mode = 1;
6007 *drop_append_mode = 0;
6008 gtk_tree_path_next (path);
6016 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
6017 GdkEventMotion *event)
6019 GtkWidget *widget = GTK_WIDGET (tree_view);
6020 GdkDragContext *context;
6021 TreeViewDragInfo *di;
6022 GtkTreePath *path = NULL;
6024 gint cell_x, cell_y;
6025 GtkTreeModel *model;
6026 gboolean retval = FALSE;
6028 di = get_info (tree_view);
6030 if (di == NULL || !di->source_set)
6033 if (tree_view->priv->pressed_button < 0)
6036 if (!gtk_drag_check_threshold (widget,
6037 tree_view->priv->press_start_x,
6038 tree_view->priv->press_start_y,
6039 event->x, event->y))
6042 model = pspp_sheet_view_get_model (tree_view);
6047 button = tree_view->priv->pressed_button;
6048 tree_view->priv->pressed_button = -1;
6050 pspp_sheet_view_get_path_at_pos (tree_view,
6051 tree_view->priv->press_start_x,
6052 tree_view->priv->press_start_y,
6061 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6062 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6066 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6069 /* Now we can begin the drag */
6073 context = gtk_drag_begin (widget,
6074 gtk_drag_source_get_target_list (widget),
6079 set_source_row (context, model, path);
6083 gtk_tree_path_free (path);
6091 pspp_sheet_view_drag_begin (GtkWidget *widget,
6092 GdkDragContext *context)
6095 PsppSheetView *tree_view;
6096 GtkTreePath *path = NULL;
6097 gint cell_x, cell_y;
6099 TreeViewDragInfo *di;
6101 tree_view = PSPP_SHEET_VIEW (widget);
6103 /* if the user uses a custom DND source impl, we don't set the icon here */
6104 di = get_info (tree_view);
6106 if (di == NULL || !di->source_set)
6109 pspp_sheet_view_get_path_at_pos (tree_view,
6110 tree_view->priv->press_start_x,
6111 tree_view->priv->press_start_y,
6117 g_return_if_fail (path != NULL);
6119 row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6122 gtk_drag_set_icon_pixmap (context,
6123 gdk_drawable_get_colormap (row_pix),
6126 /* the + 1 is for the black border in the icon */
6127 tree_view->priv->press_start_x + 1,
6130 g_object_unref (row_pix);
6131 gtk_tree_path_free (path);
6137 pspp_sheet_view_drag_end (GtkWidget *widget,
6138 GdkDragContext *context)
6143 /* Default signal implementations for the drag signals */
6145 pspp_sheet_view_drag_data_get (GtkWidget *widget,
6146 GdkDragContext *context,
6147 GtkSelectionData *selection_data,
6151 PsppSheetView *tree_view;
6152 GtkTreeModel *model;
6153 TreeViewDragInfo *di;
6154 GtkTreePath *source_row;
6156 tree_view = PSPP_SHEET_VIEW (widget);
6158 model = pspp_sheet_view_get_model (tree_view);
6163 di = get_info (PSPP_SHEET_VIEW (widget));
6168 source_row = get_source_row (context);
6170 if (source_row == NULL)
6173 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6174 * any model; for DragSource models there are some other targets
6178 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6179 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6184 /* If drag_data_get does nothing, try providing row data. */
6185 if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6187 gtk_tree_set_row_drag_data (selection_data,
6193 gtk_tree_path_free (source_row);
6198 pspp_sheet_view_drag_data_delete (GtkWidget *widget,
6199 GdkDragContext *context)
6201 TreeViewDragInfo *di;
6202 GtkTreeModel *model;
6203 PsppSheetView *tree_view;
6204 GtkTreePath *source_row;
6206 tree_view = PSPP_SHEET_VIEW (widget);
6207 model = pspp_sheet_view_get_model (tree_view);
6209 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6212 di = get_info (tree_view);
6217 source_row = get_source_row (context);
6219 if (source_row == NULL)
6222 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6225 gtk_tree_path_free (source_row);
6227 set_source_row (context, NULL, NULL);
6231 pspp_sheet_view_drag_leave (GtkWidget *widget,
6232 GdkDragContext *context,
6235 /* unset any highlight row */
6236 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6238 PSPP_SHEET_VIEW_DROP_BEFORE);
6240 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6245 pspp_sheet_view_drag_motion (GtkWidget *widget,
6246 GdkDragContext *context,
6247 /* coordinates relative to the widget */
6253 GtkTreePath *path = NULL;
6254 PsppSheetViewDropPosition pos;
6255 PsppSheetView *tree_view;
6256 GdkDragAction suggested_action = 0;
6259 tree_view = PSPP_SHEET_VIEW (widget);
6261 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6264 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6266 /* we only know this *after* set_desination_row */
6267 empty = tree_view->priv->empty_view_drop;
6269 if (path == NULL && !empty)
6271 /* Can't drop here. */
6272 gdk_drag_status (context, 0, time);
6276 if (tree_view->priv->open_dest_timeout == 0 &&
6277 (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6278 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6284 add_scroll_timeout (tree_view);
6287 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6289 /* Request data so we can use the source row when
6290 * determining whether to accept the drop
6292 set_status_pending (context, suggested_action);
6293 gtk_drag_get_data (widget, context, target, time);
6297 set_status_pending (context, 0);
6298 gdk_drag_status (context, suggested_action, time);
6303 gtk_tree_path_free (path);
6310 pspp_sheet_view_drag_drop (GtkWidget *widget,
6311 GdkDragContext *context,
6312 /* coordinates relative to the widget */
6317 PsppSheetView *tree_view;
6319 GdkDragAction suggested_action = 0;
6320 GdkAtom target = GDK_NONE;
6321 TreeViewDragInfo *di;
6322 GtkTreeModel *model;
6323 gboolean path_down_mode;
6324 gboolean drop_append_mode;
6326 tree_view = PSPP_SHEET_VIEW (widget);
6328 model = pspp_sheet_view_get_model (tree_view);
6330 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6332 di = get_info (tree_view);
6337 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6340 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6343 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6345 if (target != GDK_NONE && path != NULL)
6347 /* in case a motion had requested drag data, change things so we
6348 * treat drag data receives as a drop.
6350 set_status_pending (context, 0);
6351 set_dest_row (context, model, path,
6352 path_down_mode, tree_view->priv->empty_view_drop,
6357 gtk_tree_path_free (path);
6359 /* Unset this thing */
6360 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6362 PSPP_SHEET_VIEW_DROP_BEFORE);
6364 if (target != GDK_NONE)
6366 gtk_drag_get_data (widget, context, target, time);
6374 pspp_sheet_view_drag_data_received (GtkWidget *widget,
6375 GdkDragContext *context,
6376 /* coordinates relative to the widget */
6379 GtkSelectionData *selection_data,
6384 TreeViewDragInfo *di;
6385 gboolean accepted = FALSE;
6386 GtkTreeModel *model;
6387 PsppSheetView *tree_view;
6388 GtkTreePath *dest_row;
6389 GdkDragAction suggested_action;
6390 gboolean path_down_mode;
6391 gboolean drop_append_mode;
6393 tree_view = PSPP_SHEET_VIEW (widget);
6395 model = pspp_sheet_view_get_model (tree_view);
6397 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6400 di = get_info (tree_view);
6405 suggested_action = get_status_pending (context);
6407 if (suggested_action)
6409 /* We are getting this data due to a request in drag_motion,
6410 * rather than due to a request in drag_drop, so we are just
6411 * supposed to call drag_status, not actually paste in the
6414 path = get_logical_dest_row (tree_view, &path_down_mode,
6418 suggested_action = 0;
6419 else if (path_down_mode)
6420 gtk_tree_path_down (path);
6422 if (suggested_action)
6424 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6430 path_down_mode = FALSE;
6431 gtk_tree_path_up (path);
6433 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6436 suggested_action = 0;
6439 suggested_action = 0;
6443 gdk_drag_status (context, suggested_action, time);
6446 gtk_tree_path_free (path);
6448 /* If you can't drop, remove user drop indicator until the next motion */
6449 if (suggested_action == 0)
6450 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6452 PSPP_SHEET_VIEW_DROP_BEFORE);
6457 dest_row = get_dest_row (context, &path_down_mode);
6459 if (dest_row == NULL)
6462 if (gtk_selection_data_get_length (selection_data) >= 0)
6466 gtk_tree_path_down (dest_row);
6467 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6468 dest_row, selection_data))
6469 gtk_tree_path_up (dest_row);
6473 if (gtk_selection_data_get_length (selection_data) >= 0)
6475 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6481 gtk_drag_finish (context,
6483 (gdk_drag_context_get_actions (context) == GDK_ACTION_MOVE),
6486 if (gtk_tree_path_get_depth (dest_row) == 1
6487 && gtk_tree_path_get_indices (dest_row)[0] == 0)
6489 /* special special case drag to "0", scroll to first item */
6490 if (!tree_view->priv->scroll_to_path)
6491 pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6494 gtk_tree_path_free (dest_row);
6497 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6502 /* GtkContainer Methods
6507 pspp_sheet_view_remove (GtkContainer *container,
6510 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6511 PsppSheetViewChild *child = NULL;
6514 tmp_list = tree_view->priv->children;
6517 child = tmp_list->data;
6518 if (child->widget == widget)
6520 gtk_widget_unparent (widget);
6522 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6523 g_list_free_1 (tmp_list);
6524 g_slice_free (PsppSheetViewChild, child);
6528 tmp_list = tmp_list->next;
6531 tmp_list = tree_view->priv->columns;
6535 PsppSheetViewColumn *column;
6536 column = tmp_list->data;
6537 if (column->button == widget)
6539 gtk_widget_unparent (widget);
6542 tmp_list = tmp_list->next;
6547 pspp_sheet_view_forall (GtkContainer *container,
6548 gboolean include_internals,
6549 GtkCallback callback,
6550 gpointer callback_data)
6552 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6553 PsppSheetViewChild *child = NULL;
6554 PsppSheetViewColumn *column;
6557 tmp_list = tree_view->priv->children;
6560 child = tmp_list->data;
6561 tmp_list = tmp_list->next;
6563 (* callback) (child->widget, callback_data);
6565 if (include_internals == FALSE)
6568 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6570 column = tmp_list->data;
6573 (* callback) (column->button, callback_data);
6577 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6578 * cells. If so we draw one big row-spanning focus rectangle.
6581 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6585 if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6586 return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6588 for (list = tree_view->priv->columns; list; list = list->next)
6590 if (!((PsppSheetViewColumn *)list->data)->visible)
6592 if (_pspp_sheet_view_column_count_special_cells (list->data))
6600 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6601 PsppSheetViewColumn *focus_column,
6602 gboolean clamp_column_visible)
6604 g_return_if_fail (focus_column != NULL);
6606 tree_view->priv->focus_column = focus_column;
6608 if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view)) != focus_column->button)
6609 gtk_widget_grab_focus (focus_column->button);
6611 if (clamp_column_visible)
6612 pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6615 /* Returns TRUE if the focus is within the headers, after the focus operation is
6619 pspp_sheet_view_header_focus (PsppSheetView *tree_view,
6620 GtkDirectionType dir,
6621 gboolean clamp_column_visible)
6623 GtkWidget *focus_child;
6624 PsppSheetViewColumn *focus_column;
6625 GList *last_column, *first_column;
6629 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6632 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
6634 first_column = tree_view->priv->columns;
6635 while (first_column)
6637 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6639 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6641 first_column = first_column->next;
6644 /* No headers are visible, or are focusable. We can't focus in or out.
6646 if (first_column == NULL)
6649 last_column = g_list_last (tree_view->priv->columns);
6652 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6654 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6656 last_column = last_column->prev;
6660 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6664 case GTK_DIR_TAB_BACKWARD:
6665 case GTK_DIR_TAB_FORWARD:
6668 if (focus_child == NULL)
6670 if (tree_view->priv->focus_column != NULL &&
6671 pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6672 focus_column = tree_view->priv->focus_column;
6674 focus_column = first_column->data;
6675 pspp_sheet_view_focus_column (tree_view, focus_column,
6676 clamp_column_visible);
6683 if (focus_child == NULL)
6685 if (tree_view->priv->focus_column != NULL)
6686 focus_column = tree_view->priv->focus_column;
6687 else if (dir == GTK_DIR_LEFT)
6688 focus_column = last_column->data;
6690 focus_column = first_column->data;
6691 pspp_sheet_view_focus_column (tree_view, focus_column,
6692 clamp_column_visible);
6696 if (gtk_widget_child_focus (focus_child, dir))
6698 /* The focus moves inside the button. */
6699 /* This is probably a great example of bad UI */
6700 if (clamp_column_visible)
6701 pspp_sheet_view_clamp_column_visible (tree_view,
6702 tree_view->priv->focus_column,
6707 /* We need to move the focus among the row of buttons. */
6708 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6709 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6712 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6713 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6715 gtk_widget_error_bell (GTK_WIDGET (tree_view));
6721 PsppSheetViewColumn *column;
6723 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6724 tmp_list = tmp_list->next;
6726 tmp_list = tmp_list->prev;
6728 if (tmp_list == NULL)
6730 g_warning ("Internal button not found");
6733 column = tmp_list->data;
6734 if (column->visible &&
6735 pspp_sheet_view_column_can_focus (column))
6739 pspp_sheet_view_focus_column (tree_view, column,
6740 clamp_column_visible);
6748 g_assert_not_reached ();
6755 /* This function returns in 'path' the first focusable path, if the given path
6756 * is already focusable, it's the returned one.
6760 search_first_focusable_path (PsppSheetView *tree_view,
6762 gboolean search_forward,
6765 /* XXX this function is trivial given that the sheetview doesn't support
6769 if (!path || !*path)
6772 _pspp_sheet_view_find_node (tree_view, *path, &node);
6780 return (*path != NULL);
6784 pspp_sheet_view_focus (GtkWidget *widget,
6785 GtkDirectionType direction)
6787 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6788 GtkContainer *container = GTK_CONTAINER (widget);
6789 GtkWidget *focus_child;
6791 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6794 focus_child = gtk_container_get_focus_child (container);
6796 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6797 /* Case 1. Headers currently have focus. */
6804 pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6806 case GTK_DIR_TAB_BACKWARD:
6809 case GTK_DIR_TAB_FORWARD:
6811 gtk_widget_grab_focus (widget);
6814 g_assert_not_reached ();
6819 /* Case 2. We don't have focus at all. */
6820 if (!gtk_widget_has_focus (widget))
6822 if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6823 gtk_widget_grab_focus (widget);
6827 /* Case 3. We have focus already. */
6828 if (direction == GTK_DIR_TAB_BACKWARD)
6829 return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6830 else if (direction == GTK_DIR_TAB_FORWARD)
6833 /* Other directions caught by the keybindings */
6834 gtk_widget_grab_focus (widget);
6839 pspp_sheet_view_grab_focus (GtkWidget *widget)
6841 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6843 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6847 pspp_sheet_view_style_updated (GtkWidget *widget)
6849 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6851 PsppSheetViewColumn *column;
6852 GtkStyleContext *context;
6854 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->style_updated (widget);
6856 if (gtk_widget_get_realized (widget))
6858 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
6859 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
6860 gtk_style_context_set_background (context, tree_view->priv->header_window);
6861 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6864 gtk_widget_style_get (widget,
6865 "expander-size", &tree_view->priv->expander_size,
6867 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6869 for (list = tree_view->priv->columns; list; list = list->next)
6871 column = list->data;
6872 _pspp_sheet_view_column_cell_set_dirty (column);
6875 tree_view->priv->fixed_height = -1;
6877 /* Invalidate cached button style. */
6878 if (tree_view->priv->button_style)
6880 g_object_unref (tree_view->priv->button_style);
6881 tree_view->priv->button_style = NULL;
6884 gtk_widget_queue_resize (widget);
6889 pspp_sheet_view_set_focus_child (GtkContainer *container,
6892 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6895 for (list = tree_view->priv->columns; list; list = list->next)
6897 if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
6899 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
6904 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
6908 pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
6909 GtkAdjustment *hadj,
6910 GtkAdjustment *vadj)
6912 gboolean need_adjust = FALSE;
6914 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
6917 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
6919 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6921 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
6923 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6925 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
6927 g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
6928 pspp_sheet_view_adjustment_changed,
6930 g_object_unref (tree_view->priv->hadjustment);
6933 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
6935 g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
6936 pspp_sheet_view_adjustment_changed,
6938 g_object_unref (tree_view->priv->vadjustment);
6941 if (tree_view->priv->hadjustment != hadj)
6943 tree_view->priv->hadjustment = hadj;
6944 g_object_ref_sink (tree_view->priv->hadjustment);
6946 g_signal_connect (tree_view->priv->hadjustment, "value-changed",
6947 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6952 if (tree_view->priv->vadjustment != vadj)
6954 tree_view->priv->vadjustment = vadj;
6955 g_object_ref_sink (tree_view->priv->vadjustment);
6957 g_signal_connect (tree_view->priv->vadjustment, "value-changed",
6958 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6964 pspp_sheet_view_adjustment_changed (NULL, tree_view);
6969 pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
6970 GtkMovementStep step,
6973 PsppSheetSelectMode mode;
6974 GdkModifierType state;
6976 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
6977 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
6978 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
6979 step == GTK_MOVEMENT_DISPLAY_LINES ||
6980 step == GTK_MOVEMENT_PAGES ||
6981 step == GTK_MOVEMENT_BUFFER_ENDS ||
6982 step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
6984 if (tree_view->priv->row_count == 0)
6986 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
6989 pspp_sheet_view_stop_editing (tree_view, FALSE);
6990 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
6991 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
6994 if (gtk_get_current_event_state (&state))
6996 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
6997 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
6998 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
6999 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
7001 /* else we assume not pressed */
7005 case GTK_MOVEMENT_LOGICAL_POSITIONS:
7006 pspp_sheet_view_move_cursor_tab (tree_view, count);
7008 case GTK_MOVEMENT_VISUAL_POSITIONS:
7009 pspp_sheet_view_move_cursor_left_right (tree_view, count, mode);
7011 case GTK_MOVEMENT_DISPLAY_LINES:
7012 pspp_sheet_view_move_cursor_up_down (tree_view, count, mode);
7014 case GTK_MOVEMENT_PAGES:
7015 pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode);
7017 case GTK_MOVEMENT_BUFFER_ENDS:
7018 pspp_sheet_view_move_cursor_start_end (tree_view, count, mode);
7020 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7021 pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode);
7024 g_assert_not_reached ();
7031 pspp_sheet_view_put (PsppSheetView *tree_view,
7032 GtkWidget *child_widget,
7034 PsppSheetViewColumn *column)
7036 PsppSheetViewChild *child;
7038 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7039 g_return_if_fail (GTK_IS_WIDGET (child_widget));
7041 child = g_slice_new (PsppSheetViewChild);
7043 child->widget = child_widget;
7044 _pspp_sheet_view_find_node (tree_view, path, &child->node);
7045 if (child->node < 0)
7047 g_assert_not_reached ();
7049 child->column = column;
7051 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7053 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7054 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7056 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7059 /* TreeModel Callbacks
7063 pspp_sheet_view_row_changed (GtkTreeModel *model,
7068 PsppSheetView *tree_view = (PsppSheetView *)data;
7070 gboolean free_path = FALSE;
7071 GtkTreePath *cursor_path;
7073 g_return_if_fail (path != NULL || iter != NULL);
7075 if (tree_view->priv->cursor != NULL)
7076 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7080 if (tree_view->priv->edited_column &&
7081 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7082 pspp_sheet_view_stop_editing (tree_view, TRUE);
7084 if (cursor_path != NULL)
7085 gtk_tree_path_free (cursor_path);
7089 path = gtk_tree_model_get_path (model, iter);
7092 else if (iter == NULL)
7093 gtk_tree_model_get_iter (model, iter, path);
7095 _pspp_sheet_view_find_node (tree_view,
7101 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7102 pspp_sheet_view_node_queue_redraw (tree_view, node);
7106 gtk_tree_path_free (path);
7110 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7115 PsppSheetView *tree_view = (PsppSheetView *) data;
7118 gint height = tree_view->priv->fixed_height;
7119 gboolean free_path = FALSE;
7120 gboolean node_visible = TRUE;
7122 g_return_if_fail (path != NULL || iter != NULL);
7126 path = gtk_tree_model_get_path (model, iter);
7129 else if (iter == NULL)
7130 gtk_tree_model_get_iter (model, iter, path);
7132 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7134 /* Update all row-references */
7135 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7136 indices = gtk_tree_path_get_indices (path);
7137 tmpnode = indices[0];
7139 range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7143 if (node_visible && node_is_visible (tree_view, tmpnode))
7144 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7146 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7149 install_presize_handler (tree_view);
7151 gtk_tree_path_free (path);
7155 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7159 PsppSheetView *tree_view = (PsppSheetView *)data;
7162 g_return_if_fail (path != NULL);
7164 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7166 _pspp_sheet_view_find_node (tree_view, path, &node);
7171 range_tower_delete (tree_view->priv->selected, node, 1);
7173 /* Ensure we don't have a dangling pointer to a dead node */
7174 ensure_unprelighted (tree_view);
7176 /* Cancel editting if we've started */
7177 pspp_sheet_view_stop_editing (tree_view, TRUE);
7179 if (tree_view->priv->destroy_count_func)
7181 gint child_count = 0;
7182 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7185 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7187 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7189 gtk_tree_row_reference_free (tree_view->priv->top_row);
7190 tree_view->priv->top_row = NULL;
7193 install_scroll_sync_handler (tree_view);
7195 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7198 if (helper_data.changed)
7199 g_signal_emit_by_name (tree_view->priv->selection, "changed");
7204 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7205 GtkTreePath *parent,
7210 PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7213 /* XXX need to adjust selection */
7214 len = gtk_tree_model_iter_n_children (model, iter);
7219 gtk_tree_row_reference_reordered (G_OBJECT (data),
7224 if (gtk_tree_path_get_depth (parent) != 0)
7227 if (tree_view->priv->edited_column)
7228 pspp_sheet_view_stop_editing (tree_view, TRUE);
7230 /* we need to be unprelighted */
7231 ensure_unprelighted (tree_view);
7233 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7235 pspp_sheet_view_dy_to_top_row (tree_view);
7239 /* Internal tree functions
7244 pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view,
7245 PsppSheetViewColumn *column,
7249 PsppSheetViewColumn *tmp_column = NULL;
7260 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7263 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7265 list = (rtl ? list->prev : list->next))
7267 tmp_column = list->data;
7269 if (tmp_column == column)
7272 if (tmp_column->visible)
7273 total_width += tmp_column->width;
7276 if (tmp_column != column)
7278 g_warning (G_STRLOC": passed-in column isn't in the tree");
7287 if (column->visible)
7288 *x2 = total_width + column->width;
7290 *x2 = total_width; /* width of 0 */
7294 /* Make sure the node is visible vertically */
7296 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7299 gint node_dy, height;
7300 GtkTreePath *path = NULL;
7302 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7305 /* just return if the node is visible, avoiding a costly expose */
7306 node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7307 height = ROW_HEIGHT (tree_view);
7308 if (node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment)
7309 && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
7310 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
7313 path = _pspp_sheet_view_find_path (tree_view, node);
7316 /* We process updates because we want to clear old selected items when we scroll.
7317 * if this is removed, we get a "selection streak" at the bottom. */
7318 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7319 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7320 gtk_tree_path_free (path);
7325 pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
7326 PsppSheetViewColumn *column,
7327 gboolean focus_to_cell)
7334 x = column->allocation.x;
7335 width = column->allocation.width;
7337 if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7339 /* The column is larger than the horizontal page size. If the
7340 * column has cells which can be focussed individually, then we make
7341 * sure the cell which gets focus is fully visible (if even the
7342 * focus cell is bigger than the page size, we make sure the
7343 * left-hand side of the cell is visible).
7345 * If the column does not have those so-called special cells, we
7346 * make sure the left-hand side of the column is visible.
7349 if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7351 GtkTreePath *cursor_path;
7352 GdkRectangle background_area, cell_area, focus_area;
7354 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7356 pspp_sheet_view_get_cell_area (tree_view,
7357 cursor_path, column, &cell_area);
7358 pspp_sheet_view_get_background_area (tree_view,
7359 cursor_path, column,
7362 gtk_tree_path_free (cursor_path);
7364 _pspp_sheet_view_column_get_focus_area (column,
7370 width = focus_area.width;
7372 if (width < gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7374 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7375 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7376 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7377 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7378 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7382 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7384 gtk_adjustment_get_lower (tree_view->priv->hadjustment),
7385 gtk_adjustment_get_upper (tree_view->priv->hadjustment)
7386 - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)));
7390 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7391 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7392 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7393 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7394 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7399 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7404 path = gtk_tree_path_new ();
7406 gtk_tree_path_append_index (path, node);
7411 _pspp_sheet_view_find_node (PsppSheetView *tree_view,
7415 gint *indices = gtk_tree_path_get_indices (path);
7416 gint depth = gtk_tree_path_get_depth (path);
7419 if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7425 pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
7428 gboolean add_shifted_binding,
7429 GtkMovementStep step,
7433 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7438 if (add_shifted_binding)
7439 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7444 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7447 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7452 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7459 pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view,
7460 PsppSheetViewColumn *column)
7462 PsppSheetViewColumn *left_column;
7463 PsppSheetViewColumn *cur_column = NULL;
7464 PsppSheetViewColumnReorder *reorder;
7469 /* We want to precalculate the motion list such that we know what column slots
7473 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7475 /* First, identify all possible drop spots */
7477 tmp_list = g_list_last (tree_view->priv->columns);
7479 tmp_list = g_list_first (tree_view->priv->columns);
7483 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7484 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7486 if (cur_column->visible == FALSE)
7489 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7490 if (left_column != column && cur_column != column &&
7491 tree_view->priv->column_drop_func &&
7492 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7494 left_column = cur_column;
7497 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7498 reorder->left_column = left_column;
7499 left_column = reorder->right_column = cur_column;
7501 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7504 /* Add the last one */
7505 if (tree_view->priv->column_drop_func == NULL ||
7506 ((left_column != column) &&
7507 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7509 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7510 reorder->left_column = left_column;
7511 reorder->right_column = NULL;
7512 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7515 /* We quickly check to see if it even makes sense to reorder columns. */
7516 /* If there is nothing that can be moved, then we return */
7518 if (tree_view->priv->column_drag_info == NULL)
7521 /* We know there are always 2 slots possbile, as you can always return column. */
7522 /* If that's all there is, return */
7523 if (tree_view->priv->column_drag_info->next == NULL ||
7524 (tree_view->priv->column_drag_info->next->next == NULL &&
7525 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7526 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7528 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7529 g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7530 g_list_free (tree_view->priv->column_drag_info);
7531 tree_view->priv->column_drag_info = NULL;
7534 /* We fill in the ranges for the columns, now that we've isolated them */
7535 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7537 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7539 reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7541 reorder->left_align = left;
7542 if (tmp_list->next != NULL)
7544 g_assert (tmp_list->next->data);
7545 left = reorder->right_align = (reorder->right_column->allocation.x +
7546 reorder->right_column->allocation.width +
7547 ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7551 gint width = gdk_window_get_width (tree_view->priv->header_window);
7552 reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7558 _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view,
7559 PsppSheetViewColumn *column)
7561 GdkEvent *send_event;
7562 GtkAllocation allocation;
7564 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7565 GdkDisplay *display = gdk_screen_get_display (screen);
7567 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7568 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7569 g_return_if_fail (column->button);
7571 pspp_sheet_view_set_column_drag_info (tree_view, column);
7573 if (tree_view->priv->column_drag_info == NULL)
7576 if (tree_view->priv->drag_window == NULL)
7578 GdkWindowAttr attributes;
7579 guint attributes_mask;
7581 attributes.window_type = GDK_WINDOW_CHILD;
7582 attributes.wclass = GDK_INPUT_OUTPUT;
7583 attributes.x = column->allocation.x;
7585 attributes.width = column->allocation.width;
7586 attributes.height = column->allocation.height;
7587 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7588 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7589 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL ;
7591 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7594 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7597 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7598 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7600 gtk_grab_remove (column->button);
7602 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7603 send_event->crossing.send_event = TRUE;
7604 send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button)));
7605 send_event->crossing.subwindow = NULL;
7606 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7607 send_event->crossing.time = GDK_CURRENT_TIME;
7609 gtk_propagate_event (column->button, send_event);
7610 gdk_event_free (send_event);
7612 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7613 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7614 send_event->button.send_event = TRUE;
7615 send_event->button.time = GDK_CURRENT_TIME;
7616 send_event->button.x = -1;
7617 send_event->button.y = -1;
7618 send_event->button.axes = NULL;
7619 send_event->button.state = 0;
7620 send_event->button.button = 1;
7621 send_event->button.device =
7622 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
7624 send_event->button.x_root = 0;
7625 send_event->button.y_root = 0;
7627 gtk_propagate_event (column->button, send_event);
7628 gdk_event_free (send_event);
7630 /* Kids, don't try this at home */
7631 g_object_ref (column->button);
7632 gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7633 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7634 gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7635 g_object_unref (column->button);
7637 tree_view->priv->drag_column_x = column->allocation.x;
7638 allocation = column->allocation;
7640 gtk_widget_size_allocate (column->button, &allocation);
7641 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7643 tree_view->priv->drag_column = column;
7644 gdk_window_show (tree_view->priv->drag_window);
7646 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7648 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7649 while (gtk_events_pending ())
7650 gtk_main_iteration ();
7652 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7653 gdk_pointer_grab (tree_view->priv->drag_window,
7655 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7656 NULL, NULL, GDK_CURRENT_TIME);
7657 gdk_keyboard_grab (tree_view->priv->drag_window,
7663 _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view,
7665 const GdkRectangle *clip_rect)
7668 GtkAllocation allocation;
7670 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7673 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
7675 rect.width = MAX (tree_view->priv->width, allocation.width);
7677 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7678 rect.height = ROW_HEIGHT (tree_view);
7682 GdkRectangle new_rect;
7684 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7686 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7690 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7695 pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
7697 const GdkRectangle *clip_rect)
7701 _pspp_sheet_view_find_node (tree_view, path, &node);
7704 _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7708 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7711 GtkTreePath *cursor_path;
7713 if ((tree_view->priv->row_count == 0) ||
7714 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7718 if (tree_view->priv->cursor)
7719 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7721 if (cursor_path == NULL)
7723 /* There's no cursor. Move the cursor to the first selected row, if any
7724 * are selected, otherwise to the first row in the sheetview.
7726 GList *selected_rows;
7727 GtkTreeModel *model;
7728 PsppSheetSelection *selection;
7730 selection = pspp_sheet_view_get_selection (tree_view);
7731 selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7735 /* XXX we could avoid doing O(n) work to get this result */
7736 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7737 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7738 g_list_free (selected_rows);
7742 cursor_path = gtk_tree_path_new_first ();
7743 search_first_focusable_path (tree_view, &cursor_path,
7747 gtk_tree_row_reference_free (tree_view->priv->cursor);
7748 tree_view->priv->cursor = NULL;
7752 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7753 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7754 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0);
7756 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0);
7762 /* Now find a column for the cursor. */
7763 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7765 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7766 gtk_tree_path_free (cursor_path);
7768 if (tree_view->priv->focus_column == NULL)
7771 for (list = tree_view->priv->columns; list; list = list->next)
7773 if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7775 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7776 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7777 pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7787 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7789 PsppSheetSelectMode mode)
7791 gint selection_count;
7792 int cursor_node = -1;
7793 int new_cursor_node = -1;
7794 GtkTreePath *cursor_path = NULL;
7795 gboolean grab_focus = TRUE;
7797 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7801 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7802 /* FIXME: we lost the cursor; should we get the first? */
7805 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7806 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7808 if (cursor_node < 0)
7809 /* FIXME: we lost the cursor; should we get the first? */
7812 selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7814 if (selection_count == 0
7815 && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7816 && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
7818 /* Don't move the cursor, but just select the current node */
7819 new_cursor_node = cursor_node;
7824 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7826 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7829 gtk_tree_path_free (cursor_path);
7831 if (new_cursor_node)
7833 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7835 search_first_focusable_path (tree_view, &cursor_path,
7840 gtk_tree_path_free (cursor_path);
7844 * If the list has only one item and multi-selection is set then select
7845 * the row (if not yet selected).
7847 if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7848 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7849 new_cursor_node < 0)
7852 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7854 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7856 if (new_cursor_node < 0
7857 && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
7859 new_cursor_node = cursor_node;
7863 new_cursor_node = -1;
7867 if (new_cursor_node >= 0)
7869 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7870 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode);
7871 gtk_tree_path_free (cursor_path);
7875 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7877 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
7879 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
7881 GTK_DIR_UP : GTK_DIR_DOWN))
7883 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
7886 gtk_widget_child_focus (toplevel,
7888 GTK_DIR_TAB_BACKWARD :
7889 GTK_DIR_TAB_FORWARD);
7896 gtk_widget_error_bell (GTK_WIDGET (tree_view));
7901 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7903 return new_cursor_node >= 0;
7907 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
7909 PsppSheetSelectMode mode)
7911 int cursor_node = -1;
7912 GtkTreePath *old_cursor_path = NULL;
7913 GtkTreePath *cursor_path = NULL;
7914 int start_cursor_node = -1;
7917 gint vertical_separator;
7919 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7922 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
7923 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7925 /* This is sorta weird. Focus in should give us a cursor */
7928 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
7929 _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
7931 if (cursor_node < 0)
7933 /* FIXME: we lost the cursor. Should we try to get one? */
7934 gtk_tree_path_free (old_cursor_path);
7938 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7939 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
7940 y += tree_view->priv->cursor_offset;
7941 y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment);
7942 y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment), (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator);
7944 if (y >= tree_view->priv->height)
7945 y = tree_view->priv->height - 1;
7947 tree_view->priv->cursor_offset =
7948 pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
7950 if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
7952 cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7953 tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
7956 y -= tree_view->priv->cursor_offset;
7957 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7959 start_cursor_node = cursor_node;
7961 if (! search_first_focusable_path (tree_view, &cursor_path,
7965 /* It looks like we reached the end of the view without finding
7966 * a focusable row. We will step backwards to find the last
7969 cursor_node = start_cursor_node;
7970 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7972 search_first_focusable_path (tree_view, &cursor_path,
7981 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7983 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode);
7986 pspp_sheet_view_scroll_to_point (tree_view, -1, y);
7987 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7988 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
7990 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
7991 gtk_widget_error_bell (GTK_WIDGET (tree_view));
7993 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7996 gtk_tree_path_free (old_cursor_path);
7997 gtk_tree_path_free (cursor_path);
8001 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8003 PsppSheetSelectMode mode)
8005 int cursor_node = -1;
8006 GtkTreePath *cursor_path = NULL;
8007 PsppSheetViewColumn *column;
8010 gboolean found_column = FALSE;
8013 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8015 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8018 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8019 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8023 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8024 if (cursor_node < 0)
8026 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8028 gtk_tree_path_free (cursor_path);
8031 gtk_tree_path_free (cursor_path);
8033 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8034 if (tree_view->priv->focus_column)
8036 for (; list; list = (rtl ? list->prev : list->next))
8038 if (list->data == tree_view->priv->focus_column)
8045 gboolean left, right;
8047 column = list->data;
8048 if (column->visible == FALSE || column->row_head)
8051 pspp_sheet_view_column_cell_set_cell_data (column,
8052 tree_view->priv->model,
8057 right = list->prev ? TRUE : FALSE;
8058 left = list->next ? TRUE : FALSE;
8062 left = list->prev ? TRUE : FALSE;
8063 right = list->next ? TRUE : FALSE;
8066 if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8068 tree_view->priv->focus_column = column;
8069 found_column = TRUE;
8074 list = rtl ? list->prev : list->next;
8076 list = rtl ? list->next : list->prev;
8081 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8082 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8083 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8087 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8090 pspp_sheet_view_clamp_column_visible (tree_view,
8091 tree_view->priv->focus_column, TRUE);
8095 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8097 PsppSheetSelectMode mode)
8099 int cursor_node = -1;
8100 GtkTreePath *cursor_path = NULL;
8101 PsppSheetViewColumn *column;
8102 PsppSheetViewColumn *found_column;
8107 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8109 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8112 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8113 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8117 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8118 if (cursor_node < 0)
8120 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8122 gtk_tree_path_free (cursor_path);
8125 gtk_tree_path_free (cursor_path);
8127 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8128 if (tree_view->priv->focus_column)
8130 for (; list; list = (rtl ? list->prev : list->next))
8132 if (list->data == tree_view->priv->focus_column)
8137 found_column = NULL;
8140 gboolean left, right;
8142 column = list->data;
8143 if (column->visible == FALSE || column->row_head)
8146 pspp_sheet_view_column_cell_set_cell_data (column,
8147 tree_view->priv->model,
8152 right = list->prev ? TRUE : FALSE;
8153 left = list->next ? TRUE : FALSE;
8157 left = list->prev ? TRUE : FALSE;
8158 right = list->next ? TRUE : FALSE;
8161 if (column->tabbable
8162 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8163 found_column = column;
8167 list = rtl ? list->prev : list->next;
8169 list = rtl ? list->next : list->prev;
8174 tree_view->priv->focus_column = found_column;
8175 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8176 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8177 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8180 pspp_sheet_view_clamp_column_visible (tree_view,
8181 tree_view->priv->focus_column, TRUE);
8185 try_move_cursor_tab (PsppSheetView *tree_view,
8186 gboolean start_at_focus_column,
8189 PsppSheetViewColumn *column;
8191 int cursor_node = -1;
8192 GtkTreePath *cursor_path = NULL;
8196 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8197 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8201 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8202 if (cursor_node < 0)
8204 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8206 gtk_tree_path_free (cursor_path);
8209 gtk_tree_path_free (cursor_path);
8211 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8212 if (start_at_focus_column)
8215 ? g_list_last (tree_view->priv->columns)
8216 : g_list_first (tree_view->priv->columns));
8217 if (tree_view->priv->focus_column)
8219 for (; list; list = (rtl ? list->prev : list->next))
8221 if (list->data == tree_view->priv->focus_column)
8228 list = (rtl ^ (count == 1)
8229 ? g_list_first (tree_view->priv->columns)
8230 : g_list_last (tree_view->priv->columns));
8235 gboolean left, right;
8237 column = list->data;
8238 if (column->visible == FALSE || !column->tabbable)
8241 pspp_sheet_view_column_cell_set_cell_data (column,
8242 tree_view->priv->model,
8247 right = list->prev ? TRUE : FALSE;
8248 left = list->next ? TRUE : FALSE;
8252 left = list->prev ? TRUE : FALSE;
8253 right = list->next ? TRUE : FALSE;
8256 if (column->tabbable
8257 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8259 tree_view->priv->focus_column = column;
8260 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8261 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8262 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8267 list = rtl ? list->prev : list->next;
8269 list = rtl ? list->next : list->prev;
8276 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8279 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8282 if (!try_move_cursor_tab (tree_view, TRUE, count))
8284 if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0)
8285 && !try_move_cursor_tab (tree_view, FALSE, count))
8286 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8289 pspp_sheet_view_clamp_column_visible (tree_view,
8290 tree_view->priv->focus_column, TRUE);
8294 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8296 PsppSheetSelectMode mode)
8300 GtkTreePath *old_path;
8302 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8305 g_return_if_fail (tree_view->priv->row_count > 0);
8307 pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8311 /* Now go forward to find the first focusable row. */
8312 path = _pspp_sheet_view_find_path (tree_view, 0);
8313 search_first_focusable_path (tree_view, &path,
8314 TRUE, &cursor_node);
8318 /* Now go backwards to find last focusable row. */
8319 path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8320 search_first_focusable_path (tree_view, &path,
8321 FALSE, &cursor_node);
8327 if (gtk_tree_path_compare (old_path, path))
8329 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
8330 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8334 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8338 gtk_tree_path_free (old_path);
8339 gtk_tree_path_free (path);
8343 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8345 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8348 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8349 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8352 pspp_sheet_selection_select_all (tree_view->priv->selection);
8358 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8360 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8363 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8364 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8367 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8373 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8374 gboolean start_editing,
8375 PsppSheetSelectMode mode)
8378 int cursor_node = -1;
8379 GtkTreePath *cursor_path = NULL;
8381 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8384 if (tree_view->priv->cursor)
8385 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8387 if (cursor_path == NULL)
8390 _pspp_sheet_view_find_node (tree_view, cursor_path,
8393 if (cursor_node < 0)
8395 gtk_tree_path_free (cursor_path);
8399 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing &&
8400 tree_view->priv->focus_column)
8402 if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8404 gtk_tree_path_free (cursor_path);
8409 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8415 /* We bail out if the original (tree, node) don't exist anymore after
8416 * handling the selection-changed callback. We do return TRUE because
8417 * the key press has been handled at this point.
8419 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8421 if (cursor_node != new_node)
8424 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8426 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8427 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8429 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8430 pspp_sheet_view_row_activated (tree_view, cursor_path,
8431 tree_view->priv->focus_column);
8433 gtk_tree_path_free (cursor_path);
8439 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8442 int cursor_node = -1;
8443 GtkTreePath *cursor_path = NULL;
8445 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8449 if (tree_view->priv->cursor)
8450 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8452 if (cursor_path == NULL)
8455 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8456 if (cursor_node < 0)
8458 gtk_tree_path_free (cursor_path);
8462 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8465 PSPP_SHEET_SELECT_MODE_TOGGLE,
8468 /* We bail out if the original (tree, node) don't exist anymore after
8469 * handling the selection-changed callback. We do return TRUE because
8470 * the key press has been handled at this point.
8472 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8474 if (cursor_node != new_node)
8477 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8479 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8480 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8481 gtk_tree_path_free (cursor_path);
8487 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8489 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8490 tree_view->priv->typeselect_flush_timeout = 0;
8495 /* Cut and paste from gtkwindow.c */
8497 send_focus_change (GtkWidget *widget,
8500 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8502 fevent->focus_change.type = GDK_FOCUS_CHANGE;
8503 fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
8504 fevent->focus_change.in = in;
8506 gtk_widget_send_focus_change (widget, fevent);
8507 gdk_event_free (fevent);
8511 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8513 GtkWidget *frame, *vbox, *toplevel;
8516 if (tree_view->priv->search_custom_entry_set)
8519 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8520 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8522 if (tree_view->priv->search_window != NULL)
8524 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8525 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8526 GTK_WINDOW (tree_view->priv->search_window));
8527 else if (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)))
8528 gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
8529 GTK_WINDOW (tree_view->priv->search_window));
8530 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8534 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8535 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8537 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8538 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8539 GTK_WINDOW (tree_view->priv->search_window));
8541 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8542 GDK_WINDOW_TYPE_HINT_UTILITY);
8543 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8544 g_signal_connect (tree_view->priv->search_window, "delete-event",
8545 G_CALLBACK (pspp_sheet_view_search_delete_event),
8547 g_signal_connect (tree_view->priv->search_window, "key-press-event",
8548 G_CALLBACK (pspp_sheet_view_search_key_press_event),
8550 g_signal_connect (tree_view->priv->search_window, "button-press-event",
8551 G_CALLBACK (pspp_sheet_view_search_button_press_event),
8553 g_signal_connect (tree_view->priv->search_window, "scroll-event",
8554 G_CALLBACK (pspp_sheet_view_search_scroll_event),
8557 frame = gtk_frame_new (NULL);
8558 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8559 gtk_widget_show (frame);
8560 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8562 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
8563 gtk_widget_show (vbox);
8564 gtk_container_add (GTK_CONTAINER (frame), vbox);
8565 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8568 tree_view->priv->search_entry = gtk_entry_new ();
8569 gtk_widget_show (tree_view->priv->search_entry);
8570 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8571 G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8573 g_signal_connect (tree_view->priv->search_entry,
8574 "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8578 g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8580 G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8584 gtk_container_add (GTK_CONTAINER (vbox),
8585 tree_view->priv->search_entry);
8587 gtk_widget_realize (tree_view->priv->search_entry);
8590 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8591 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8594 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8595 gboolean keybinding)
8597 /* We only start interactive search if we have focus or the columns
8598 * have focus. If one of our children have focus, we don't want to
8602 gboolean found_focus = FALSE;
8603 GtkWidgetClass *entry_parent_class;
8605 if (!tree_view->priv->enable_search && !keybinding)
8608 if (tree_view->priv->search_custom_entry_set)
8611 if (tree_view->priv->search_window != NULL &&
8612 gtk_widget_get_visible (tree_view->priv->search_window))
8615 for (list = tree_view->priv->columns; list; list = list->next)
8617 PsppSheetViewColumn *column;
8619 column = list->data;
8620 if (! column->visible)
8623 if (column->button && gtk_widget_has_focus (column->button))
8630 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8636 if (tree_view->priv->search_column < 0)
8639 pspp_sheet_view_ensure_interactive_directory (tree_view);
8642 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8645 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8646 gtk_widget_show (tree_view->priv->search_window);
8647 if (tree_view->priv->search_entry_changed_id == 0)
8649 tree_view->priv->search_entry_changed_id =
8650 g_signal_connect (tree_view->priv->search_entry, "changed",
8651 G_CALLBACK (pspp_sheet_view_search_init),
8655 tree_view->priv->typeselect_flush_timeout =
8656 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8657 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8660 /* Grab focus will select all the text. We don't want that to happen, so we
8661 * call the parent instance and bypass the selection change. This is probably
8662 * really non-kosher. */
8663 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8664 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8666 /* send focus-in event */
8667 send_focus_change (tree_view->priv->search_entry, TRUE);
8669 /* search first matching iter */
8670 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8676 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8678 return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8681 /* this function returns the new width of the column being resized given
8682 * the column and x position of the cursor; the x cursor position is passed
8683 * in as a pointer and automagicly corrected if it's beyond min/max limits
8686 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8690 PsppSheetViewColumn *column;
8694 /* first translate the x position from gtk_widget_get_window (widget)
8695 * to clist->clist_window
8697 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8698 column = g_list_nth (tree_view->priv->columns, i)->data;
8699 width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8701 /* Clamp down the value */
8702 if (column->min_width == -1)
8703 width = MAX (column->button_request, width);
8705 width = MAX (column->min_width, width);
8706 if (column->max_width != -1)
8707 width = MIN (width, column->max_width);
8709 *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8715 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8719 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8720 PsppSheetView *tree_view)
8722 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8726 gdk_window_move (tree_view->priv->bin_window,
8727 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8728 TREE_VIEW_HEADER_HEIGHT (tree_view));
8729 gdk_window_move (tree_view->priv->header_window,
8730 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8732 dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8734 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8738 /* update our dy and top_row */
8739 tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8741 update_prelight (tree_view,
8742 tree_view->priv->event_last_x,
8743 tree_view->priv->event_last_y);
8745 if (!tree_view->priv->in_top_row_to_dy)
8746 pspp_sheet_view_dy_to_top_row (tree_view);
8749 update_childrens_allocation(tree_view);
8757 * pspp_sheet_view_new:
8759 * Creates a new #PsppSheetView widget.
8761 * Return value: A newly created #PsppSheetView widget.
8764 pspp_sheet_view_new (void)
8766 return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
8770 * pspp_sheet_view_new_with_model:
8771 * @model: the model.
8773 * Creates a new #PsppSheetView widget with the model initialized to @model.
8775 * Return value: A newly created #PsppSheetView widget.
8778 pspp_sheet_view_new_with_model (GtkTreeModel *model)
8780 return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
8787 * pspp_sheet_view_get_model:
8788 * @tree_view: a #PsppSheetView
8790 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
8793 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
8796 pspp_sheet_view_get_model (PsppSheetView *tree_view)
8798 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8800 return tree_view->priv->model;
8804 * pspp_sheet_view_set_model:
8805 * @tree_view: A #GtkTreeNode.
8806 * @model: (allow-none): The model.
8808 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
8809 * set, it will remove it before setting the new model. If @model is %NULL,
8810 * then it will unset the old model.
8813 pspp_sheet_view_set_model (PsppSheetView *tree_view,
8814 GtkTreeModel *model)
8816 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8817 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
8819 if (model == tree_view->priv->model)
8822 if (tree_view->priv->scroll_to_path)
8824 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8825 tree_view->priv->scroll_to_path = NULL;
8828 if (tree_view->priv->model)
8830 GList *tmplist = tree_view->priv->columns;
8832 if (tree_view->priv->selected)
8833 range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
8834 pspp_sheet_view_stop_editing (tree_view, TRUE);
8836 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8837 pspp_sheet_view_row_changed,
8839 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8840 pspp_sheet_view_row_inserted,
8842 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8843 pspp_sheet_view_row_deleted,
8845 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8846 pspp_sheet_view_rows_reordered,
8849 for (; tmplist; tmplist = tmplist->next)
8850 _pspp_sheet_view_column_unset_model (tmplist->data,
8851 tree_view->priv->model);
8853 tree_view->priv->prelight_node = -1;
8855 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
8856 tree_view->priv->drag_dest_row = NULL;
8857 gtk_tree_row_reference_free (tree_view->priv->cursor);
8858 tree_view->priv->cursor = NULL;
8859 gtk_tree_row_reference_free (tree_view->priv->anchor);
8860 tree_view->priv->anchor = NULL;
8861 gtk_tree_row_reference_free (tree_view->priv->top_row);
8862 tree_view->priv->top_row = NULL;
8863 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8864 tree_view->priv->scroll_to_path = NULL;
8866 tree_view->priv->scroll_to_column = NULL;
8868 g_object_unref (tree_view->priv->model);
8870 tree_view->priv->search_column = -1;
8871 tree_view->priv->fixed_height = -1;
8872 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
8873 tree_view->priv->last_button_x = -1;
8874 tree_view->priv->last_button_y = -1;
8877 tree_view->priv->model = model;
8879 if (tree_view->priv->model)
8883 if (tree_view->priv->search_column == -1)
8885 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
8887 GType type = gtk_tree_model_get_column_type (model, i);
8889 if (g_value_type_transformable (type, G_TYPE_STRING))
8891 tree_view->priv->search_column = i;
8897 g_object_ref (tree_view->priv->model);
8898 g_signal_connect (tree_view->priv->model,
8900 G_CALLBACK (pspp_sheet_view_row_changed),
8902 g_signal_connect (tree_view->priv->model,
8904 G_CALLBACK (pspp_sheet_view_row_inserted),
8906 g_signal_connect (tree_view->priv->model,
8908 G_CALLBACK (pspp_sheet_view_row_deleted),
8910 g_signal_connect (tree_view->priv->model,
8912 G_CALLBACK (pspp_sheet_view_rows_reordered),
8915 tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
8917 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
8918 install_presize_handler (tree_view);
8921 g_object_notify (G_OBJECT (tree_view), "model");
8923 if (tree_view->priv->selection)
8924 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
8926 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8927 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8931 * pspp_sheet_view_get_selection:
8932 * @tree_view: A #PsppSheetView.
8934 * Gets the #PsppSheetSelection associated with @tree_view.
8936 * Return value: A #PsppSheetSelection object.
8938 PsppSheetSelection *
8939 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
8941 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8943 return tree_view->priv->selection;
8947 * pspp_sheet_view_get_hadjustment:
8948 * @tree_view: A #PsppSheetView
8950 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
8952 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
8956 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
8958 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8960 return pspp_sheet_view_do_get_hadjustment (tree_view);
8963 static GtkAdjustment *
8964 pspp_sheet_view_do_get_hadjustment (PsppSheetView *tree_view)
8966 return tree_view->priv->hadjustment;
8970 * pspp_sheet_view_set_hadjustment:
8971 * @tree_view: A #PsppSheetView
8972 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
8974 * Sets the #GtkAdjustment for the current horizontal aspect.
8977 pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view,
8978 GtkAdjustment *adjustment)
8980 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8982 pspp_sheet_view_set_adjustments (tree_view,
8984 tree_view->priv->vadjustment);
8986 g_object_notify (G_OBJECT (tree_view), "hadjustment");
8990 pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
8991 GtkAdjustment *adjustment)
8993 PsppSheetViewPrivate *priv = tree_view->priv;
8995 if (adjustment && priv->hadjustment == adjustment)
8998 if (priv->hadjustment != NULL)
9000 g_signal_handlers_disconnect_by_func (priv->hadjustment,
9001 pspp_sheet_view_adjustment_changed,
9003 g_object_unref (priv->hadjustment);
9006 if (adjustment == NULL)
9007 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9010 g_signal_connect (adjustment, "value-changed",
9011 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9012 priv->hadjustment = g_object_ref_sink (adjustment);
9013 /* FIXME: Adjustment should probably be populated here with fresh values, but
9014 * internal details are too complicated for me to decipher right now.
9016 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9018 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9022 * pspp_sheet_view_get_vadjustment:
9023 * @tree_view: A #PsppSheetView
9025 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9027 * Return value: (transfer none): A #GtkAdjustment object, or %NULL
9028 * if none is currently being used.
9030 * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
9033 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9035 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9037 return pspp_sheet_view_do_get_vadjustment (tree_view);
9040 static GtkAdjustment *
9041 pspp_sheet_view_do_get_vadjustment (PsppSheetView *tree_view)
9043 return tree_view->priv->vadjustment;
9047 * pspp_sheet_view_set_vadjustment:
9048 * @tree_view: A #PsppSheetView
9049 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9051 * Sets the #GtkAdjustment for the current vertical aspect.
9053 * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
9056 pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view,
9057 GtkAdjustment *adjustment)
9059 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9060 g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
9062 pspp_sheet_view_do_set_vadjustment (tree_view, adjustment);
9066 pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
9067 GtkAdjustment *adjustment)
9069 PsppSheetViewPrivate *priv = tree_view->priv;
9071 if (adjustment && priv->vadjustment == adjustment)
9074 if (priv->vadjustment != NULL)
9076 g_signal_handlers_disconnect_by_func (priv->vadjustment,
9077 pspp_sheet_view_adjustment_changed,
9079 g_object_unref (priv->vadjustment);
9082 if (adjustment == NULL)
9083 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9086 g_signal_connect (adjustment, "value-changed",
9087 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9088 priv->vadjustment = g_object_ref_sink (adjustment);
9089 /* FIXME: Adjustment should probably be populated here with fresh values, but
9090 * internal details are too complicated for me to decipher right now.
9092 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9093 g_object_notify (G_OBJECT (tree_view), "vadjustment");
9096 /* Column and header operations */
9099 * pspp_sheet_view_get_headers_visible:
9100 * @tree_view: A #PsppSheetView.
9102 * Returns %TRUE if the headers on the @tree_view are visible.
9104 * Return value: Whether the headers are visible or not.
9107 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9109 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9111 return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9115 * pspp_sheet_view_set_headers_visible:
9116 * @tree_view: A #PsppSheetView.
9117 * @headers_visible: %TRUE if the headers are visible
9119 * Sets the visibility state of the headers.
9122 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9123 gboolean headers_visible)
9127 PsppSheetViewColumn *column;
9128 GtkAllocation allocation;
9130 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9132 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9134 headers_visible = !! headers_visible;
9136 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9139 if (headers_visible)
9140 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9142 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9144 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9146 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9147 if (headers_visible)
9149 gdk_window_move_resize (tree_view->priv->bin_window, x, y + TREE_VIEW_HEADER_HEIGHT (tree_view),
9150 tree_view->priv->width, allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view));
9152 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9153 pspp_sheet_view_map_buttons (tree_view);
9157 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9159 for (list = tree_view->priv->columns; list; list = list->next)
9161 column = list->data;
9163 gtk_widget_unmap (column->button);
9165 gdk_window_hide (tree_view->priv->header_window);
9169 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view));
9170 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2);
9171 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
9172 gtk_adjustment_set_upper (tree_view->priv->vadjustment, tree_view->priv->height);
9173 gtk_adjustment_changed (tree_view->priv->vadjustment);
9175 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9177 g_object_notify (G_OBJECT (tree_view), "headers-visible");
9181 * pspp_sheet_view_columns_autosize:
9182 * @tree_view: A #PsppSheetView.
9184 * Resizes all columns to their optimal width. Only works after the
9185 * treeview has been realized.
9188 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9190 gboolean dirty = FALSE;
9192 PsppSheetViewColumn *column;
9194 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9196 for (list = tree_view->priv->columns; list; list = list->next)
9198 column = list->data;
9199 _pspp_sheet_view_column_cell_set_dirty (column);
9204 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9208 * pspp_sheet_view_set_headers_clickable:
9209 * @tree_view: A #PsppSheetView.
9210 * @setting: %TRUE if the columns are clickable.
9212 * Allow the column title buttons to be clicked.
9215 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9220 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9222 for (list = tree_view->priv->columns; list; list = list->next)
9223 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9225 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9230 * pspp_sheet_view_get_headers_clickable:
9231 * @tree_view: A #PsppSheetView.
9233 * Returns whether all header columns are clickable.
9235 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9240 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9244 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9246 for (list = tree_view->priv->columns; list; list = list->next)
9247 if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9254 * pspp_sheet_view_set_rules_hint
9255 * @tree_view: a #PsppSheetView
9256 * @setting: %TRUE if the tree requires reading across rows
9258 * This function tells GTK+ that the user interface for your
9259 * application requires users to read across tree rows and associate
9260 * cells with one another. By default, GTK+ will then render the tree
9261 * with alternating row colors. Do <emphasis>not</emphasis> use it
9262 * just because you prefer the appearance of the ruled tree; that's a
9263 * question for the theme. Some themes will draw tree rows in
9264 * alternating colors even when rules are turned off, and users who
9265 * prefer that appearance all the time can choose those themes. You
9266 * should call this function only as a <emphasis>semantic</emphasis>
9267 * hint to the theme engine that your tree makes alternating colors
9268 * useful from a functional standpoint (since it has lots of columns,
9273 pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view,
9276 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9278 setting = setting != FALSE;
9280 if (tree_view->priv->has_rules != setting)
9282 tree_view->priv->has_rules = setting;
9283 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9286 g_object_notify (G_OBJECT (tree_view), "rules-hint");
9290 * pspp_sheet_view_get_rules_hint
9291 * @tree_view: a #PsppSheetView
9293 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9295 * Return value: %TRUE if rules are useful for the user of this tree
9298 pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view)
9300 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9302 return tree_view->priv->has_rules;
9305 /* Public Column functions
9309 * pspp_sheet_view_append_column:
9310 * @tree_view: A #PsppSheetView.
9311 * @column: The #PsppSheetViewColumn to add.
9313 * Appends @column to the list of columns.
9315 * Return value: The number of columns in @tree_view after appending.
9318 pspp_sheet_view_append_column (PsppSheetView *tree_view,
9319 PsppSheetViewColumn *column)
9321 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9322 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9323 g_return_val_if_fail (column->tree_view == NULL, -1);
9325 return pspp_sheet_view_insert_column (tree_view, column, -1);
9330 * pspp_sheet_view_remove_column:
9331 * @tree_view: A #PsppSheetView.
9332 * @column: The #PsppSheetViewColumn to remove.
9334 * Removes @column from @tree_view.
9336 * Return value: The number of columns in @tree_view after removing.
9339 pspp_sheet_view_remove_column (PsppSheetView *tree_view,
9340 PsppSheetViewColumn *column)
9342 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9343 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9344 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9346 if (tree_view->priv->focus_column == column)
9347 tree_view->priv->focus_column = NULL;
9349 if (tree_view->priv->edited_column == column)
9351 pspp_sheet_view_stop_editing (tree_view, TRUE);
9353 /* no need to, but just to be sure ... */
9354 tree_view->priv->edited_column = NULL;
9357 _pspp_sheet_view_column_unset_tree_view (column);
9359 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9360 tree_view->priv->n_columns--;
9362 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9366 _pspp_sheet_view_column_unrealize_button (column);
9367 for (list = tree_view->priv->columns; list; list = list->next)
9369 PsppSheetViewColumn *tmp_column;
9371 tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9372 if (tmp_column->visible)
9373 _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9376 if (tree_view->priv->n_columns == 0 &&
9377 pspp_sheet_view_get_headers_visible (tree_view) &&
9378 tree_view->priv->header_window)
9379 gdk_window_hide (tree_view->priv->header_window);
9381 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9384 g_object_unref (column);
9385 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9387 return tree_view->priv->n_columns;
9391 * pspp_sheet_view_insert_column:
9392 * @tree_view: A #PsppSheetView.
9393 * @column: The #PsppSheetViewColumn to be inserted.
9394 * @position: The position to insert @column in.
9396 * This inserts the @column into the @tree_view at @position. If @position is
9397 * -1, then the column is inserted at the end.
9399 * Return value: The number of columns in @tree_view after insertion.
9402 pspp_sheet_view_insert_column (PsppSheetView *tree_view,
9403 PsppSheetViewColumn *column,
9406 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9407 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9408 g_return_val_if_fail (column->tree_view == NULL, -1);
9410 g_object_ref_sink (column);
9412 if (tree_view->priv->n_columns == 0 &&
9413 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9414 pspp_sheet_view_get_headers_visible (tree_view))
9416 gdk_window_show (tree_view->priv->header_window);
9419 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9421 tree_view->priv->n_columns++;
9423 _pspp_sheet_view_column_set_tree_view (column, tree_view);
9425 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9429 _pspp_sheet_view_column_realize_button (column);
9431 for (list = tree_view->priv->columns; list; list = list->next)
9433 column = PSPP_SHEET_VIEW_COLUMN (list->data);
9434 if (column->visible)
9435 _pspp_sheet_view_column_cell_set_dirty (column);
9437 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9440 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9442 return tree_view->priv->n_columns;
9446 * pspp_sheet_view_insert_column_with_attributes:
9447 * @tree_view: A #PsppSheetView
9448 * @position: The position to insert the new column in.
9449 * @title: The title to set the header to.
9450 * @cell: The #GtkCellRenderer.
9451 * @Varargs: A %NULL-terminated list of attributes.
9453 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9454 * @position. If @position is -1, then the newly created column is inserted at
9455 * the end. The column is initialized with the attributes given.
9457 * Return value: The number of columns in @tree_view after insertion.
9460 pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view,
9463 GtkCellRenderer *cell,
9466 PsppSheetViewColumn *column;
9471 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9473 column = pspp_sheet_view_column_new ();
9474 pspp_sheet_view_column_set_title (column, title);
9475 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9477 va_start (args, cell);
9479 attribute = va_arg (args, gchar *);
9481 while (attribute != NULL)
9483 column_id = va_arg (args, gint);
9484 pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9485 attribute = va_arg (args, gchar *);
9490 pspp_sheet_view_insert_column (tree_view, column, position);
9492 return tree_view->priv->n_columns;
9496 * pspp_sheet_view_insert_column_with_data_func:
9497 * @tree_view: a #PsppSheetView
9498 * @position: Position to insert, -1 for append
9499 * @title: column title
9500 * @cell: cell renderer for column
9501 * @func: function to set attributes of cell renderer
9502 * @data: data for @func
9503 * @dnotify: destroy notifier for @data
9505 * Convenience function that inserts a new column into the #PsppSheetView
9506 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9507 * attributes (normally using data from the model). See also
9508 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9510 * Return value: number of columns in the tree view post-insert
9513 pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view,
9516 GtkCellRenderer *cell,
9517 PsppSheetCellDataFunc func,
9519 GDestroyNotify dnotify)
9521 PsppSheetViewColumn *column;
9523 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9525 column = pspp_sheet_view_column_new ();
9526 pspp_sheet_view_column_set_title (column, title);
9527 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9528 pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9530 pspp_sheet_view_insert_column (tree_view, column, position);
9532 return tree_view->priv->n_columns;
9536 * pspp_sheet_view_get_column:
9537 * @tree_view: A #PsppSheetView.
9538 * @n: The position of the column, counting from 0.
9540 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9542 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9545 PsppSheetViewColumn *
9546 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9549 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9551 if (n < 0 || n >= tree_view->priv->n_columns)
9554 if (tree_view->priv->columns == NULL)
9557 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9561 * pspp_sheet_view_get_columns:
9562 * @tree_view: A #PsppSheetView
9564 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9565 * The returned list must be freed with g_list_free ().
9567 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9570 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9572 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9574 return g_list_copy (tree_view->priv->columns);
9578 * pspp_sheet_view_move_column_after:
9579 * @tree_view: A #PsppSheetView
9580 * @column: The #PsppSheetViewColumn to be moved.
9581 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9583 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9584 * @column is placed in the first position.
9587 pspp_sheet_view_move_column_after (PsppSheetView *tree_view,
9588 PsppSheetViewColumn *column,
9589 PsppSheetViewColumn *base_column)
9591 GList *column_list_el, *base_el = NULL;
9593 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9595 column_list_el = g_list_find (tree_view->priv->columns, column);
9596 g_return_if_fail (column_list_el != NULL);
9600 base_el = g_list_find (tree_view->priv->columns, base_column);
9601 g_return_if_fail (base_el != NULL);
9604 if (column_list_el->prev == base_el)
9607 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9608 if (base_el == NULL)
9610 column_list_el->prev = NULL;
9611 column_list_el->next = tree_view->priv->columns;
9612 if (column_list_el->next)
9613 column_list_el->next->prev = column_list_el;
9614 tree_view->priv->columns = column_list_el;
9618 column_list_el->prev = base_el;
9619 column_list_el->next = base_el->next;
9620 if (column_list_el->next)
9621 column_list_el->next->prev = column_list_el;
9622 base_el->next = column_list_el;
9625 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9627 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9628 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9631 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9635 * pspp_sheet_view_set_column_drag_function:
9636 * @tree_view: A #PsppSheetView.
9637 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9638 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9639 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9641 * Sets a user function for determining where a column may be dropped when
9642 * dragged. This function is called on every column pair in turn at the
9643 * beginning of a column drag to determine where a drop can take place. The
9644 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9645 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9646 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9647 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9648 * @tree_view reverts to the default behavior of allowing all columns to be
9649 * dropped everywhere.
9652 pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view,
9653 PsppSheetViewColumnDropFunc func,
9655 GDestroyNotify destroy)
9657 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9659 if (tree_view->priv->column_drop_func_data_destroy)
9660 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9662 tree_view->priv->column_drop_func = func;
9663 tree_view->priv->column_drop_func_data = user_data;
9664 tree_view->priv->column_drop_func_data_destroy = destroy;
9668 * pspp_sheet_view_scroll_to_point:
9669 * @tree_view: a #PsppSheetView
9670 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9671 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9673 * Scrolls the tree view such that the top-left corner of the visible
9674 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9675 * in tree coordinates. The @tree_view must be realized before
9676 * this function is called. If it isn't, you probably want to be
9677 * using pspp_sheet_view_scroll_to_cell().
9679 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9682 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9686 GtkAdjustment *hadj;
9687 GtkAdjustment *vadj;
9689 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9690 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9692 hadj = tree_view->priv->hadjustment;
9693 vadj = tree_view->priv->vadjustment;
9696 gtk_adjustment_set_value (hadj, CLAMP (tree_x, gtk_adjustment_get_lower (hadj), gtk_adjustment_get_upper (hadj) - gtk_adjustment_get_page_size (hadj)));
9698 gtk_adjustment_set_value (vadj, CLAMP (tree_y, gtk_adjustment_get_lower (vadj), gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj)));
9702 * pspp_sheet_view_scroll_to_cell:
9703 * @tree_view: A #PsppSheetView.
9704 * @path: (allow-none): The path of the row to move to, or %NULL.
9705 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9706 * @use_align: whether to use alignment arguments, or %FALSE.
9707 * @row_align: The vertical alignment of the row specified by @path.
9708 * @col_align: The horizontal alignment of the column specified by @column.
9710 * Moves the alignments of @tree_view to the position specified by @column and
9711 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9712 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9713 * or @path need to be non-%NULL. @row_align determines where the row is
9714 * placed, and @col_align determines where @column is placed. Both are expected
9715 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9716 * right/bottom alignment, 0.5 means center.
9718 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9719 * tree does the minimum amount of work to scroll the cell onto the screen.
9720 * This means that the cell will be scrolled to the edge closest to its current
9721 * position. If the cell is currently visible on the screen, nothing is done.
9723 * This function only works if the model is set, and @path is a valid row on the
9724 * model. If the model changes before the @tree_view is realized, the centered
9725 * path will be modified to reflect this change.
9728 pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view,
9730 PsppSheetViewColumn *column,
9735 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9736 g_return_if_fail (tree_view->priv->model != NULL);
9737 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9738 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9739 g_return_if_fail (path != NULL || column != NULL);
9742 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9743 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9745 row_align = CLAMP (row_align, 0.0, 1.0);
9746 col_align = CLAMP (col_align, 0.0, 1.0);
9749 /* Note: Despite the benefits that come from having one code path for the
9750 * scrolling code, we short-circuit validate_visible_area's immplementation as
9751 * it is much slower than just going to the point.
9753 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9754 !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9755 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9757 if (tree_view->priv->scroll_to_path)
9758 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9760 tree_view->priv->scroll_to_path = NULL;
9761 tree_view->priv->scroll_to_column = NULL;
9764 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
9766 tree_view->priv->scroll_to_column = column;
9767 tree_view->priv->scroll_to_use_align = use_align;
9768 tree_view->priv->scroll_to_row_align = row_align;
9769 tree_view->priv->scroll_to_col_align = col_align;
9771 install_presize_handler (tree_view);
9775 GdkRectangle cell_rect;
9776 GdkRectangle vis_rect;
9777 gint dest_x, dest_y;
9779 pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
9780 pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
9782 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
9784 dest_x = vis_rect.x;
9785 dest_y = vis_rect.y;
9791 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
9795 if (cell_rect.x < vis_rect.x)
9796 dest_x = cell_rect.x;
9797 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
9798 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
9806 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
9807 dest_y = MAX (dest_y, 0);
9811 if (cell_rect.y < vis_rect.y)
9812 dest_y = cell_rect.y;
9813 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
9814 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
9818 pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
9823 * pspp_sheet_view_row_activated:
9824 * @tree_view: A #PsppSheetView
9825 * @path: The #GtkTreePath to be activated.
9826 * @column: The #PsppSheetViewColumn to be activated.
9828 * Activates the cell determined by @path and @column.
9831 pspp_sheet_view_row_activated (PsppSheetView *tree_view,
9833 PsppSheetViewColumn *column)
9835 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9837 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
9842 * pspp_sheet_view_get_reorderable:
9843 * @tree_view: a #PsppSheetView
9845 * Retrieves whether the user can reorder the tree via drag-and-drop. See
9846 * pspp_sheet_view_set_reorderable().
9848 * Return value: %TRUE if the tree can be reordered.
9851 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
9853 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9855 return tree_view->priv->reorderable;
9859 * pspp_sheet_view_set_reorderable:
9860 * @tree_view: A #PsppSheetView.
9861 * @reorderable: %TRUE, if the tree can be reordered.
9863 * This function is a convenience function to allow you to reorder
9864 * models that support the #GtkDragSourceIface and the
9865 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
9866 * these. If @reorderable is %TRUE, then the user can reorder the
9867 * model by dragging and dropping rows. The developer can listen to
9868 * these changes by connecting to the model's row_inserted and
9869 * row_deleted signals. The reordering is implemented by setting up
9870 * the tree view as a drag source and destination. Therefore, drag and
9871 * drop can not be used in a reorderable view for any other purpose.
9873 * This function does not give you any degree of control over the order -- any
9874 * reordering is allowed. If more control is needed, you should probably
9875 * handle drag and drop manually.
9878 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
9879 gboolean reorderable)
9881 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9883 reorderable = reorderable != FALSE;
9885 if (tree_view->priv->reorderable == reorderable)
9890 const GtkTargetEntry row_targets[] = {
9891 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
9894 pspp_sheet_view_enable_model_drag_source (tree_view,
9897 G_N_ELEMENTS (row_targets),
9899 pspp_sheet_view_enable_model_drag_dest (tree_view,
9901 G_N_ELEMENTS (row_targets),
9906 pspp_sheet_view_unset_rows_drag_source (tree_view);
9907 pspp_sheet_view_unset_rows_drag_dest (tree_view);
9910 tree_view->priv->reorderable = reorderable;
9912 g_object_notify (G_OBJECT (tree_view), "reorderable");
9915 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
9916 is pressed, other rows will be unselected.
9918 If CLAMP_NODE is true, then the sheetview will scroll to make the row
9921 pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
9923 gboolean clear_and_select,
9924 gboolean clamp_node,
9925 PsppSheetSelectMode mode)
9929 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9931 GtkTreePath *cursor_path;
9932 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9933 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
9934 gtk_tree_path_free (cursor_path);
9937 gtk_tree_row_reference_free (tree_view->priv->cursor);
9938 tree_view->priv->cursor = NULL;
9940 _pspp_sheet_view_find_node (tree_view, path, &node);
9941 tree_view->priv->cursor =
9942 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
9943 tree_view->priv->model,
9946 if (tree_view->priv->row_count > 0)
9950 if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
9951 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
9955 /* We have to re-find tree and node here again, somebody might have
9956 * cleared the node or the whole tree in the PsppSheetSelection::changed
9957 * callback. If the nodes differ we bail out here.
9959 _pspp_sheet_view_find_node (tree_view, path, &new_node);
9961 if (node != new_node)
9966 pspp_sheet_view_clamp_node_visible (tree_view, node);
9967 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
9971 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
9975 * pspp_sheet_view_get_cursor:
9976 * @tree_view: A #PsppSheetView
9977 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
9978 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
9980 * Fills in @path and @focus_column with the current path and focus column. If
9981 * the cursor isn't currently set, then *@path will be %NULL. If no column
9982 * currently has focus, then *@focus_column will be %NULL.
9984 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
9985 * you are done with it.
9988 pspp_sheet_view_get_cursor (PsppSheetView *tree_view,
9990 PsppSheetViewColumn **focus_column)
9992 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9996 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9997 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10004 *focus_column = tree_view->priv->focus_column;
10009 * pspp_sheet_view_set_cursor:
10010 * @tree_view: A #PsppSheetView
10011 * @path: A #GtkTreePath
10012 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10013 * @start_editing: %TRUE if the specified cell should start being edited.
10015 * Sets the current keyboard focus to be at @path, and selects it. This is
10016 * useful when you want to focus the user's attention on a particular row. If
10017 * @focus_column is not %NULL, then focus is given to the column specified by
10018 * it. Additionally, if @focus_column is specified, and @start_editing is
10019 * %TRUE, then editing should be started in the specified cell.
10020 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10021 * in order to give keyboard focus to the widget. Please note that editing
10022 * can only happen when the widget is realized.
10024 * If @path is invalid for @model, the current cursor (if any) will be unset
10025 * and the function will return without failing.
10028 pspp_sheet_view_set_cursor (PsppSheetView *tree_view,
10030 PsppSheetViewColumn *focus_column,
10031 gboolean start_editing)
10033 pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10034 NULL, start_editing);
10038 * pspp_sheet_view_set_cursor_on_cell:
10039 * @tree_view: A #PsppSheetView
10040 * @path: A #GtkTreePath
10041 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10042 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10043 * @start_editing: %TRUE if the specified cell should start being edited.
10045 * Sets the current keyboard focus to be at @path, and selects it. This is
10046 * useful when you want to focus the user's attention on a particular row. If
10047 * @focus_column is not %NULL, then focus is given to the column specified by
10048 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10049 * contains 2 or more editable or activatable cells, then focus is given to
10050 * the cell specified by @focus_cell. Additionally, if @focus_column is
10051 * specified, and @start_editing is %TRUE, then editing should be started in
10052 * the specified cell. This function is often followed by
10053 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10054 * widget. Please note that editing can only happen when the widget is
10057 * If @path is invalid for @model, the current cursor (if any) will be unset
10058 * and the function will return without failing.
10063 pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view,
10065 PsppSheetViewColumn *focus_column,
10066 GtkCellRenderer *focus_cell,
10067 gboolean start_editing)
10069 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10070 g_return_if_fail (path != NULL);
10071 g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10073 if (!tree_view->priv->model)
10078 g_return_if_fail (focus_column);
10079 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10082 /* cancel the current editing, if it exists */
10083 if (tree_view->priv->edited_column &&
10084 tree_view->priv->edited_column->editable_widget)
10085 pspp_sheet_view_stop_editing (tree_view, TRUE);
10087 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
10089 if (focus_column && focus_column->visible)
10092 gboolean column_in_tree = FALSE;
10094 for (list = tree_view->priv->columns; list; list = list->next)
10095 if (list->data == focus_column)
10097 column_in_tree = TRUE;
10100 g_return_if_fail (column_in_tree);
10101 tree_view->priv->focus_column = focus_column;
10103 pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10105 pspp_sheet_view_start_editing (tree_view, path);
10107 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10108 pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10114 * pspp_sheet_view_get_bin_window:
10115 * @tree_view: A #PsppSheetView
10117 * Returns the window that @tree_view renders to. This is used primarily to
10118 * compare to <literal>event->window</literal> to confirm that the event on
10119 * @tree_view is on the right window.
10121 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10124 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10126 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10128 return tree_view->priv->bin_window;
10132 * pspp_sheet_view_get_path_at_pos:
10133 * @tree_view: A #PsppSheetView.
10134 * @x: The x position to be identified (relative to bin_window).
10135 * @y: The y position to be identified (relative to bin_window).
10136 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10137 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10138 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10139 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10141 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10142 * (please see pspp_sheet_view_get_bin_window()).
10143 * That is, @x and @y are relative to an events coordinates. @x and @y must
10144 * come from an event on the @tree_view only where <literal>event->window ==
10145 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10146 * things like popup menus. If @path is non-%NULL, then it will be filled
10147 * with the #GtkTreePath at that point. This path should be freed with
10148 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10149 * with the column at that point. @cell_x and @cell_y return the coordinates
10150 * relative to the cell background (i.e. the @background_area passed to
10151 * gtk_cell_renderer_render()). This function is only meaningful if
10152 * @tree_view is realized. Therefore this function will always return %FALSE
10153 * if @tree_view is not realized or does not have a model.
10155 * For converting widget coordinates (eg. the ones you get from
10156 * GtkWidget::query-tooltip), please see
10157 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10159 * Return value: %TRUE if a row exists at that coordinate.
10162 pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view,
10165 GtkTreePath **path,
10166 PsppSheetViewColumn **column,
10173 g_return_val_if_fail (tree_view != NULL, FALSE);
10180 if (tree_view->priv->bin_window == NULL)
10183 if (tree_view->priv->row_count == 0)
10186 if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment))
10189 if (x < 0 || y < 0)
10192 if (column || cell_x)
10194 PsppSheetViewColumn *tmp_column;
10195 PsppSheetViewColumn *last_column = NULL;
10197 gint remaining_x = x;
10198 gboolean found = FALSE;
10201 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10202 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10204 list = (rtl ? list->prev : list->next))
10206 tmp_column = list->data;
10208 if (tmp_column->visible == FALSE)
10211 last_column = tmp_column;
10212 if (remaining_x <= tmp_column->width)
10217 *column = tmp_column;
10220 *cell_x = remaining_x;
10224 remaining_x -= tmp_column->width;
10227 /* If found is FALSE and there is a last_column, then it the remainder
10228 * space is in that area
10235 *column = last_column;
10238 *cell_x = last_column->width + remaining_x;
10247 y_offset = pspp_sheet_view_find_offset (tree_view,
10248 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10255 *cell_y = y_offset;
10258 *path = _pspp_sheet_view_find_path (tree_view, node);
10263 /* Computes 'cell_area' from 'background_area', which must be the background
10264 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10265 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10266 the cell area as passed to _pspp_sheet_view_column_cell_render().
10268 'column' is required to properly adjust 'cell_area->x' and
10269 'cell_area->width'. It may be set to NULL if these values are not of
10270 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10273 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
10274 PsppSheetViewColumn *column,
10275 const GdkRectangle *background_area,
10276 gboolean subtract_focus_rect,
10277 GdkRectangle *cell_area)
10279 gint vertical_separator;
10280 gint horizontal_separator;
10282 *cell_area = *background_area;
10284 gtk_widget_style_get (GTK_WIDGET (tree_view),
10285 "vertical-separator", &vertical_separator,
10286 "horizontal-separator", &horizontal_separator,
10288 cell_area->x += horizontal_separator / 2;
10289 cell_area->y += vertical_separator / 2;
10290 cell_area->width -= horizontal_separator;
10291 cell_area->height -= vertical_separator;
10293 if (subtract_focus_rect)
10295 int focus_line_width;
10297 gtk_widget_style_get (GTK_WIDGET (tree_view),
10298 "focus-line-width", &focus_line_width,
10300 cell_area->x += focus_line_width;
10301 cell_area->y += focus_line_width;
10302 cell_area->width -= 2 * focus_line_width;
10303 cell_area->height -= 2 * focus_line_width;
10306 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10308 gint grid_line_width;
10309 gtk_widget_style_get (GTK_WIDGET (tree_view),
10310 "grid-line-width", &grid_line_width,
10313 if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10314 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10317 PsppSheetViewColumn *first_column, *last_column;
10320 /* Find the last visible column. */
10321 last_column = NULL;
10322 for (list = g_list_last (tree_view->priv->columns);
10326 PsppSheetViewColumn *c = list->data;
10334 /* Find the first visible column. */
10335 first_column = NULL;
10336 for (list = g_list_first (tree_view->priv->columns);
10340 PsppSheetViewColumn *c = list->data;
10348 if (column == first_column)
10350 cell_area->width -= grid_line_width / 2;
10352 else if (column == last_column)
10354 cell_area->x += grid_line_width / 2;
10355 cell_area->width -= grid_line_width / 2;
10359 cell_area->x += grid_line_width / 2;
10360 cell_area->width -= grid_line_width;
10364 if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10365 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10367 cell_area->y += grid_line_width / 2;
10368 cell_area->height -= grid_line_width;
10372 if (column == NULL)
10375 cell_area->width = 0;
10380 * pspp_sheet_view_get_cell_area:
10381 * @tree_view: a #PsppSheetView
10382 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10383 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10384 * @rect: rectangle to fill with cell rect
10386 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10387 * row specified by @path and the column specified by @column. If @path is
10388 * %NULL, or points to a path not currently displayed, the @y and @height fields
10389 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10390 * fields will be filled with 0. The sum of all cell rects does not cover the
10391 * entire tree; there are extra pixels in between rows, for example. The
10392 * returned rectangle is equivalent to the @cell_area passed to
10393 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10397 pspp_sheet_view_get_cell_area (PsppSheetView *tree_view,
10399 PsppSheetViewColumn *column,
10400 GdkRectangle *rect)
10402 GdkRectangle background_area;
10404 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10405 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10406 g_return_if_fail (rect != NULL);
10407 g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10408 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10410 pspp_sheet_view_get_background_area (tree_view, path, column,
10412 pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10417 * pspp_sheet_view_get_background_area:
10418 * @tree_view: a #PsppSheetView
10419 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10420 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10421 * @rect: rectangle to fill with cell background rect
10423 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10424 * row specified by @path and the column specified by @column. If @path is
10425 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10426 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10427 * fields will be filled with 0. The returned rectangle is equivalent to the
10428 * @background_area passed to gtk_cell_renderer_render(). These background
10429 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10430 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10431 * itself, excluding surrounding borders.
10435 pspp_sheet_view_get_background_area (PsppSheetView *tree_view,
10437 PsppSheetViewColumn *column,
10438 GdkRectangle *rect)
10442 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10443 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10444 g_return_if_fail (rect != NULL);
10453 /* Get vertical coords */
10455 _pspp_sheet_view_find_node (tree_view, path, &node);
10459 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10461 rect->height = ROW_HEIGHT (tree_view);
10468 pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10469 rect->width = x2 - rect->x;
10474 * pspp_sheet_view_get_visible_rect:
10475 * @tree_view: a #PsppSheetView
10476 * @visible_rect: rectangle to fill
10478 * Fills @visible_rect with the currently-visible region of the
10479 * buffer, in tree coordinates. Convert to bin_window coordinates with
10480 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10481 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10482 * scrollable area of the tree.
10485 pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view,
10486 GdkRectangle *visible_rect)
10490 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10492 widget = GTK_WIDGET (tree_view);
10496 GtkAllocation allocation;
10497 gtk_widget_get_allocation (widget, &allocation);
10498 visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
10499 visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
10500 visible_rect->width = allocation.width;
10501 visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10506 * pspp_sheet_view_widget_to_tree_coords:
10507 * @tree_view: a #PsppSheetView
10508 * @wx: X coordinate relative to bin_window
10509 * @wy: Y coordinate relative to bin_window
10510 * @tx: return location for tree X coordinate
10511 * @ty: return location for tree Y coordinate
10513 * Converts bin_window coordinates to coordinates for the
10514 * tree (the full scrollable area of the tree).
10516 * Deprecated: 2.12: Due to historial reasons the name of this function is
10517 * incorrect. For converting coordinates relative to the widget to
10518 * bin_window coordinates, please see
10519 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10523 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10529 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10532 *tx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10534 *ty = wy + tree_view->priv->dy;
10538 * pspp_sheet_view_tree_to_widget_coords:
10539 * @tree_view: a #PsppSheetView
10540 * @tx: tree X coordinate
10541 * @ty: tree Y coordinate
10542 * @wx: return location for X coordinate relative to bin_window
10543 * @wy: return location for Y coordinate relative to bin_window
10545 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10546 * to bin_window coordinates.
10548 * Deprecated: 2.12: Due to historial reasons the name of this function is
10549 * incorrect. For converting bin_window coordinates to coordinates relative
10550 * to bin_window, please see
10551 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10555 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10561 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10564 *wx = tx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10566 *wy = ty - tree_view->priv->dy;
10571 * pspp_sheet_view_convert_widget_to_tree_coords:
10572 * @tree_view: a #PsppSheetView
10573 * @wx: X coordinate relative to the widget
10574 * @wy: Y coordinate relative to the widget
10575 * @tx: return location for tree X coordinate
10576 * @ty: return location for tree Y coordinate
10578 * Converts widget coordinates to coordinates for the
10579 * tree (the full scrollable area of the tree).
10584 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10592 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10594 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10597 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10603 * pspp_sheet_view_convert_tree_to_widget_coords:
10604 * @tree_view: a #PsppSheetView
10605 * @tx: X coordinate relative to the tree
10606 * @ty: Y coordinate relative to the tree
10607 * @wx: return location for widget X coordinate
10608 * @wy: return location for widget Y coordinate
10610 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10611 * to widget coordinates.
10616 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10624 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10626 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10629 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10635 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10636 * @tree_view: a #PsppSheetView
10637 * @wx: X coordinate relative to the widget
10638 * @wy: Y coordinate relative to the widget
10639 * @bx: return location for bin_window X coordinate
10640 * @by: return location for bin_window Y coordinate
10642 * Converts widget coordinates to coordinates for the bin_window
10643 * (see pspp_sheet_view_get_bin_window()).
10648 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10654 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10657 *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10659 *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10663 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10664 * @tree_view: a #PsppSheetView
10665 * @bx: bin_window X coordinate
10666 * @by: bin_window Y coordinate
10667 * @wx: return location for widget X coordinate
10668 * @wy: return location for widget Y coordinate
10670 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10671 * to widget relative coordinates.
10676 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10682 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10685 *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10687 *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10691 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10692 * @tree_view: a #PsppSheetView
10693 * @tx: tree X coordinate
10694 * @ty: tree Y coordinate
10695 * @bx: return location for X coordinate relative to bin_window
10696 * @by: return location for Y coordinate relative to bin_window
10698 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10699 * to bin_window coordinates.
10704 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10710 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10715 *by = ty - tree_view->priv->dy;
10719 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10720 * @tree_view: a #PsppSheetView
10721 * @bx: X coordinate relative to bin_window
10722 * @by: Y coordinate relative to bin_window
10723 * @tx: return location for tree X coordinate
10724 * @ty: return location for tree Y coordinate
10726 * Converts bin_window coordinates to coordinates for the
10727 * tree (the full scrollable area of the tree).
10732 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10738 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10743 *ty = by + tree_view->priv->dy;
10749 * pspp_sheet_view_get_visible_range:
10750 * @tree_view: A #PsppSheetView
10751 * @start_path: (allow-none): Return location for start of region, or %NULL.
10752 * @end_path: (allow-none): Return location for end of region, or %NULL.
10754 * Sets @start_path and @end_path to be the first and last visible path.
10755 * Note that there may be invisible paths in between.
10757 * The paths should be freed with gtk_tree_path_free() after use.
10759 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
10764 pspp_sheet_view_get_visible_range (PsppSheetView *tree_view,
10765 GtkTreePath **start_path,
10766 GtkTreePath **end_path)
10771 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10773 if (!tree_view->priv->row_count)
10780 pspp_sheet_view_find_offset (tree_view,
10781 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
10784 *start_path = _pspp_sheet_view_find_path (tree_view, node);
10793 if (tree_view->priv->height < gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
10794 y = tree_view->priv->height - 1;
10796 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1;
10798 pspp_sheet_view_find_offset (tree_view, y, &node);
10800 *end_path = _pspp_sheet_view_find_path (tree_view, node);
10809 unset_reorderable (PsppSheetView *tree_view)
10811 if (tree_view->priv->reorderable)
10813 tree_view->priv->reorderable = FALSE;
10814 g_object_notify (G_OBJECT (tree_view), "reorderable");
10819 * pspp_sheet_view_enable_model_drag_source:
10820 * @tree_view: a #PsppSheetView
10821 * @start_button_mask: Mask of allowed buttons to start drag
10822 * @targets: the table of targets that the drag will support
10823 * @n_targets: the number of items in @targets
10824 * @actions: the bitmask of possible actions for a drag from this
10827 * Turns @tree_view into a drag source for automatic DND. Calling this
10828 * method sets #PsppSheetView:reorderable to %FALSE.
10831 pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view,
10832 GdkModifierType start_button_mask,
10833 const GtkTargetEntry *targets,
10835 GdkDragAction actions)
10837 TreeViewDragInfo *di;
10839 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10841 gtk_drag_source_set (GTK_WIDGET (tree_view),
10847 di = ensure_info (tree_view);
10849 di->start_button_mask = start_button_mask;
10850 di->source_actions = actions;
10851 di->source_set = TRUE;
10853 unset_reorderable (tree_view);
10857 * pspp_sheet_view_enable_model_drag_dest:
10858 * @tree_view: a #PsppSheetView
10859 * @targets: the table of targets that the drag will support
10860 * @n_targets: the number of items in @targets
10861 * @actions: the bitmask of possible actions for a drag from this
10864 * Turns @tree_view into a drop destination for automatic DND. Calling
10865 * this method sets #PsppSheetView:reorderable to %FALSE.
10868 pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view,
10869 const GtkTargetEntry *targets,
10871 GdkDragAction actions)
10873 TreeViewDragInfo *di;
10875 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10877 gtk_drag_dest_set (GTK_WIDGET (tree_view),
10883 di = ensure_info (tree_view);
10884 di->dest_set = TRUE;
10886 unset_reorderable (tree_view);
10890 * pspp_sheet_view_unset_rows_drag_source:
10891 * @tree_view: a #PsppSheetView
10893 * Undoes the effect of
10894 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
10895 * #PsppSheetView:reorderable to %FALSE.
10898 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
10900 TreeViewDragInfo *di;
10902 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10904 di = get_info (tree_view);
10908 if (di->source_set)
10910 gtk_drag_source_unset (GTK_WIDGET (tree_view));
10911 di->source_set = FALSE;
10914 if (!di->dest_set && !di->source_set)
10915 remove_info (tree_view);
10918 unset_reorderable (tree_view);
10922 * pspp_sheet_view_unset_rows_drag_dest:
10923 * @tree_view: a #PsppSheetView
10925 * Undoes the effect of
10926 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
10927 * #PsppSheetView:reorderable to %FALSE.
10930 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
10932 TreeViewDragInfo *di;
10934 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10936 di = get_info (tree_view);
10942 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
10943 di->dest_set = FALSE;
10946 if (!di->dest_set && !di->source_set)
10947 remove_info (tree_view);
10950 unset_reorderable (tree_view);
10954 * pspp_sheet_view_set_drag_dest_row:
10955 * @tree_view: a #PsppSheetView
10956 * @path: (allow-none): The path of the row to highlight, or %NULL.
10957 * @pos: Specifies whether to drop before, after or into the row
10959 * Sets the row that is highlighted for feedback.
10962 pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view,
10964 PsppSheetViewDropPosition pos)
10966 GtkTreePath *current_dest;
10968 /* Note; this function is exported to allow a custom DND
10969 * implementation, so it can't touch TreeViewDragInfo
10972 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10974 current_dest = NULL;
10976 if (tree_view->priv->drag_dest_row)
10978 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
10979 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
10982 /* special case a drop on an empty model */
10983 tree_view->priv->empty_view_drop = 0;
10985 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
10986 && gtk_tree_path_get_depth (path) == 1
10987 && gtk_tree_path_get_indices (path)[0] == 0)
10991 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
10995 tree_view->priv->empty_view_drop = 1;
10998 tree_view->priv->drag_dest_pos = pos;
11002 tree_view->priv->drag_dest_row =
11003 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
11004 pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
11007 tree_view->priv->drag_dest_row = NULL;
11011 int node, new_node;
11013 _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11014 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11018 new_node = pspp_sheet_view_node_next (tree_view, node);
11020 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11022 new_node = pspp_sheet_view_node_prev (tree_view, node);
11024 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11026 gtk_tree_path_free (current_dest);
11031 * pspp_sheet_view_get_drag_dest_row:
11032 * @tree_view: a #PsppSheetView
11033 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11034 * @pos: (allow-none): Return location for the drop position, or %NULL
11036 * Gets information about the row that is highlighted for feedback.
11039 pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view,
11040 GtkTreePath **path,
11041 PsppSheetViewDropPosition *pos)
11043 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11047 if (tree_view->priv->drag_dest_row)
11048 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11051 if (tree_view->priv->empty_view_drop)
11052 *path = gtk_tree_path_new_from_indices (0, -1);
11059 *pos = tree_view->priv->drag_dest_pos;
11063 * pspp_sheet_view_get_dest_row_at_pos:
11064 * @tree_view: a #PsppSheetView
11065 * @drag_x: the position to determine the destination row for
11066 * @drag_y: the position to determine the destination row for
11067 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11068 * @pos: (allow-none): Return location for the drop position, or %NULL
11070 * Determines the destination row for a given position. @drag_x and
11071 * @drag_y are expected to be in widget coordinates. This function is only
11072 * meaningful if @tree_view is realized. Therefore this function will always
11073 * return %FALSE if @tree_view is not realized or does not have a model.
11075 * Return value: whether there is a row at the given position, %TRUE if this
11076 * is indeed the case.
11079 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view,
11082 GtkTreePath **path,
11083 PsppSheetViewDropPosition *pos)
11087 gdouble offset_into_row;
11090 PsppSheetViewColumn *column = NULL;
11091 GtkTreePath *tmp_path = NULL;
11093 /* Note; this function is exported to allow a custom DND
11094 * implementation, so it can't touch TreeViewDragInfo
11097 g_return_val_if_fail (tree_view != NULL, FALSE);
11098 g_return_val_if_fail (drag_x >= 0, FALSE);
11099 g_return_val_if_fail (drag_y >= 0, FALSE);
11104 if (tree_view->priv->bin_window == NULL)
11107 if (tree_view->priv->row_count == 0)
11110 /* If in the top third of a row, we drop before that row; if
11111 * in the bottom third, drop after that row; if in the middle,
11112 * and the row has children, drop into the row.
11114 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11117 if (!pspp_sheet_view_get_path_at_pos (tree_view,
11126 pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11129 offset_into_row = cell_y;
11134 gtk_tree_path_free (tmp_path);
11138 third = cell.height / 3.0;
11142 if (offset_into_row < third)
11144 *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11146 else if (offset_into_row < (cell.height / 2.0))
11148 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11150 else if (offset_into_row < third * 2.0)
11152 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11156 *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11164 #if GTK3_TRANSITION
11165 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11167 * pspp_sheet_view_create_row_drag_icon:
11168 * @tree_view: a #PsppSheetView
11169 * @path: a #GtkTreePath in @tree_view
11171 * Creates a #GdkPixmap representation of the row at @path.
11172 * This image is used for a drag icon.
11174 * Return value: a newly-allocated pixmap of the drag icon.
11177 pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view,
11184 GdkRectangle background_area;
11185 GdkRectangle expose_area;
11187 /* start drawing inside the black outline */
11189 GdkDrawable *drawable;
11190 gint bin_window_width;
11193 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11194 g_return_val_if_fail (path != NULL, NULL);
11196 widget = GTK_WIDGET (tree_view);
11198 if (!gtk_widget_get_realized (widget))
11201 _pspp_sheet_view_find_node (tree_view,
11208 if (!gtk_tree_model_get_iter (tree_view->priv->model,
11215 background_area.y = y;
11216 background_area.height = ROW_HEIGHT (tree_view);
11218 bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
11220 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11221 bin_window_width + 2,
11222 background_area.height + 2,
11227 expose_area.width = bin_window_width + 2;
11228 expose_area.height = background_area.height + 2;
11230 #if GTK3_TRANSITION
11231 gdk_draw_rectangle (drawable,
11232 widget->style->base_gc [gtk_widget_get_state (widget)],
11235 bin_window_width + 2,
11236 background_area.height + 2);
11239 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11241 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11243 list = (rtl ? list->prev : list->next))
11245 PsppSheetViewColumn *column = list->data;
11246 GdkRectangle cell_area;
11247 gint vertical_separator;
11249 if (!column->visible)
11252 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11254 background_area.x = cell_offset;
11255 background_area.width = column->width;
11257 gtk_widget_style_get (widget,
11258 "vertical-separator", &vertical_separator,
11261 cell_area = background_area;
11263 cell_area.y += vertical_separator / 2;
11264 cell_area.height -= vertical_separator;
11266 if (pspp_sheet_view_column_cell_is_visible (column))
11267 _pspp_sheet_view_column_cell_render (column,
11273 cell_offset += column->width;
11276 #if GTK3_TRANSITION
11277 gdk_draw_rectangle (drawable,
11278 widget->style->black_gc,
11281 bin_window_width + 1,
11282 background_area.height + 1);
11290 * pspp_sheet_view_set_destroy_count_func:
11291 * @tree_view: A #PsppSheetView
11292 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11293 * @data: (allow-none): User data to be passed to @func, or %NULL
11294 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11296 * This function should almost never be used. It is meant for private use by
11297 * ATK for determining the number of visible children that are removed when a row is deleted.
11300 pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view,
11301 PsppSheetDestroyCountFunc func,
11303 GDestroyNotify destroy)
11305 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11307 if (tree_view->priv->destroy_count_destroy)
11308 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11310 tree_view->priv->destroy_count_func = func;
11311 tree_view->priv->destroy_count_data = data;
11312 tree_view->priv->destroy_count_destroy = destroy;
11317 * Interactive search
11321 * pspp_sheet_view_set_enable_search:
11322 * @tree_view: A #PsppSheetView
11323 * @enable_search: %TRUE, if the user can search interactively
11325 * If @enable_search is set, then the user can type in text to search through
11326 * the tree interactively (this is sometimes called "typeahead find").
11328 * Note that even if this is %FALSE, the user can still initiate a search
11329 * using the "start-interactive-search" key binding.
11332 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11333 gboolean enable_search)
11335 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11337 enable_search = !!enable_search;
11339 if (tree_view->priv->enable_search != enable_search)
11341 tree_view->priv->enable_search = enable_search;
11342 g_object_notify (G_OBJECT (tree_view), "enable-search");
11347 * pspp_sheet_view_get_enable_search:
11348 * @tree_view: A #PsppSheetView
11350 * Returns whether or not the tree allows to start interactive searching
11351 * by typing in text.
11353 * Return value: whether or not to let the user search interactively
11356 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11358 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11360 return tree_view->priv->enable_search;
11365 * pspp_sheet_view_get_search_column:
11366 * @tree_view: A #PsppSheetView
11368 * Gets the column searched on by the interactive search code.
11370 * Return value: the column the interactive search code searches in.
11373 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11375 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11377 return (tree_view->priv->search_column);
11381 * pspp_sheet_view_set_search_column:
11382 * @tree_view: A #PsppSheetView
11383 * @column: the column of the model to search in, or -1 to disable searching
11385 * Sets @column as the column where the interactive search code should
11386 * search in for the current model.
11388 * If the search column is set, users can use the "start-interactive-search"
11389 * key binding to bring up search popup. The enable-search property controls
11390 * whether simply typing text will also start an interactive search.
11392 * Note that @column refers to a column of the current model. The search
11393 * column is reset to -1 when the model is changed.
11396 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11399 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11400 g_return_if_fail (column >= -1);
11402 if (tree_view->priv->search_column == column)
11405 tree_view->priv->search_column = column;
11406 g_object_notify (G_OBJECT (tree_view), "search-column");
11410 * pspp_sheet_view_get_search_equal_func:
11411 * @tree_view: A #PsppSheetView
11413 * Returns the compare function currently in use.
11415 * Return value: the currently used compare function for the search code.
11418 PsppSheetViewSearchEqualFunc
11419 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11421 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11423 return tree_view->priv->search_equal_func;
11427 * pspp_sheet_view_set_search_equal_func:
11428 * @tree_view: A #PsppSheetView
11429 * @search_equal_func: the compare function to use during the search
11430 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11431 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11433 * Sets the compare function for the interactive search capabilities; note
11434 * that somewhat like strcmp() returning 0 for equality
11435 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11438 pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view,
11439 PsppSheetViewSearchEqualFunc search_equal_func,
11440 gpointer search_user_data,
11441 GDestroyNotify search_destroy)
11443 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11444 g_return_if_fail (search_equal_func != NULL);
11446 if (tree_view->priv->search_destroy)
11447 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11449 tree_view->priv->search_equal_func = search_equal_func;
11450 tree_view->priv->search_user_data = search_user_data;
11451 tree_view->priv->search_destroy = search_destroy;
11452 if (tree_view->priv->search_equal_func == NULL)
11453 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11457 * pspp_sheet_view_get_search_entry:
11458 * @tree_view: A #PsppSheetView
11460 * Returns the #GtkEntry which is currently in use as interactive search
11461 * entry for @tree_view. In case the built-in entry is being used, %NULL
11462 * will be returned.
11464 * Return value: the entry currently in use as search entry.
11469 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11471 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11473 if (tree_view->priv->search_custom_entry_set)
11474 return GTK_ENTRY (tree_view->priv->search_entry);
11480 * pspp_sheet_view_set_search_entry:
11481 * @tree_view: A #PsppSheetView
11482 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11484 * Sets the entry which the interactive search code will use for this
11485 * @tree_view. This is useful when you want to provide a search entry
11486 * in our interface at all time at a fixed position. Passing %NULL for
11487 * @entry will make the interactive search code use the built-in popup
11493 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11496 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11497 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11499 if (tree_view->priv->search_custom_entry_set)
11501 if (tree_view->priv->search_entry_changed_id)
11503 g_signal_handler_disconnect (tree_view->priv->search_entry,
11504 tree_view->priv->search_entry_changed_id);
11505 tree_view->priv->search_entry_changed_id = 0;
11507 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11508 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11511 g_object_unref (tree_view->priv->search_entry);
11513 else if (tree_view->priv->search_window)
11515 gtk_widget_destroy (tree_view->priv->search_window);
11517 tree_view->priv->search_window = NULL;
11522 tree_view->priv->search_entry = g_object_ref (entry);
11523 tree_view->priv->search_custom_entry_set = TRUE;
11525 if (tree_view->priv->search_entry_changed_id == 0)
11527 tree_view->priv->search_entry_changed_id =
11528 g_signal_connect (tree_view->priv->search_entry, "changed",
11529 G_CALLBACK (pspp_sheet_view_search_init),
11533 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11534 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11537 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11541 tree_view->priv->search_entry = NULL;
11542 tree_view->priv->search_custom_entry_set = FALSE;
11547 * pspp_sheet_view_set_search_position_func:
11548 * @tree_view: A #PsppSheetView
11549 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11550 * to use the default search position function
11551 * @data: (allow-none): user data to pass to @func, or %NULL
11552 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11554 * Sets the function to use when positioning the search dialog.
11559 pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view,
11560 PsppSheetViewSearchPositionFunc func,
11561 gpointer user_data,
11562 GDestroyNotify destroy)
11564 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11566 if (tree_view->priv->search_position_destroy)
11567 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11569 tree_view->priv->search_position_func = func;
11570 tree_view->priv->search_position_user_data = user_data;
11571 tree_view->priv->search_position_destroy = destroy;
11572 if (tree_view->priv->search_position_func == NULL)
11573 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11577 * pspp_sheet_view_get_search_position_func:
11578 * @tree_view: A #PsppSheetView
11580 * Returns the positioning function currently in use.
11582 * Return value: the currently used function for positioning the search dialog.
11586 PsppSheetViewSearchPositionFunc
11587 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11589 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11591 return tree_view->priv->search_position_func;
11596 pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
11597 PsppSheetView *tree_view)
11599 if (tree_view->priv->disable_popdown)
11602 if (tree_view->priv->search_entry_changed_id)
11604 g_signal_handler_disconnect (tree_view->priv->search_entry,
11605 tree_view->priv->search_entry_changed_id);
11606 tree_view->priv->search_entry_changed_id = 0;
11608 if (tree_view->priv->typeselect_flush_timeout)
11610 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11611 tree_view->priv->typeselect_flush_timeout = 0;
11614 if (gtk_widget_get_visible (search_dialog))
11616 /* send focus-in event */
11617 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11618 gtk_widget_hide (search_dialog);
11619 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11620 send_focus_change (GTK_WIDGET (tree_view), TRUE);
11625 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11626 GtkWidget *search_dialog,
11627 gpointer user_data)
11630 gint tree_x, tree_y;
11631 gint tree_width, tree_height;
11632 GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
11633 GdkScreen *screen = gdk_window_get_screen (tree_window);
11634 GtkRequisition requisition;
11636 GdkRectangle monitor;
11638 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11639 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11641 gtk_widget_realize (search_dialog);
11643 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11644 tree_width = gdk_window_get_width (tree_window);
11645 tree_height = gdk_window_get_height (tree_window);
11647 gtk_widget_size_request (search_dialog, &requisition);
11649 if (tree_x + tree_width > gdk_screen_get_width (screen))
11650 x = gdk_screen_get_width (screen) - requisition.width;
11651 else if (tree_x + tree_width - requisition.width < 0)
11654 x = tree_x + tree_width - requisition.width;
11656 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11657 y = gdk_screen_get_height (screen) - requisition.height;
11658 else if (tree_y + tree_height < 0) /* isn't really possible ... */
11661 y = tree_y + tree_height;
11663 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11667 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11671 PsppSheetView *tree_view = (PsppSheetView *)data;
11673 tree_view->priv->disable_popdown = 1;
11674 g_signal_connect (menu, "hide",
11675 G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11678 #if GTK3_TRANSITION
11679 /* Because we're visible but offscreen, we just set a flag in the preedit
11683 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11684 PsppSheetView *tree_view)
11686 tree_view->priv->imcontext_changed = 1;
11687 if (tree_view->priv->typeselect_flush_timeout)
11689 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11690 tree_view->priv->typeselect_flush_timeout =
11691 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11692 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11700 pspp_sheet_view_search_activate (GtkEntry *entry,
11701 PsppSheetView *tree_view)
11706 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11709 /* If we have a row selected and it's the cursor row, we activate
11711 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11713 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11715 _pspp_sheet_view_find_node (tree_view, path, &node);
11717 if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11718 pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11720 gtk_tree_path_free (path);
11725 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11727 PsppSheetView *tree_view = (PsppSheetView *)data;
11729 tree_view->priv->disable_popdown = 0;
11735 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11738 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11742 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11743 GdkEventAny *event,
11744 PsppSheetView *tree_view)
11746 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11748 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11754 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11755 GdkEventButton *event,
11756 PsppSheetView *tree_view)
11758 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11760 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11762 if (event->window == tree_view->priv->bin_window)
11763 pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
11769 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
11770 GdkEventScroll *event,
11771 PsppSheetView *tree_view)
11773 gboolean retval = FALSE;
11775 if (event->direction == GDK_SCROLL_UP)
11777 pspp_sheet_view_search_move (widget, tree_view, TRUE);
11780 else if (event->direction == GDK_SCROLL_DOWN)
11782 pspp_sheet_view_search_move (widget, tree_view, FALSE);
11786 /* renew the flush timeout */
11787 if (retval && tree_view->priv->typeselect_flush_timeout
11788 && !tree_view->priv->search_custom_entry_set)
11790 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11791 tree_view->priv->typeselect_flush_timeout =
11792 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11793 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11801 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
11802 GdkEventKey *event,
11803 PsppSheetView *tree_view)
11805 gboolean retval = FALSE;
11807 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11808 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11810 /* close window and cancel the search */
11811 if (!tree_view->priv->search_custom_entry_set
11812 && (event->keyval == GDK_Escape ||
11813 event->keyval == GDK_Tab ||
11814 event->keyval == GDK_KP_Tab ||
11815 event->keyval == GDK_ISO_Left_Tab))
11817 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11821 /* select previous matching iter */
11822 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
11824 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11825 gtk_widget_error_bell (widget);
11830 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
11831 && (event->keyval == GDK_g || event->keyval == GDK_G))
11833 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11834 gtk_widget_error_bell (widget);
11839 /* select next matching iter */
11840 if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
11842 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11843 gtk_widget_error_bell (widget);
11848 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
11849 && (event->keyval == GDK_g || event->keyval == GDK_G))
11851 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11852 gtk_widget_error_bell (widget);
11857 /* renew the flush timeout */
11858 if (retval && tree_view->priv->typeselect_flush_timeout
11859 && !tree_view->priv->search_custom_entry_set)
11861 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11862 tree_view->priv->typeselect_flush_timeout =
11863 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11864 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11871 /* this function returns FALSE if there is a search string but
11872 * nothing was found, and TRUE otherwise.
11875 pspp_sheet_view_search_move (GtkWidget *window,
11876 PsppSheetView *tree_view,
11884 GtkTreeModel *model;
11885 PsppSheetSelection *selection;
11887 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
11889 g_return_val_if_fail (text != NULL, FALSE);
11891 len = strlen (text);
11893 if (up && tree_view->priv->selected_iter == 1)
11894 return strlen (text) < 1;
11896 len = strlen (text);
11901 model = pspp_sheet_view_get_model (tree_view);
11902 selection = pspp_sheet_view_get_selection (tree_view);
11905 pspp_sheet_selection_unselect_all (selection);
11906 if (!gtk_tree_model_get_iter_first (model, &iter))
11909 ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
11910 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
11915 tree_view->priv->selected_iter += up?(-1):(1);
11920 /* return to old iter */
11922 gtk_tree_model_get_iter_first (model, &iter);
11923 pspp_sheet_view_search_iter (model, selection,
11925 &count, tree_view->priv->selected_iter);
11931 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
11935 gpointer search_data)
11937 gboolean retval = TRUE;
11939 gchar *normalized_string;
11940 gchar *normalized_key;
11941 gchar *case_normalized_string = NULL;
11942 gchar *case_normalized_key = NULL;
11943 GValue value = {0,};
11944 GValue transformed = {0,};
11946 gtk_tree_model_get_value (model, iter, column, &value);
11948 g_value_init (&transformed, G_TYPE_STRING);
11950 if (!g_value_transform (&value, &transformed))
11952 g_value_unset (&value);
11956 g_value_unset (&value);
11958 str = g_value_get_string (&transformed);
11961 g_value_unset (&transformed);
11965 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
11966 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
11968 if (normalized_string && normalized_key)
11970 case_normalized_string = g_utf8_casefold (normalized_string, -1);
11971 case_normalized_key = g_utf8_casefold (normalized_key, -1);
11973 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
11977 g_value_unset (&transformed);
11978 g_free (normalized_key);
11979 g_free (normalized_string);
11980 g_free (case_normalized_key);
11981 g_free (case_normalized_string);
11987 pspp_sheet_view_search_iter (GtkTreeModel *model,
11988 PsppSheetSelection *selection,
11997 PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
11999 path = gtk_tree_model_get_path (model, iter);
12000 _pspp_sheet_view_find_node (tree_view, path, &node);
12004 gboolean done = FALSE;
12006 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
12011 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
12013 pspp_sheet_selection_select_iter (selection, iter);
12014 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12017 gtk_tree_path_free (path);
12026 node = pspp_sheet_view_node_next (tree_view, node);
12032 has_next = gtk_tree_model_iter_next (model, iter);
12035 gtk_tree_path_next (path);
12038 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12043 gtk_tree_path_free (path);
12045 /* we've run out of tree, done with this func */
12057 pspp_sheet_view_search_init (GtkWidget *entry,
12058 PsppSheetView *tree_view)
12064 GtkTreeModel *model;
12065 PsppSheetSelection *selection;
12067 g_return_if_fail (GTK_IS_ENTRY (entry));
12068 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12070 text = gtk_entry_get_text (GTK_ENTRY (entry));
12072 model = pspp_sheet_view_get_model (tree_view);
12073 selection = pspp_sheet_view_get_selection (tree_view);
12076 pspp_sheet_selection_unselect_all (selection);
12077 if (tree_view->priv->typeselect_flush_timeout
12078 && !tree_view->priv->search_custom_entry_set)
12080 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12081 tree_view->priv->typeselect_flush_timeout =
12082 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12083 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12090 if (!gtk_tree_model_get_iter_first (model, &iter))
12093 ret = pspp_sheet_view_search_iter (model, selection,
12098 tree_view->priv->selected_iter = 1;
12102 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12103 PsppSheetView *tree_view)
12105 if (tree_view->priv->edited_column == NULL)
12108 _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12109 tree_view->priv->edited_column = NULL;
12111 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12112 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12114 g_signal_handlers_disconnect_by_func (cell_editable,
12115 pspp_sheet_view_remove_widget,
12117 g_signal_handlers_disconnect_by_func (cell_editable,
12118 pspp_sheet_view_editable_button_press_event,
12120 g_signal_handlers_disconnect_by_func (cell_editable,
12121 pspp_sheet_view_editable_clicked,
12124 gtk_container_remove (GTK_CONTAINER (tree_view),
12125 GTK_WIDGET (cell_editable));
12127 /* FIXME should only redraw a single node */
12128 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12132 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12133 GtkTreePath *cursor_path)
12136 GdkRectangle background_area;
12137 GdkRectangle cell_area;
12138 GtkCellEditable *editable_widget = NULL;
12139 gchar *path_string;
12140 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12141 gint retval = FALSE;
12144 g_assert (tree_view->priv->focus_column);
12146 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12149 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12150 if (cursor_node < 0)
12153 path_string = gtk_tree_path_to_string (cursor_path);
12154 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12156 pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12157 tree_view->priv->model,
12159 pspp_sheet_view_get_background_area (tree_view,
12161 tree_view->priv->focus_column,
12163 pspp_sheet_view_get_cell_area (tree_view,
12165 tree_view->priv->focus_column,
12168 if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12177 if (editable_widget != NULL)
12181 GtkCellRenderer *cell;
12184 cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12186 _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12189 area.width -= right + left;
12191 pspp_sheet_view_real_start_editing (tree_view,
12192 tree_view->priv->focus_column,
12201 g_free (path_string);
12206 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12207 GdkEventButton *event,
12208 PsppSheetView *sheet_view)
12212 node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12213 "pspp-sheet-view-node"));
12214 return pspp_sheet_view_row_head_clicked (sheet_view,
12216 sheet_view->priv->edited_column,
12221 pspp_sheet_view_editable_clicked (GtkButton *button,
12222 PsppSheetView *sheet_view)
12224 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12229 is_all_selected (GtkWidget *widget)
12231 GtkEntryBuffer *buffer;
12232 gint start_pos, end_pos;
12234 if (!GTK_IS_ENTRY (widget))
12237 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12238 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12239 &start_pos, &end_pos)
12241 && end_pos == gtk_entry_buffer_get_length (buffer));
12245 is_at_left (GtkWidget *widget)
12247 return (GTK_IS_ENTRY (widget)
12248 && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12252 is_at_right (GtkWidget *widget)
12254 GtkEntryBuffer *buffer;
12257 if (!GTK_IS_ENTRY (widget))
12260 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12261 length = gtk_entry_buffer_get_length (buffer);
12262 return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12266 pspp_sheet_view_event (GtkWidget *widget,
12267 GdkEventKey *event,
12268 PsppSheetView *tree_view)
12270 PsppSheetViewColumn *column;
12277 /* Intercept only key press events.
12278 It would make sense to use "key-press-event" instead of "event", but
12279 GtkEntry attaches its own signal handler to "key-press-event" that runs
12280 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12282 if (event->type != GDK_KEY_PRESS)
12285 keyval = event->keyval;
12287 switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12290 switch (event->keyval)
12292 case GDK_Left: case GDK_KP_Left:
12293 case GDK_Home: case GDK_KP_Home:
12294 if (!is_all_selected (widget) && !is_at_left (widget))
12298 case GDK_Right: case GDK_KP_Right:
12299 case GDK_End: case GDK_KP_End:
12300 if (!is_all_selected (widget) && !is_at_right (widget))
12304 case GDK_Up: case GDK_KP_Up:
12305 case GDK_Down: case GDK_KP_Down:
12308 case GDK_Page_Up: case GDK_KP_Page_Up:
12309 case GDK_Page_Down: case GDK_KP_Page_Down:
12320 case GDK_Tab: case GDK_KP_Tab:
12321 case GDK_ISO_Left_Tab:
12330 case GDK_SHIFT_MASK:
12331 switch (event->keyval)
12334 case GDK_ISO_Left_Tab:
12343 case GDK_CONTROL_MASK:
12344 switch (event->keyval)
12346 case GDK_Left: case GDK_KP_Left:
12347 if (!is_all_selected (widget) && !is_at_left (widget))
12351 case GDK_Right: case GDK_KP_Right:
12352 if (!is_all_selected (widget) && !is_at_right (widget))
12356 case GDK_Up: case GDK_KP_Up:
12357 case GDK_Down: case GDK_KP_Down:
12369 row = tree_view->priv->edited_row;
12370 column = tree_view->priv->edited_column;
12371 path = gtk_tree_path_new_from_indices (row, -1);
12373 pspp_sheet_view_stop_editing (tree_view, cancel);
12374 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12376 pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12377 gtk_tree_path_free (path);
12379 handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12380 G_OBJECT (tree_view));
12382 g_signal_stop_emission_by_name (widget, "event");
12384 pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12385 pspp_sheet_view_start_editing (tree_view, path);
12386 gtk_tree_path_free (path);
12392 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12395 PsppSheetView *sheet_view = data;
12397 g_signal_connect (widget, "event",
12398 G_CALLBACK (pspp_sheet_view_event),
12401 if (GTK_IS_CONTAINER (widget))
12402 gtk_container_foreach (GTK_CONTAINER (widget),
12403 pspp_sheet_view_override_cell_keypresses,
12408 pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
12409 PsppSheetViewColumn *column,
12411 GtkCellEditable *cell_editable,
12412 GdkRectangle *cell_area,
12416 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12417 gint pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
12420 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12422 tree_view->priv->edited_column = column;
12423 _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12425 row = gtk_tree_path_get_indices (path)[0];
12426 tree_view->priv->edited_row = row;
12427 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12428 cell_area->y += pre_val - (int)gtk_adjustment_get_value (tree_view->priv->vadjustment);
12430 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12431 pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12432 tree_view->priv->anchor_column = column;
12434 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12436 pspp_sheet_view_put (tree_view,
12437 GTK_WIDGET (cell_editable),
12441 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12442 (GdkEvent *)event);
12444 gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12445 g_signal_connect (cell_editable, "remove-widget",
12446 G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12447 if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12448 GTK_IS_BUTTON (cell_editable))
12450 g_signal_connect (cell_editable, "button-press-event",
12451 G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12453 g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12454 GINT_TO_POINTER (row));
12455 g_signal_connect (cell_editable, "clicked",
12456 G_CALLBACK (pspp_sheet_view_editable_clicked),
12460 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12465 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12466 gboolean cancel_editing)
12468 PsppSheetViewColumn *column;
12469 GtkCellRenderer *cell;
12471 if (tree_view->priv->edited_column == NULL)
12475 * This is very evil. We need to do this, because
12476 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12477 * later on. If pspp_sheet_view_row_changed notices
12478 * tree_view->priv->edited_column != NULL, it'll call
12479 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12481 * Please read that again if you intend to modify anything here.
12484 column = tree_view->priv->edited_column;
12485 tree_view->priv->edited_column = NULL;
12487 cell = _pspp_sheet_view_column_get_edited_cell (column);
12488 gtk_cell_renderer_stop_editing (cell, cancel_editing);
12490 if (!cancel_editing)
12491 gtk_cell_editable_editing_done (column->editable_widget);
12493 tree_view->priv->edited_column = column;
12495 gtk_cell_editable_remove_widget (column->editable_widget);
12500 * pspp_sheet_view_set_hover_selection:
12501 * @tree_view: a #PsppSheetView
12502 * @hover: %TRUE to enable hover selection mode
12504 * Enables of disables the hover selection mode of @tree_view.
12505 * Hover selection makes the selected row follow the pointer.
12506 * Currently, this works only for the selection modes
12507 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12512 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12515 hover = hover != FALSE;
12517 if (hover != tree_view->priv->hover_selection)
12519 tree_view->priv->hover_selection = hover;
12521 g_object_notify (G_OBJECT (tree_view), "hover-selection");
12526 * pspp_sheet_view_get_hover_selection:
12527 * @tree_view: a #PsppSheetView
12529 * Returns whether hover selection mode is turned on for @tree_view.
12531 * Return value: %TRUE if @tree_view is in hover selection mode
12536 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12538 return tree_view->priv->hover_selection;
12542 * pspp_sheet_view_set_rubber_banding:
12543 * @tree_view: a #PsppSheetView
12544 * @enable: %TRUE to enable rubber banding
12546 * Enables or disables rubber banding in @tree_view. If the selection mode is
12547 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12548 * banding will allow the user to select multiple rows by dragging the mouse.
12553 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12556 enable = enable != FALSE;
12558 if (enable != tree_view->priv->rubber_banding_enable)
12560 tree_view->priv->rubber_banding_enable = enable;
12562 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12567 * pspp_sheet_view_get_rubber_banding:
12568 * @tree_view: a #PsppSheetView
12570 * Returns whether rubber banding is turned on for @tree_view. If the
12571 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12572 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12573 * select multiple rows by dragging the mouse.
12575 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12580 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12582 return tree_view->priv->rubber_banding_enable;
12586 * pspp_sheet_view_is_rubber_banding_active:
12587 * @tree_view: a #PsppSheetView
12589 * Returns whether a rubber banding operation is currently being done
12592 * Return value: %TRUE if a rubber banding operation is currently being
12593 * done in @tree_view.
12598 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12600 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12602 if (tree_view->priv->rubber_banding_enable
12603 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12610 pspp_sheet_view_grab_notify (GtkWidget *widget,
12611 gboolean was_grabbed)
12613 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12615 tree_view->priv->in_grab = !was_grabbed;
12619 tree_view->priv->pressed_button = -1;
12621 if (tree_view->priv->rubber_band_status)
12622 pspp_sheet_view_stop_rubber_band (tree_view);
12627 pspp_sheet_view_state_changed (GtkWidget *widget,
12628 GtkStateType previous_state)
12630 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12632 if (gtk_widget_get_realized (widget))
12634 GtkStyle *style = gtk_widget_get_style (widget);
12635 gdk_window_set_background (tree_view->priv->bin_window, &style->base[gtk_widget_get_state (widget)]);
12638 gtk_widget_queue_draw (widget);
12642 * pspp_sheet_view_get_grid_lines:
12643 * @tree_view: a #PsppSheetView
12645 * Returns which grid lines are enabled in @tree_view.
12647 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12652 PsppSheetViewGridLines
12653 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12655 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12657 return tree_view->priv->grid_lines;
12661 * pspp_sheet_view_set_grid_lines:
12662 * @tree_view: a #PsppSheetView
12663 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12666 * Sets which grid lines to draw in @tree_view.
12671 pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view,
12672 PsppSheetViewGridLines grid_lines)
12674 PsppSheetViewPrivate *priv;
12675 PsppSheetViewGridLines old_grid_lines;
12677 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12679 priv = tree_view->priv;
12681 old_grid_lines = priv->grid_lines;
12682 priv->grid_lines = grid_lines;
12684 if (old_grid_lines != grid_lines)
12686 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12688 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12693 * pspp_sheet_view_get_special_cells:
12694 * @tree_view: a #PsppSheetView
12696 * Returns which grid lines are enabled in @tree_view.
12698 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12699 * the sheet view contain special cells.
12701 PsppSheetViewSpecialCells
12702 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12704 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12706 return tree_view->priv->special_cells;
12710 * pspp_sheet_view_set_special_cells:
12711 * @tree_view: a #PsppSheetView
12712 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12713 * the sheet view contain special cells.
12715 * Sets whether rows in the sheet view contain special cells, controlling the
12716 * rendering of row selections.
12719 pspp_sheet_view_set_special_cells (PsppSheetView *tree_view,
12720 PsppSheetViewSpecialCells special_cells)
12722 PsppSheetViewPrivate *priv;
12724 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12726 priv = tree_view->priv;
12728 if (priv->special_cells != special_cells)
12730 priv->special_cells = special_cells;
12731 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12732 g_object_notify (G_OBJECT (tree_view), "special-cells");
12737 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12739 /* XXX (re)calculate fixed_height if necessary */
12740 return tree_view->priv->fixed_height;
12744 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
12747 g_return_if_fail (fixed_height > 0);
12749 if (tree_view->priv->fixed_height != fixed_height)
12751 tree_view->priv->fixed_height = fixed_height;
12752 g_object_notify (G_OBJECT (tree_view), "fixed-height");
12754 if (!tree_view->priv->fixed_height_set)
12756 tree_view->priv->fixed_height_set = TRUE;
12757 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
12762 * pspp_sheet_view_set_tooltip_row:
12763 * @tree_view: a #PsppSheetView
12764 * @tooltip: a #GtkTooltip
12765 * @path: a #GtkTreePath
12767 * Sets the tip area of @tooltip to be the area covered by the row at @path.
12768 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12769 * See also gtk_tooltip_set_tip_area().
12774 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
12775 GtkTooltip *tooltip,
12778 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12779 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12781 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
12785 * pspp_sheet_view_set_tooltip_cell:
12786 * @tree_view: a #PsppSheetView
12787 * @tooltip: a #GtkTooltip
12788 * @path: (allow-none): a #GtkTreePath or %NULL
12789 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
12790 * @cell: (allow-none): a #GtkCellRenderer or %NULL
12792 * Sets the tip area of @tooltip to the area @path, @column and @cell have
12793 * in common. For example if @path is %NULL and @column is set, the tip
12794 * area will be set to the full area covered by @column. See also
12795 * gtk_tooltip_set_tip_area().
12797 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12802 pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view,
12803 GtkTooltip *tooltip,
12805 PsppSheetViewColumn *column,
12806 GtkCellRenderer *cell)
12810 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12811 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12812 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
12813 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
12815 /* Determine x values. */
12816 if (column && cell)
12821 pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
12822 pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
12824 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12827 rect.width = width;
12833 pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
12834 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12837 rect.width = tmp.width;
12841 GtkAllocation allocation;
12842 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
12844 rect.width = allocation.width;
12847 /* Determine y values. */
12852 pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
12853 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12856 rect.height = tmp.height;
12861 rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
12864 gtk_tooltip_set_tip_area (tooltip, &rect);
12868 * pspp_sheet_view_get_tooltip_context:
12869 * @tree_view: a #PsppSheetView
12870 * @x: the x coordinate (relative to widget coordinates)
12871 * @y: the y coordinate (relative to widget coordinates)
12872 * @keyboard_tip: whether this is a keyboard tooltip or not
12873 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
12874 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
12875 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
12877 * This function is supposed to be used in a #GtkWidget::query-tooltip
12878 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
12879 * which are received in the signal handler, should be passed to this
12880 * function without modification.
12882 * The return value indicates whether there is a tree view row at the given
12883 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
12884 * tooltips the row returned will be the cursor row. When %TRUE, then any of
12885 * @model, @path and @iter which have been provided will be set to point to
12886 * that row and the corresponding model. @x and @y will always be converted
12887 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
12889 * Return value: whether or not the given tooltip context points to a row.
12894 pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view,
12897 gboolean keyboard_tip,
12898 GtkTreeModel **model,
12899 GtkTreePath **path,
12902 GtkTreePath *tmppath = NULL;
12904 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12905 g_return_val_if_fail (x != NULL, FALSE);
12906 g_return_val_if_fail (y != NULL, FALSE);
12910 pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
12917 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
12920 if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
12921 &tmppath, NULL, NULL, NULL))
12926 *model = pspp_sheet_view_get_model (tree_view);
12929 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
12935 gtk_tree_path_free (tmppath);
12941 pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget,
12944 gboolean keyboard_tip,
12945 GtkTooltip *tooltip,
12948 GValue value = { 0, };
12949 GValue transformed = { 0, };
12952 GtkTreeModel *model;
12953 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12955 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
12958 &model, &path, &iter))
12961 gtk_tree_model_get_value (model, &iter,
12962 tree_view->priv->tooltip_column, &value);
12964 g_value_init (&transformed, G_TYPE_STRING);
12966 if (!g_value_transform (&value, &transformed))
12968 g_value_unset (&value);
12969 gtk_tree_path_free (path);
12974 g_value_unset (&value);
12976 if (!g_value_get_string (&transformed))
12978 g_value_unset (&transformed);
12979 gtk_tree_path_free (path);
12984 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
12985 pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
12987 gtk_tree_path_free (path);
12988 g_value_unset (&transformed);
12994 * pspp_sheet_view_set_tooltip_column:
12995 * @tree_view: a #PsppSheetView
12996 * @column: an integer, which is a valid column number for @tree_view's model
12998 * If you only plan to have simple (text-only) tooltips on full rows, you
12999 * can use this function to have #PsppSheetView handle these automatically
13000 * for you. @column should be set to the column in @tree_view's model
13001 * containing the tooltip texts, or -1 to disable this feature.
13003 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
13004 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
13006 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
13007 * so &, <, etc have to be escaped in the text.
13012 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
13015 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13017 if (column == tree_view->priv->tooltip_column)
13022 g_signal_handlers_disconnect_by_func (tree_view,
13023 pspp_sheet_view_set_tooltip_query_cb,
13025 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13029 if (tree_view->priv->tooltip_column == -1)
13031 g_signal_connect (tree_view, "query-tooltip",
13032 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13033 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13037 tree_view->priv->tooltip_column = column;
13038 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13042 * pspp_sheet_view_get_tooltip_column:
13043 * @tree_view: a #PsppSheetView
13045 * Returns the column of @tree_view's model which is being used for
13046 * displaying tooltips on @tree_view's rows.
13048 * Return value: the index of the tooltip column that is currently being
13049 * used, or -1 if this is disabled.
13054 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13056 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13058 return tree_view->priv->tooltip_column;
13062 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13063 GValue *return_accu,
13064 const GValue *handler_return,
13067 gboolean continue_emission;
13068 gboolean signal_handled;
13070 signal_handled = g_value_get_boolean (handler_return);
13071 g_value_set_boolean (return_accu, signal_handled);
13072 continue_emission = !signal_handled;
13074 return continue_emission;
13079 pspp_sheet_view_grid_lines_get_type (void)
13081 static GType etype = 0;
13082 if (G_UNLIKELY(etype == 0)) {
13083 static const GEnumValue values[] = {
13084 { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13085 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13086 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13087 { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13090 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13096 pspp_sheet_view_special_cells_get_type (void)
13098 static GType etype = 0;
13099 if (G_UNLIKELY(etype == 0)) {
13100 static const GEnumValue values[] = {
13101 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13102 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13103 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13106 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);