1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012, 2013 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 #if GTK_CHECK_VERSION(3,8,0)
1527 gtk_widget_register_window (widget, window);
1529 gdk_window_set_user_data (window, widget);
1531 gtk_widget_get_allocation (widget, &allocation);
1533 /* Make the window for the tree */
1535 attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
1536 attributes.width = MAX (tree_view->priv->width, allocation.width);
1537 attributes.height = allocation.height;
1538 attributes.event_mask = (GDK_EXPOSURE_MASK |
1540 GDK_POINTER_MOTION_MASK |
1541 GDK_ENTER_NOTIFY_MASK |
1542 GDK_LEAVE_NOTIFY_MASK |
1543 GDK_BUTTON_PRESS_MASK |
1544 GDK_BUTTON_RELEASE_MASK |
1545 gtk_widget_get_events (widget));
1547 tree_view->priv->bin_window = gdk_window_new (window,
1548 &attributes, attributes_mask);
1549 #if GTK_CHECK_VERSION(3,8,0)
1550 gtk_widget_register_window (widget, tree_view->priv->bin_window);
1552 gdk_window_set_user_data (tree_view->priv->bin_window, widget);
1554 gtk_widget_get_allocation (widget, &allocation);
1556 /* Make the column header window */
1559 attributes.width = MAX (tree_view->priv->width, allocation.width);
1560 attributes.height = tree_view->priv->header_height;
1561 attributes.event_mask = (GDK_EXPOSURE_MASK |
1563 GDK_BUTTON_PRESS_MASK |
1564 GDK_BUTTON_RELEASE_MASK |
1565 GDK_KEY_PRESS_MASK |
1566 GDK_KEY_RELEASE_MASK |
1567 gtk_widget_get_events (widget));
1569 tree_view->priv->header_window = gdk_window_new (window,
1570 &attributes, attributes_mask);
1571 #if GTK_CHECK_VERSION(3,8,0)
1572 gtk_widget_register_window (widget, tree_view->priv->header_window);
1574 gdk_window_set_user_data (tree_view->priv->header_window, widget);
1577 { /* Ensure Background */
1578 GtkStyleContext *context;
1580 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
1582 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
1583 gtk_style_context_set_background (context, tree_view->priv->header_window);
1586 tmp_list = tree_view->priv->children;
1589 PsppSheetViewChild *child = tmp_list->data;
1590 tmp_list = tmp_list->next;
1592 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
1595 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
1596 _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list->data));
1598 /* Need to call those here, since they create GCs */
1599 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
1601 install_presize_handler (tree_view);
1605 pspp_sheet_view_unrealize (GtkWidget *widget)
1607 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1608 PsppSheetViewPrivate *priv = tree_view->priv;
1611 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget);
1613 if (priv->scroll_timeout != 0)
1615 g_source_remove (priv->scroll_timeout);
1616 priv->scroll_timeout = 0;
1619 if (priv->open_dest_timeout != 0)
1621 g_source_remove (priv->open_dest_timeout);
1622 priv->open_dest_timeout = 0;
1625 if (priv->presize_handler_timer != 0)
1627 g_source_remove (priv->presize_handler_timer);
1628 priv->presize_handler_timer = 0;
1631 if (priv->validate_rows_timer != 0)
1633 g_source_remove (priv->validate_rows_timer);
1634 priv->validate_rows_timer = 0;
1637 if (priv->scroll_sync_timer != 0)
1639 g_source_remove (priv->scroll_sync_timer);
1640 priv->scroll_sync_timer = 0;
1643 if (priv->typeselect_flush_timeout)
1645 g_source_remove (priv->typeselect_flush_timeout);
1646 priv->typeselect_flush_timeout = 0;
1649 for (list = priv->columns; list; list = list->next)
1650 _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list->data));
1652 gdk_window_set_user_data (priv->bin_window, NULL);
1653 gdk_window_destroy (priv->bin_window);
1654 priv->bin_window = NULL;
1656 gdk_window_set_user_data (priv->header_window, NULL);
1657 gdk_window_destroy (priv->header_window);
1658 priv->header_window = NULL;
1660 if (priv->drag_window)
1662 gdk_window_set_user_data (priv->drag_window, NULL);
1663 gdk_window_destroy (priv->drag_window);
1664 priv->drag_window = NULL;
1667 if (priv->drag_highlight_window)
1669 gdk_window_set_user_data (priv->drag_highlight_window, NULL);
1670 gdk_window_destroy (priv->drag_highlight_window);
1671 priv->drag_highlight_window = NULL;
1674 if (tree_view->priv->columns != NULL)
1676 list = tree_view->priv->columns;
1679 PsppSheetViewColumn *column;
1680 column = PSPP_SHEET_VIEW_COLUMN (list->data);
1682 pspp_sheet_view_remove_column (tree_view, column);
1684 tree_view->priv->columns = NULL;
1688 /* GtkWidget::size_request helper */
1690 pspp_sheet_view_size_request_columns (PsppSheetView *tree_view)
1694 tree_view->priv->header_height = 0;
1696 if (tree_view->priv->model)
1698 for (list = tree_view->priv->columns; list; list = list->next)
1700 GtkRequisition requisition;
1701 PsppSheetViewColumn *column = list->data;
1703 pspp_sheet_view_column_size_request (column, &requisition);
1704 column->button_request = requisition.width;
1705 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
1711 /* Called only by ::size_request */
1713 pspp_sheet_view_update_size (PsppSheetView *tree_view)
1716 PsppSheetViewColumn *column;
1719 if (tree_view->priv->model == NULL)
1721 tree_view->priv->width = 0;
1722 tree_view->priv->prev_width = 0;
1723 tree_view->priv->height = 0;
1727 tree_view->priv->prev_width = tree_view->priv->width;
1728 tree_view->priv->width = 0;
1730 /* keep this in sync with size_allocate below */
1731 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
1733 gint real_requested_width = 0;
1734 column = list->data;
1735 if (!column->visible)
1738 if (column->use_resized_width)
1740 real_requested_width = column->resized_width;
1744 real_requested_width = column->fixed_width;
1747 if (column->min_width != -1)
1748 real_requested_width = MAX (real_requested_width, column->min_width);
1749 if (column->max_width != -1)
1750 real_requested_width = MIN (real_requested_width, column->max_width);
1752 tree_view->priv->width += real_requested_width;
1755 tree_view->priv->height = tree_view->priv->fixed_height * tree_view->priv->row_count;
1759 pspp_sheet_view_size_request (GtkWidget *widget,
1760 GtkRequisition *requisition)
1762 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1765 /* we validate some rows initially just to make sure we have some size.
1766 * In practice, with a lot of static lists, this should get a good width.
1768 initialize_fixed_height_mode (tree_view);
1769 pspp_sheet_view_size_request_columns (tree_view);
1770 pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget));
1772 requisition->width = tree_view->priv->width;
1773 requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
1775 tmp_list = tree_view->priv->children;
1779 PsppSheetViewChild *child = tmp_list->data;
1780 GtkRequisition child_requisition;
1782 tmp_list = tmp_list->next;
1784 if (gtk_widget_get_visible (child->widget))
1785 gtk_widget_size_request (child->widget, &child_requisition);
1790 invalidate_column (PsppSheetView *tree_view,
1791 PsppSheetViewColumn *column)
1793 gint column_offset = 0;
1795 GtkWidget *widget = GTK_WIDGET (tree_view);
1798 if (!gtk_widget_get_realized (widget))
1801 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1802 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
1804 list = (rtl ? list->prev : list->next))
1806 PsppSheetViewColumn *tmpcolumn = list->data;
1807 if (tmpcolumn == column)
1809 GdkRectangle invalid_rect;
1810 GtkAllocation allocation;
1812 gtk_widget_get_allocation (widget, &allocation);
1813 invalid_rect.x = column_offset;
1815 invalid_rect.width = column->width;
1816 invalid_rect.height = allocation.height;
1818 gdk_window_invalidate_rect (gtk_widget_get_window (widget), &invalid_rect, TRUE);
1822 column_offset += tmpcolumn->width;
1827 invalidate_last_column (PsppSheetView *tree_view)
1832 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1834 for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
1836 last_column = (rtl ? last_column->next : last_column->prev))
1838 if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible)
1840 invalidate_column (tree_view, last_column->data);
1847 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_view,
1848 PsppSheetViewColumn *column)
1850 gint real_requested_width;
1852 if (column->use_resized_width)
1854 real_requested_width = column->resized_width;
1858 real_requested_width = column->fixed_width;
1861 if (column->min_width != -1)
1862 real_requested_width = MAX (real_requested_width, column->min_width);
1863 if (column->max_width != -1)
1864 real_requested_width = MIN (real_requested_width, column->max_width);
1866 return real_requested_width;
1870 span_intersects (int a0, int a_width,
1871 int b0, int b_width)
1873 int a1 = a0 + a_width;
1874 int b1 = b0 + b_width;
1875 return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1);
1878 /* GtkWidget::size_allocate helper */
1880 pspp_sheet_view_size_allocate_columns (GtkWidget *widget,
1881 gboolean *width_changed)
1883 PsppSheetView *tree_view;
1884 GList *list, *first_column, *last_column;
1885 PsppSheetViewColumn *column;
1886 GtkAllocation col_allocation;
1887 GtkAllocation allocation;
1889 gint extra, extra_per_column;
1890 gint full_requested_width = 0;
1891 gint number_of_expand_columns = 0;
1892 gboolean column_changed = FALSE;
1895 tree_view = PSPP_SHEET_VIEW (widget);
1897 for (last_column = g_list_last (tree_view->priv->columns);
1898 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
1899 last_column = last_column->prev)
1902 if (last_column == NULL)
1905 for (first_column = g_list_first (tree_view->priv->columns);
1906 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
1907 first_column = first_column->next)
1910 col_allocation.y = 0;
1911 col_allocation.height = tree_view->priv->header_height;
1913 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1915 /* find out how many extra space and expandable columns we have */
1916 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
1918 column = (PsppSheetViewColumn *)list->data;
1920 if (!column->visible)
1923 full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1926 number_of_expand_columns++;
1929 gtk_widget_get_allocation (widget, &allocation);
1930 extra = MAX (allocation.width - full_requested_width, 0);
1931 if (number_of_expand_columns > 0)
1932 extra_per_column = extra/number_of_expand_columns;
1934 extra_per_column = 0;
1936 for (list = (rtl ? last_column : first_column);
1937 list != (rtl ? first_column->prev : last_column->next);
1938 list = (rtl ? list->prev : list->next))
1940 gint real_requested_width = 0;
1943 column = list->data;
1944 old_width = column->width;
1946 if (!column->visible)
1949 /* We need to handle the dragged button specially.
1951 if (column == tree_view->priv->drag_column)
1953 GtkAllocation drag_allocation;
1954 drag_allocation.width = gdk_window_get_width (tree_view->priv->drag_window);
1955 drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window);
1956 drag_allocation.x = 0;
1957 drag_allocation.y = 0;
1958 pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column,
1960 width += drag_allocation.width;
1964 real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1966 col_allocation.x = width;
1967 column->width = real_requested_width;
1971 if (number_of_expand_columns == 1)
1973 /* We add the remander to the last column as
1975 column->width += extra;
1979 column->width += extra_per_column;
1980 extra -= extra_per_column;
1981 number_of_expand_columns --;
1985 if (column->width != old_width)
1986 g_object_notify (G_OBJECT (column), "width");
1988 col_allocation.width = column->width;
1989 width += column->width;
1991 if (column->width > old_width)
1992 column_changed = TRUE;
1994 pspp_sheet_view_column_size_allocate (column, &col_allocation);
1997 gdk_window_move_resize (column->window,
1998 col_allocation.x + (rtl ? 0 : col_allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
2000 TREE_VIEW_DRAG_WIDTH, col_allocation.height);
2003 /* We change the width here. The user might have been resizing columns,
2004 * so the total width of the tree view changes.
2006 tree_view->priv->width = width;
2008 *width_changed = TRUE;
2011 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2015 update_childrens_allocation (PsppSheetView *tree_view)
2018 for (tmp_list = tree_view->priv->children; tmp_list; tmp_list = tmp_list->next)
2020 PsppSheetViewChild *child = tmp_list->data;
2021 GtkAllocation allocation;
2024 /* totally ignore our child's requisition */
2025 path = _pspp_sheet_view_find_path (tree_view, child->node);
2026 pspp_sheet_view_get_cell_area (tree_view, path, child->column, &allocation);
2027 gtk_tree_path_free (path);
2028 gtk_widget_size_allocate (child->widget, &allocation);
2033 pspp_sheet_view_size_allocate (GtkWidget *widget,
2034 GtkAllocation *allocation)
2036 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2038 gboolean width_changed = FALSE;
2039 GtkAllocation old_allocation;
2040 gtk_widget_get_allocation (widget, &old_allocation);
2042 if (allocation->width != old_allocation.width)
2043 width_changed = TRUE;
2045 gtk_widget_set_allocation (widget, allocation);
2047 /* We size-allocate the columns first because the width of the
2048 * tree view (used in updating the adjustments below) might change.
2050 pspp_sheet_view_size_allocate_columns (widget, &width_changed);
2052 gtk_adjustment_set_page_size (tree_view->priv->hadjustment, allocation->width);
2053 gtk_adjustment_set_page_increment (tree_view->priv->hadjustment, allocation->width * 0.9);
2054 gtk_adjustment_set_step_increment (tree_view->priv->hadjustment, allocation->width * 0.1);
2055 gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0);
2056 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->hadjustment), tree_view->priv->width));
2058 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2060 if (allocation->width < tree_view->priv->width)
2062 if (tree_view->priv->init_hadjust_value)
2064 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2065 tree_view->priv->init_hadjust_value = FALSE;
2067 else if (allocation->width != old_allocation.width)
2069 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));
2072 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));
2076 gtk_adjustment_set_value (tree_view->priv->hadjustment, 0);
2077 tree_view->priv->init_hadjust_value = TRUE;
2081 if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + allocation->width > tree_view->priv->width)
2082 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2084 gtk_adjustment_changed (tree_view->priv->hadjustment);
2086 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2087 gtk_adjustment_set_step_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.1);
2088 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.9);
2089 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
2090 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->vadjustment), tree_view->priv->height));
2092 gtk_adjustment_changed (tree_view->priv->vadjustment);
2094 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2095 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
2096 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2097 else if (gtk_adjustment_get_value (tree_view->priv->vadjustment) + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
2098 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2099 tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
2100 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2101 pspp_sheet_view_top_row_to_dy (tree_view);
2103 pspp_sheet_view_dy_to_top_row (tree_view);
2105 if (gtk_widget_get_realized (widget))
2107 gdk_window_move_resize (gtk_widget_get_window (widget),
2108 allocation->x, allocation->y,
2109 allocation->width, allocation->height);
2110 gdk_window_move_resize (tree_view->priv->header_window,
2111 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2113 MAX (tree_view->priv->width, allocation->width),
2114 tree_view->priv->header_height);
2115 gdk_window_move_resize (tree_view->priv->bin_window,
2116 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2117 TREE_VIEW_HEADER_HEIGHT (tree_view),
2118 MAX (tree_view->priv->width, allocation->width),
2119 allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2122 if (tree_view->priv->row_count == 0)
2123 invalidate_empty_focus (tree_view);
2125 if (gtk_widget_get_realized (widget))
2127 gboolean has_expand_column = FALSE;
2128 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2130 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2132 has_expand_column = TRUE;
2137 /* This little hack only works if we have an LTR locale, and no column has the */
2140 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2141 ! has_expand_column)
2142 invalidate_last_column (tree_view);
2144 gtk_widget_queue_draw (widget);
2146 update_childrens_allocation(tree_view);
2150 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2152 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2154 GtkWidget *widget = GTK_WIDGET (tree_view);
2156 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2157 gtk_widget_grab_focus (widget);
2158 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2162 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2165 return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2169 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2172 range_tower_set1 (tree_view->priv->selected, node, 1);
2176 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2179 range_tower_set0 (tree_view->priv->selected, node, 1);
2183 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2186 return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2190 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2193 return node > 0 ? node - 1 : -1;
2197 all_columns_selected (PsppSheetView *tree_view)
2201 for (list = tree_view->priv->columns; list; list = list->next)
2203 PsppSheetViewColumn *column = list->data;
2204 if (column->selectable && !column->selected)
2212 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2214 PsppSheetViewColumn *column,
2215 GdkEventButton *event)
2217 PsppSheetSelection *selection;
2218 PsppSheetSelectionMode mode;
2220 gboolean update_anchor;
2224 g_return_val_if_fail (tree_view != NULL, FALSE);
2225 g_return_val_if_fail (column != NULL, FALSE);
2227 selection = tree_view->priv->selection;
2228 mode = pspp_sheet_selection_get_mode (selection);
2229 if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2232 if (!column->row_head)
2237 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2238 if (event->type != GDK_BUTTON_PRESS
2239 || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2245 path = gtk_tree_path_new_from_indices (node, -1);
2248 pspp_sheet_selection_unselect_all (selection);
2249 pspp_sheet_selection_select_path (selection, path);
2250 pspp_sheet_selection_select_all_columns (selection);
2251 update_anchor = TRUE;
2254 else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2256 if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2257 || !all_columns_selected (tree_view))
2259 pspp_sheet_selection_unselect_all (selection);
2260 pspp_sheet_selection_select_path (selection, path);
2261 pspp_sheet_selection_select_all_columns (selection);
2262 update_anchor = TRUE;
2266 update_anchor = handled = FALSE;
2268 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2269 && modifiers == GDK_CONTROL_MASK)
2271 if (!all_columns_selected (tree_view))
2273 pspp_sheet_selection_unselect_all (selection);
2274 pspp_sheet_selection_select_all_columns (selection);
2277 if (pspp_sheet_selection_path_is_selected (selection, path))
2278 pspp_sheet_selection_unselect_path (selection, path);
2280 pspp_sheet_selection_select_path (selection, path);
2281 update_anchor = TRUE;
2284 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2285 && modifiers == GDK_SHIFT_MASK)
2287 GtkTreeRowReference *anchor = tree_view->priv->anchor;
2288 GtkTreePath *anchor_path;
2290 if (all_columns_selected (tree_view)
2291 && gtk_tree_row_reference_valid (anchor))
2293 update_anchor = FALSE;
2294 anchor_path = gtk_tree_row_reference_get_path (anchor);
2298 update_anchor = TRUE;
2299 anchor_path = gtk_tree_path_copy (path);
2302 pspp_sheet_selection_unselect_all (selection);
2303 pspp_sheet_selection_select_range (selection, anchor_path, path);
2304 pspp_sheet_selection_select_all_columns (selection);
2306 gtk_tree_path_free (anchor_path);
2311 update_anchor = handled = FALSE;
2315 if (tree_view->priv->anchor)
2316 gtk_tree_row_reference_free (tree_view->priv->anchor);
2317 tree_view->priv->anchor =
2318 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2319 tree_view->priv->model,
2323 gtk_tree_path_free (path);
2328 find_click (PsppSheetView *tree_view,
2331 PsppSheetViewColumn **column,
2332 GdkRectangle *background_area,
2333 GdkRectangle *cell_area)
2340 /* find the node that was clicked */
2341 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2344 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2349 background_area->y = y_offset + y;
2350 background_area->height = ROW_HEIGHT (tree_view);
2351 background_area->x = 0;
2353 /* Let the column have a chance at selecting it. */
2354 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2355 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2356 list; list = (rtl ? list->prev : list->next))
2358 PsppSheetViewColumn *candidate = list->data;
2360 if (!candidate->visible)
2363 background_area->width = candidate->width;
2364 if ((background_area->x > x) ||
2365 (background_area->x + background_area->width <= x))
2367 background_area->x += background_area->width;
2371 /* we found the focus column */
2373 pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2375 *column = candidate;
2383 pspp_sheet_view_button_press (GtkWidget *widget,
2384 GdkEventButton *event)
2386 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2388 PsppSheetViewColumn *column = NULL;
2390 GdkRectangle background_area;
2391 GdkRectangle cell_area;
2394 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2395 pspp_sheet_view_stop_editing (tree_view, FALSE);
2398 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2399 * we're done handling the button press.
2402 if (event->window == tree_view->priv->bin_window)
2407 gint pre_val, aft_val;
2408 PsppSheetViewColumn *column = NULL;
2409 GtkCellRenderer *focus_cell = NULL;
2410 gboolean row_double_click = FALSE;
2413 if (tree_view->priv->row_count == 0)
2415 grab_focus_and_unset_draw_keyfocus (tree_view);
2419 if (!find_click (tree_view, event->x, event->y, &node, &column,
2420 &background_area, &cell_area))
2422 grab_focus_and_unset_draw_keyfocus (tree_view);
2426 tree_view->priv->focus_column = column;
2428 if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2432 pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2434 path = _pspp_sheet_view_find_path (tree_view, node);
2436 /* we only handle selection modifications on the first button press
2438 if (event->type == GDK_BUTTON_PRESS)
2440 PsppSheetSelectionMode mode = 0;
2442 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2443 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
2444 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2445 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
2447 focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2449 pspp_sheet_view_column_focus_cell (column, focus_cell);
2451 if (event->state & GDK_CONTROL_MASK)
2453 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, mode);
2454 pspp_sheet_view_real_toggle_cursor_row (tree_view);
2456 else if (event->state & GDK_SHIFT_MASK)
2458 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
2459 pspp_sheet_view_real_select_cursor_row (tree_view, FALSE, mode);
2463 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
2466 if (tree_view->priv->anchor_column == NULL ||
2467 !(event->state & GDK_SHIFT_MASK))
2468 tree_view->priv->anchor_column = column;
2469 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2470 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2471 tree_view->priv->anchor_column,
2475 /* the treeview may have been scrolled because of _set_cursor,
2479 aft_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2480 dval = pre_val - aft_val;
2482 cell_area.y += dval;
2483 background_area.y += dval;
2485 /* Save press to possibly begin a drag
2487 if (!tree_view->priv->in_grab &&
2488 tree_view->priv->pressed_button < 0)
2490 tree_view->priv->pressed_button = event->button;
2491 tree_view->priv->press_start_x = event->x;
2492 tree_view->priv->press_start_y = event->y;
2493 tree_view->priv->press_start_node = node;
2495 if (tree_view->priv->rubber_banding_enable
2496 && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2497 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2499 tree_view->priv->press_start_y += tree_view->priv->dy;
2500 tree_view->priv->rubber_band_x = event->x;
2501 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2502 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2504 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2505 tree_view->priv->rubber_band_ctrl = TRUE;
2506 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2507 tree_view->priv->rubber_band_shift = TRUE;
2512 /* Test if a double click happened on the same row. */
2513 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2515 int double_click_time, double_click_distance;
2517 g_object_get (gtk_settings_get_for_screen (
2518 gtk_widget_get_screen (widget)),
2519 "gtk-double-click-time", &double_click_time,
2520 "gtk-double-click-distance", &double_click_distance,
2523 /* Same conditions as _gdk_event_button_generate */
2524 if (tree_view->priv->last_button_x != -1 &&
2525 (event->time < tree_view->priv->last_button_time + double_click_time) &&
2526 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2527 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2529 /* We do no longer compare paths of this row and the
2530 * row clicked previously. We use the double click
2531 * distance to decide whether this is a valid click,
2532 * allowing the mouse to slightly move over another row.
2534 row_double_click = TRUE;
2536 tree_view->priv->last_button_time = 0;
2537 tree_view->priv->last_button_x = -1;
2538 tree_view->priv->last_button_y = -1;
2542 tree_view->priv->last_button_time = event->time;
2543 tree_view->priv->last_button_x = event->x;
2544 tree_view->priv->last_button_y = event->y;
2548 if (row_double_click)
2550 gtk_grab_remove (widget);
2551 pspp_sheet_view_row_activated (tree_view, path, column);
2553 if (tree_view->priv->pressed_button == event->button)
2554 tree_view->priv->pressed_button = -1;
2557 gtk_tree_path_free (path);
2559 /* If we activated the row through a double click we don't want to grab
2560 * focus back, as moving focus to another widget is pretty common.
2562 if (!row_double_click)
2563 grab_focus_and_unset_draw_keyfocus (tree_view);
2568 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2570 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2572 column = list->data;
2573 if (event->window == column->window &&
2574 column->resizable &&
2579 if (gdk_pointer_grab (column->window, FALSE,
2580 GDK_POINTER_MOTION_HINT_MASK |
2581 GDK_BUTTON1_MOTION_MASK |
2582 GDK_BUTTON_RELEASE_MASK,
2583 NULL, NULL, event->time))
2586 gtk_grab_add (widget);
2587 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2588 column->resized_width = column->width;
2590 /* block attached dnd signal handler */
2591 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2593 g_signal_handlers_block_matched (widget,
2594 G_SIGNAL_MATCH_DATA,
2598 tree_view->priv->drag_pos = i;
2599 tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2601 if (!gtk_widget_has_focus (widget))
2602 gtk_widget_grab_focus (widget);
2610 /* GtkWidget::button_release_event helper */
2612 pspp_sheet_view_button_release_drag_column (GtkWidget *widget,
2613 GdkEventButton *event)
2615 PsppSheetView *tree_view;
2619 tree_view = PSPP_SHEET_VIEW (widget);
2621 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2622 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2623 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2625 /* Move the button back */
2626 g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2628 g_object_ref (tree_view->priv->drag_column->button);
2629 gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2630 gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2631 gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2632 g_object_unref (tree_view->priv->drag_column->button);
2633 gtk_widget_queue_resize (widget);
2634 if (tree_view->priv->drag_column->resizable)
2636 gdk_window_raise (tree_view->priv->drag_column->window);
2637 gdk_window_show (tree_view->priv->drag_column->window);
2640 gdk_window_hide (tree_view->priv->drag_column->window);
2642 gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2646 if (tree_view->priv->cur_reorder &&
2647 tree_view->priv->cur_reorder->right_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->right_column);
2653 if (tree_view->priv->cur_reorder &&
2654 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2655 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2656 tree_view->priv->cur_reorder->left_column);
2658 tree_view->priv->drag_column = NULL;
2659 gdk_window_hide (tree_view->priv->drag_window);
2661 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2662 g_slice_free (PsppSheetViewColumnReorder, l->data);
2663 g_list_free (tree_view->priv->column_drag_info);
2664 tree_view->priv->column_drag_info = NULL;
2665 tree_view->priv->cur_reorder = NULL;
2667 if (tree_view->priv->drag_highlight_window)
2668 gdk_window_hide (tree_view->priv->drag_highlight_window);
2670 /* Reset our flags */
2671 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2672 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2677 /* GtkWidget::button_release_event helper */
2679 pspp_sheet_view_button_release_column_resize (GtkWidget *widget,
2680 GdkEventButton *event)
2682 PsppSheetView *tree_view;
2685 tree_view = PSPP_SHEET_VIEW (widget);
2687 tree_view->priv->drag_pos = -1;
2689 /* unblock attached dnd signal handler */
2690 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2692 g_signal_handlers_unblock_matched (widget,
2693 G_SIGNAL_MATCH_DATA,
2697 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2698 gtk_grab_remove (widget);
2699 gdk_display_pointer_ungrab (gdk_window_get_display (event->window),
2705 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2706 GdkEventButton *event)
2708 GtkCellEditable *cell_editable;
2713 PsppSheetViewColumn *column;
2714 GdkRectangle background_area;
2715 GdkRectangle cell_area;
2721 if (event->window != tree_view->priv->bin_window)
2724 /* Ignore a released button, if that button wasn't depressed */
2725 if (tree_view->priv->pressed_button != event->button)
2728 if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2732 /* decide if we edit */
2733 path = _pspp_sheet_view_find_path (tree_view, node);
2734 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2735 if (event->button != 1 || modifiers)
2738 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2739 pspp_sheet_view_column_cell_set_cell_data (column,
2740 tree_view->priv->model,
2743 if (!pspp_sheet_view_column_get_quick_edit (column)
2744 && _pspp_sheet_view_column_has_editable_cell (column))
2747 flags = 0; /* FIXME: get the right flags */
2748 path_string = gtk_tree_path_to_string (path);
2750 if (!_pspp_sheet_view_column_cell_event (column,
2758 if (cell_editable == NULL)
2761 pspp_sheet_view_real_set_cursor (tree_view, path,
2762 TRUE, TRUE, 0); /* XXX mode? */
2763 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2766 _pspp_sheet_view_column_get_neighbor_sizes (
2767 column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2770 area.width -= right + left;
2772 pspp_sheet_view_real_start_editing (tree_view,
2779 g_free (path_string);
2780 gtk_tree_path_free (path);
2785 pspp_sheet_view_button_release (GtkWidget *widget,
2786 GdkEventButton *event)
2788 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2790 pspp_sheet_view_stop_editing (tree_view, FALSE);
2791 if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2792 && pspp_sheet_view_button_release_edit (tree_view, event))
2794 if (tree_view->priv->pressed_button == event->button)
2795 tree_view->priv->pressed_button = -1;
2797 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2801 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2802 return pspp_sheet_view_button_release_drag_column (widget, event);
2804 if (tree_view->priv->rubber_band_status)
2805 pspp_sheet_view_stop_rubber_band (tree_view);
2807 if (tree_view->priv->pressed_button == event->button)
2808 tree_view->priv->pressed_button = -1;
2810 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2811 return pspp_sheet_view_button_release_column_resize (widget, event);
2817 pspp_sheet_view_grab_broken (GtkWidget *widget,
2818 GdkEventGrabBroken *event)
2820 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2822 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2823 pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2825 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2826 pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2831 /* GtkWidget::motion_event function set.
2835 do_prelight (PsppSheetView *tree_view,
2837 /* these are in bin_window coords */
2841 int prev_node = tree_view->priv->prelight_node;
2843 if (prev_node != node)
2845 tree_view->priv->prelight_node = node;
2848 _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2851 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2857 prelight_or_select (PsppSheetView *tree_view,
2859 /* these are in bin_window coords */
2863 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2865 if (tree_view->priv->hover_selection &&
2866 (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2867 !(tree_view->priv->edited_column &&
2868 tree_view->priv->edited_column->editable_widget))
2872 if (!pspp_sheet_view_node_is_selected (tree_view, node))
2876 path = _pspp_sheet_view_find_path (tree_view, node);
2877 pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2878 if (pspp_sheet_view_node_is_selected (tree_view, node))
2880 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2881 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE, 0); /* XXX mode? */
2883 gtk_tree_path_free (path);
2887 else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2888 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2891 do_prelight (tree_view, node, x, y);
2895 ensure_unprelighted (PsppSheetView *tree_view)
2897 do_prelight (tree_view,
2899 -1000, -1000); /* coords not possibly over an arrow */
2901 g_assert (tree_view->priv->prelight_node < 0);
2905 update_prelight (PsppSheetView *tree_view,
2912 if (tree_view->priv->row_count == 0)
2917 ensure_unprelighted (tree_view);
2921 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2925 pspp_sheet_view_find_offset (tree_view, new_y, &node);
2928 prelight_or_select (tree_view, node, x, y);
2934 /* Our motion arrow is either a box (in the case of the original spot)
2935 * or an arrow. It is expander_size wide.
2958 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2961 PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2962 GtkWidget *widget = GTK_WIDGET (tree_view);
2963 GdkBitmap *mask = NULL;
2968 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2969 GdkWindowAttr attributes;
2970 guint attributes_mask;
2973 reorder->left_column == tree_view->priv->drag_column ||
2974 reorder->right_column == tree_view->priv->drag_column)
2975 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2976 else if (reorder->left_column || reorder->right_column)
2978 GdkRectangle visible_rect;
2979 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2980 if (reorder->left_column)
2981 x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2983 x = reorder->right_column->allocation.x;
2985 if (x < visible_rect.x)
2986 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2987 else if (x > visible_rect.x + visible_rect.width)
2988 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2990 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2993 /* We want to draw the rectangle over the initial location. */
2994 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2999 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
3001 if (tree_view->priv->drag_highlight_window)
3003 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3005 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3008 attributes.window_type = GDK_WINDOW_CHILD;
3009 attributes.wclass = GDK_INPUT_OUTPUT;
3010 attributes.x = tree_view->priv->drag_column_x;
3012 width = attributes.width = tree_view->priv->drag_column->allocation.width;
3013 height = attributes.height = tree_view->priv->drag_column->allocation.height;
3014 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3015 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3016 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3017 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3018 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
3019 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3021 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3022 gc = gdk_gc_new (mask);
3024 gdk_gc_set_foreground (gc, &col);
3025 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3027 gdk_gc_set_foreground(gc, &col);
3028 gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
3029 g_object_unref (gc);
3031 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3033 if (mask) g_object_unref (mask);
3034 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3037 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
3043 width = tree_view->priv->expander_size;
3045 /* Get x, y, width, height of arrow */
3046 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
3047 if (reorder->left_column)
3049 x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
3050 height = reorder->left_column->allocation.height;
3054 x += reorder->right_column->allocation.x - width/2;
3055 height = reorder->right_column->allocation.height;
3057 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3058 height += tree_view->priv->expander_size;
3060 /* Create the new window */
3061 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3063 if (tree_view->priv->drag_highlight_window)
3065 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3067 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3070 attributes.window_type = GDK_WINDOW_TEMP;
3071 attributes.wclass = GDK_INPUT_OUTPUT;
3072 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3073 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3074 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3075 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3078 attributes.width = width;
3079 attributes.height = height;
3080 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3081 &attributes, attributes_mask);
3082 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3084 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3085 gc = gdk_gc_new (mask);
3087 gdk_gc_set_foreground (gc, &col);
3088 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3090 /* Draw the 2 arrows as per above */
3092 gdk_gc_set_foreground (gc, &col);
3093 for (i = 0; i < width; i ++)
3095 if (i == (width/2 - 1))
3097 gdk_draw_line (mask, gc, i, j, i, height - j);
3098 if (i < (width/2 - 1))
3103 g_object_unref (gc);
3104 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3106 if (mask) g_object_unref (mask);
3109 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3110 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3112 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3113 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3119 width = tree_view->priv->expander_size;
3121 /* Get x, y, width, height of arrow */
3122 width = width/2; /* remember, the arrow only takes half the available width */
3123 gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
3124 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3125 x += widget->allocation.width - width;
3127 if (reorder->left_column)
3128 height = reorder->left_column->allocation.height;
3130 height = reorder->right_column->allocation.height;
3132 y -= tree_view->priv->expander_size;
3133 height += 2*tree_view->priv->expander_size;
3135 /* Create the new window */
3136 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3137 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3139 if (tree_view->priv->drag_highlight_window)
3141 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3143 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3146 attributes.window_type = GDK_WINDOW_TEMP;
3147 attributes.wclass = GDK_INPUT_OUTPUT;
3148 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3149 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3150 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3151 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3154 attributes.width = width;
3155 attributes.height = height;
3156 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3157 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3159 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3160 gc = gdk_gc_new (mask);
3162 gdk_gc_set_foreground (gc, &col);
3163 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3165 /* Draw the 2 arrows as per above */
3167 gdk_gc_set_foreground (gc, &col);
3168 j = tree_view->priv->expander_size;
3169 for (i = 0; i < width; i ++)
3172 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3176 gdk_draw_line (mask, gc, k, j, k, height - j);
3177 gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3178 gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3181 g_object_unref (gc);
3182 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3184 if (mask) g_object_unref (mask);
3187 tree_view->priv->drag_column_window_state = arrow_type;
3188 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3192 g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3193 gdk_window_hide (tree_view->priv->drag_highlight_window);
3197 gdk_window_show (tree_view->priv->drag_highlight_window);
3198 gdk_window_raise (tree_view->priv->drag_highlight_window);
3203 pspp_sheet_view_motion_resize_column (GtkWidget *widget,
3204 GdkEventMotion *event)
3208 PsppSheetViewColumn *column;
3209 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3211 column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3213 if (event->is_hint || event->window != gtk_widget_get_window (widget))
3214 gtk_widget_get_pointer (widget, &x, NULL);
3218 if (tree_view->priv->hadjustment)
3219 x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
3221 new_width = pspp_sheet_view_new_column_width (tree_view,
3222 tree_view->priv->drag_pos, &x);
3223 if (x != tree_view->priv->x_drag &&
3224 (new_width != column->fixed_width))
3226 column->use_resized_width = TRUE;
3227 column->resized_width = new_width;
3230 column->resized_width -= tree_view->priv->last_extra_space_per_column;
3232 gtk_widget_queue_resize (widget);
3240 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3242 PsppSheetViewColumnReorder *reorder = NULL;
3246 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3247 for (list = tree_view->priv->column_drag_info; list; list = list->next)
3249 reorder = (PsppSheetViewColumnReorder *) list->data;
3250 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3255 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3258 tree_view->priv->cur_reorder = reorder;
3259 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3263 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3265 GdkRectangle visible_rect;
3270 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3271 y += tree_view->priv->dy;
3273 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3275 /* see if we are near the edge. */
3276 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3279 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3284 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->vadjustment) + offset, 0.0,
3285 gtk_adjustment_get_upper (tree_view->priv->vadjustment) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
3286 gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3290 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3292 GdkRectangle visible_rect;
3297 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3299 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3301 /* See if we are near the edge. */
3302 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3305 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3311 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) + offset,
3312 0.0, gtk_adjustment_get_upper (tree_view->priv->hadjustment) - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
3313 gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3320 pspp_sheet_view_motion_drag_column (GtkWidget *widget,
3321 GdkEventMotion *event)
3323 PsppSheetView *tree_view = (PsppSheetView *) widget;
3324 PsppSheetViewColumn *column = tree_view->priv->drag_column;
3326 GtkAllocation allocation;
3329 if ((column == NULL) ||
3330 (event->window != tree_view->priv->drag_window))
3333 /* Handle moving the header */
3334 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3335 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
3336 x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3337 MAX (tree_view->priv->width, allocation.width) - column->allocation.width);
3338 gdk_window_move (tree_view->priv->drag_window, x, y);
3340 /* autoscroll, if needed */
3341 pspp_sheet_view_horizontal_autoscroll (tree_view);
3342 /* Update the current reorder position and arrow; */
3343 pspp_sheet_view_update_current_reorder (tree_view);
3349 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3351 remove_scroll_timeout (tree_view);
3352 gtk_grab_remove (GTK_WIDGET (tree_view));
3354 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3356 GtkTreePath *tmp_path;
3358 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3360 /* The anchor path should be set to the start path */
3361 tmp_path = _pspp_sheet_view_find_path (tree_view,
3362 tree_view->priv->rubber_band_start_node);
3364 if (tree_view->priv->anchor)
3365 gtk_tree_row_reference_free (tree_view->priv->anchor);
3367 tree_view->priv->anchor =
3368 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3369 tree_view->priv->model,
3372 gtk_tree_path_free (tmp_path);
3374 /* ... and the cursor to the end path */
3375 tmp_path = _pspp_sheet_view_find_path (tree_view,
3376 tree_view->priv->rubber_band_end_node);
3377 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE, 0); /* XXX mode? */
3378 gtk_tree_path_free (tmp_path);
3380 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3383 /* Clear status variables */
3384 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3385 tree_view->priv->rubber_band_shift = 0;
3386 tree_view->priv->rubber_band_ctrl = 0;
3388 tree_view->priv->rubber_band_start_node = -1;
3389 tree_view->priv->rubber_band_end_node = -1;
3393 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3397 gboolean skip_start,
3400 if (start_node == end_node)
3403 /* We skip the first node and jump inside the loop */
3409 /* Small optimization by assuming insensitive nodes are never
3414 if (tree_view->priv->rubber_band_shift)
3415 pspp_sheet_view_node_select (tree_view, start_node);
3416 else if (tree_view->priv->rubber_band_ctrl)
3418 /* Toggle the selection state */
3419 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3420 pspp_sheet_view_node_unselect (tree_view, start_node);
3422 pspp_sheet_view_node_select (tree_view, start_node);
3425 pspp_sheet_view_node_select (tree_view, start_node);
3429 /* Mirror the above */
3430 if (tree_view->priv->rubber_band_shift)
3431 pspp_sheet_view_node_unselect (tree_view, start_node);
3432 else if (tree_view->priv->rubber_band_ctrl)
3434 /* Toggle the selection state */
3435 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3436 pspp_sheet_view_node_unselect (tree_view, start_node);
3438 pspp_sheet_view_node_select (tree_view, start_node);
3441 pspp_sheet_view_node_unselect (tree_view, start_node);
3444 _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3446 if (start_node == end_node)
3451 start_node = pspp_sheet_view_node_next (tree_view, start_node);
3454 /* Ran out of tree */
3457 if (skip_end && start_node == end_node)
3464 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3467 return node * tree_view->priv->fixed_height;
3471 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3475 int fixed_height = tree_view->priv->fixed_height;
3476 if (fixed_height <= 0
3478 || height >= tree_view->priv->row_count * fixed_height)
3485 *new_node = height / fixed_height;
3486 return height % fixed_height;
3491 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3496 pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3497 pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3499 /* Handle the start area first */
3500 if (tree_view->priv->rubber_band_start_node < 0)
3502 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3509 else if (start_node < tree_view->priv->rubber_band_start_node)
3511 /* New node is above the old one; selection became bigger */
3512 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3514 tree_view->priv->rubber_band_start_node,
3519 else if (start_node > tree_view->priv->rubber_band_start_node)
3521 /* New node is below the old one; selection became smaller */
3522 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3523 tree_view->priv->rubber_band_start_node,
3530 tree_view->priv->rubber_band_start_node = start_node;
3532 /* Next, handle the end area */
3533 if (tree_view->priv->rubber_band_end_node < 0)
3535 /* In the event this happens, start_node was also -1; this case is
3539 else if (end_node < 0)
3541 /* Find the last node in the tree */
3542 pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3545 /* Selection reached end of the tree */
3546 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3547 tree_view->priv->rubber_band_end_node,
3553 else if (end_node > tree_view->priv->rubber_band_end_node)
3555 /* New node is below the old one; selection became bigger */
3556 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3557 tree_view->priv->rubber_band_end_node,
3563 else if (end_node < tree_view->priv->rubber_band_end_node)
3565 /* New node is above the old one; selection became smaller */
3566 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3568 tree_view->priv->rubber_band_end_node,
3574 tree_view->priv->rubber_band_end_node = end_node;
3577 #define GDK_RECTANGLE_PTR(X) ((GdkRectangle *)(X))
3580 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3583 cairo_rectangle_int_t old_area;
3584 cairo_rectangle_int_t new_area;
3585 cairo_rectangle_int_t common;
3586 cairo_region_t *invalid_region;
3587 PsppSheetViewColumn *column;
3589 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3590 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3591 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3592 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3594 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3597 y = MAX (y, 0) + tree_view->priv->dy;
3599 new_area.x = MIN (tree_view->priv->press_start_x, x);
3600 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3601 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3602 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3604 invalid_region = cairo_region_create_rectangle (&old_area);
3605 cairo_region_union_rectangle (invalid_region, &new_area);
3607 gdk_rectangle_intersect (GDK_RECTANGLE_PTR (&old_area),
3608 GDK_RECTANGLE_PTR (&new_area), GDK_RECTANGLE_PTR (&common));
3609 if (common.width > 2 && common.height > 2)
3611 cairo_region_t *common_region;
3613 /* make sure the border is invalidated */
3619 common_region = cairo_region_create_rectangle (&common);
3621 cairo_region_subtract (invalid_region, common_region);
3622 cairo_region_destroy (common_region);
3625 #if GTK_MAJOR_VERSION == 3
3626 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
3629 cairo_rectangle_int_t extents;
3631 cairo_region_get_extents (invalid_region, &extents);
3632 ereg = gdk_region_rectangle (GDK_RECTANGLE_PTR (&extents));
3633 gdk_window_invalidate_region (tree_view->priv->bin_window, ereg, TRUE);
3634 gdk_region_destroy (ereg);
3638 cairo_region_destroy (invalid_region);
3640 tree_view->priv->rubber_band_x = x;
3641 tree_view->priv->rubber_band_y = y;
3642 pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3644 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3645 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3646 tree_view->priv->anchor_column,
3649 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3651 pspp_sheet_view_update_rubber_band_selection (tree_view);
3656 pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view,
3661 GdkRectangle rubber_rect;
3665 rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3666 rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3667 rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3668 rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3670 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3673 cr = gdk_cairo_create (tree_view->priv->bin_window);
3674 cairo_set_line_width (cr, 1.0);
3676 style = gtk_widget_get_style (GTK_WIDGET (tree_view));
3677 cairo_set_source_rgba (cr,
3678 style->fg[GTK_STATE_NORMAL].red / 65535.,
3679 style->fg[GTK_STATE_NORMAL].green / 65535.,
3680 style->fg[GTK_STATE_NORMAL].blue / 65535.,
3683 gdk_cairo_rectangle (cr, &rect);
3687 cairo_set_source_rgb (cr,
3688 style->fg[GTK_STATE_NORMAL].red / 65535.,
3689 style->fg[GTK_STATE_NORMAL].green / 65535.,
3690 style->fg[GTK_STATE_NORMAL].blue / 65535.);
3692 cairo_rectangle (cr,
3693 rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3694 rubber_rect.width - 1, rubber_rect.height - 1);
3703 pspp_sheet_view_motion_bin_window (GtkWidget *widget,
3704 GdkEventMotion *event)
3706 PsppSheetView *tree_view;
3710 tree_view = (PsppSheetView *) widget;
3712 if (tree_view->priv->row_count == 0)
3715 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3717 GdkRectangle background_area, cell_area;
3718 PsppSheetViewColumn *column;
3720 if (find_click (tree_view, event->x, event->y, &node, &column,
3721 &background_area, &cell_area)
3722 && tree_view->priv->focus_column == column
3723 && tree_view->priv->press_start_node == node)
3726 gtk_grab_add (GTK_WIDGET (tree_view));
3727 pspp_sheet_view_update_rubber_band (tree_view);
3729 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3731 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3733 pspp_sheet_view_update_rubber_band (tree_view);
3735 add_scroll_timeout (tree_view);
3738 /* only check for an initiated drag when a button is pressed */
3739 if (tree_view->priv->pressed_button >= 0
3740 && !tree_view->priv->rubber_band_status)
3741 pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3743 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3747 pspp_sheet_view_find_offset (tree_view, new_y, &node);
3749 tree_view->priv->event_last_x = event->x;
3750 tree_view->priv->event_last_y = event->y;
3752 prelight_or_select (tree_view, node, event->x, event->y);
3758 pspp_sheet_view_motion (GtkWidget *widget,
3759 GdkEventMotion *event)
3761 PsppSheetView *tree_view;
3763 tree_view = (PsppSheetView *) widget;
3765 /* Resizing a column */
3766 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3767 return pspp_sheet_view_motion_resize_column (widget, event);
3770 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3771 return pspp_sheet_view_motion_drag_column (widget, event);
3773 /* Sanity check it */
3774 if (event->window == tree_view->priv->bin_window)
3775 return pspp_sheet_view_motion_bin_window (widget, event);
3780 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3781 * the tree is empty.
3784 invalidate_empty_focus (PsppSheetView *tree_view)
3788 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3793 area.width = gdk_window_get_width (tree_view->priv->bin_window);
3794 area.height = gdk_window_get_height (tree_view->priv->bin_window);
3795 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3798 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3802 draw_empty_focus (PsppSheetView *tree_view)
3804 GtkWidget *widget = GTK_WIDGET (tree_view);
3806 cairo_t *cr = gdk_cairo_create (tree_view->priv->bin_window);
3808 if (!gtk_widget_has_focus (widget))
3811 w = gdk_window_get_width (tree_view->priv->bin_window);
3812 h = gdk_window_get_height (tree_view->priv->bin_window);
3818 gtk_paint_focus (gtk_widget_get_style (widget),
3820 gtk_widget_get_state (widget),
3828 pspp_sheet_view_draw_vertical_grid_lines (PsppSheetView *tree_view,
3830 gint n_visible_columns,
3834 GList *list = tree_view->priv->columns;
3838 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3839 && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3842 /* Only draw the lines for visible rows and columns */
3843 for (list = tree_view->priv->columns; list; list = list->next, i++)
3845 PsppSheetViewColumn *column = list->data;
3848 if (! column->visible)
3851 current_x += column->width;
3853 /* Generally the grid lines should fit within the column, but for the
3854 last visible column we put it just past the end of the column.
3855 (Otherwise horizontal grid lines sometimes stick out by one pixel.) */
3857 if (i != n_visible_columns - 1)
3860 cairo_set_line_width (cr, 1.0);
3861 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
3862 cairo_move_to (cr, x + 0.5, min_y);
3863 cairo_line_to (cr, x + 0.5, max_y - min_y);
3868 /* Warning: Very scary function.
3869 * Modify at your own risk
3871 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3872 * FIXME: It's not...
3875 pspp_sheet_view_draw_bin (GtkWidget *widget,
3878 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3883 int drag_highlight = -1;
3886 gint y_offset, cell_offset;
3888 GdkRectangle background_area;
3889 GdkRectangle cell_area;
3891 gint bin_window_width;
3892 gint bin_window_height;
3893 GtkTreePath *cursor_path;
3894 GtkTreePath *drag_dest_path;
3895 GList *first_column, *last_column;
3896 gint vertical_separator;
3897 gint horizontal_separator;
3898 gint focus_line_width;
3899 gboolean allow_rules;
3900 gboolean has_special_cell;
3902 gint n_visible_columns;
3903 gint grid_line_width;
3904 gboolean row_ending_details;
3905 gboolean draw_vgrid_lines, draw_hgrid_lines;
3907 GtkStyleContext *context;
3908 context = gtk_widget_get_style_context (widget);
3911 GtkAllocation allocation;
3912 gtk_widget_get_allocation (widget, &allocation);
3914 GdkRectangle exposed_rect;
3915 gdk_cairo_get_clip_rectangle (cr, &exposed_rect);
3919 Zarea.height = allocation.height;
3921 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3923 gtk_widget_style_get (widget,
3924 "horizontal-separator", &horizontal_separator,
3925 "vertical-separator", &vertical_separator,
3926 "allow-rules", &allow_rules,
3927 "focus-line-width", &focus_line_width,
3928 "row-ending-details", &row_ending_details,
3931 if (tree_view->priv->row_count == 0)
3933 draw_empty_focus (tree_view);
3938 /* clip event->area to the visible area */
3939 if (Zarea.height < 0.5)
3943 validate_visible_area (tree_view);
3945 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, Zarea.y);
3949 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3951 gdk_window_get_width (tree_view->priv->bin_window);
3954 gdk_window_get_height (tree_view->priv->bin_window);
3957 if (tree_view->priv->height < bin_window_height)
3959 gtk_paint_flat_box (gtk_widget_get_style (widget),
3961 gtk_widget_get_state (widget),
3965 0, tree_view->priv->height,
3967 bin_window_height - tree_view->priv->height);
3973 /* find the path for the node */
3974 path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3975 gtk_tree_model_get_iter (tree_view->priv->model,
3978 gtk_tree_path_free (path);
3981 drag_dest_path = NULL;
3983 if (tree_view->priv->cursor)
3984 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3987 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3989 if (tree_view->priv->drag_dest_row)
3990 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3993 _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3997 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3998 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
4000 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
4001 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
4003 if (draw_vgrid_lines || draw_hgrid_lines)
4004 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
4006 n_visible_columns = 0;
4007 for (list = tree_view->priv->columns; list; list = list->next)
4009 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4011 n_visible_columns ++;
4014 /* Find the last column */
4015 for (last_column = g_list_last (tree_view->priv->columns);
4016 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
4017 last_column = last_column->prev)
4021 for (first_column = g_list_first (tree_view->priv->columns);
4022 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
4023 first_column = first_column->next)
4026 /* Actually process the expose event. To do this, we want to
4027 * start at the first node of the event, and walk the tree in
4028 * order, drawing each successive node.
4035 gboolean is_first = FALSE;
4036 gboolean is_last = FALSE;
4037 gboolean done = FALSE;
4040 max_height = ROW_HEIGHT (tree_view);
4044 background_area.y = y_offset + Zarea.y;
4045 background_area.height = max_height;
4046 max_y = background_area.y + max_height;
4050 if (node == tree_view->priv->prelight_node)
4051 flags |= GTK_CELL_RENDERER_PRELIT;
4053 selected = pspp_sheet_view_node_is_selected (tree_view, node);
4057 if (tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
4059 /* we *need* to set cell data on all cells before the call
4060 * to _has_special_cell, else _has_special_cell() does not
4061 * return a correct value.
4063 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4065 list = (rtl ? list->prev : list->next))
4067 PsppSheetViewColumn *column = list->data;
4068 pspp_sheet_view_column_cell_set_cell_data (column,
4069 tree_view->priv->model,
4073 has_special_cell = pspp_sheet_view_has_special_cell (tree_view);
4076 has_special_cell = tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
4078 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4080 list = (rtl ? list->prev : list->next))
4082 PsppSheetViewColumn *column = list->data;
4083 const gchar *detail = NULL;
4084 gboolean selected_column;
4087 if (!column->visible)
4090 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
4091 selected_column = column->selected && column->selectable;
4093 selected_column = TRUE;
4096 if (cell_offset > Zarea.x + Zarea.width ||
4097 cell_offset + column->width < Zarea.x)
4099 cell_offset += column->width;
4104 if (selected && selected_column)
4105 flags |= GTK_CELL_RENDERER_SELECTED;
4107 flags &= ~GTK_CELL_RENDERER_SELECTED;
4109 if (column->show_sort_indicator)
4110 flags |= GTK_CELL_RENDERER_SORTED;
4112 flags &= ~GTK_CELL_RENDERER_SORTED;
4115 flags |= GTK_CELL_RENDERER_FOCUSED;
4117 flags &= ~GTK_CELL_RENDERER_FOCUSED;
4119 background_area.x = cell_offset;
4120 background_area.width = column->width;
4122 cell_area = background_area;
4123 cell_area.y += vertical_separator / 2;
4124 cell_area.x += horizontal_separator / 2;
4125 cell_area.height -= vertical_separator;
4126 cell_area.width -= horizontal_separator;
4128 if (draw_vgrid_lines)
4130 if (list == first_column)
4132 cell_area.width -= grid_line_width / 2;
4134 else if (list == last_column)
4136 cell_area.x += grid_line_width / 2;
4137 cell_area.width -= grid_line_width / 2;
4141 cell_area.x += grid_line_width / 2;
4142 cell_area.width -= grid_line_width;
4146 if (draw_hgrid_lines)
4148 cell_area.y += grid_line_width / 2;
4149 cell_area.height -= grid_line_width;
4153 if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4155 if (!gdk_rectangle_intersect (&background_area, &exposed_rect, NULL))
4158 cell_offset += column->width;
4163 pspp_sheet_view_column_cell_set_cell_data (column,
4164 tree_view->priv->model,
4167 /* Select the detail for drawing the cell. relevant
4168 * factors are parity, sortedness, and whether to
4171 if (allow_rules && tree_view->priv->has_rules)
4173 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4174 n_visible_columns >= 3)
4177 detail = "cell_odd_ruled_sorted";
4179 detail = "cell_even_ruled_sorted";
4184 detail = "cell_odd_ruled";
4186 detail = "cell_even_ruled";
4191 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4192 n_visible_columns >= 3)
4195 detail = "cell_odd_sorted";
4197 detail = "cell_even_sorted";
4202 detail = "cell_odd";
4204 detail = "cell_even";
4210 gtk_style_context_save (context);
4211 state = gtk_cell_renderer_get_state (NULL, widget, flags);
4212 gtk_style_context_set_state (context, state);
4213 gtk_style_context_add_class (context, GTK_STYLE_CLASS_CELL);
4215 /* Draw background */
4216 gtk_render_background (context, cr,
4219 background_area.width,
4220 background_area.height);
4223 gtk_render_frame (context, cr,
4226 background_area.width,
4227 background_area.height);
4229 if (draw_hgrid_lines)
4231 cairo_set_line_width (cr, 1.0);
4232 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
4234 if (background_area.y >= 0)
4237 gdk_draw_line (event->window,
4238 tree_view->priv->grid_line_gc[widget->state],
4239 background_area.x, background_area.y,
4240 background_area.x + background_area.width,
4243 cairo_move_to (cr, background_area.x, background_area.y - 0.5);
4244 cairo_line_to (cr, background_area.x + background_area.width,
4245 background_area.y - 0.5);
4249 if (y_offset + max_height <= Zarea.height - 0.5)
4252 gdk_draw_line (event->window,
4253 tree_view->priv->grid_line_gc[widget->state],
4254 background_area.x, background_area.y + max_height,
4255 background_area.x + background_area.width,
4256 background_area.y + max_height);
4259 cairo_move_to (cr, background_area.x, background_area.y + max_height - 0.5);
4260 cairo_line_to (cr, background_area.x + background_area.width,
4261 background_area.y + max_height - 0.5);
4267 _pspp_sheet_view_column_cell_render (column,
4274 cell_offset += column->width;
4275 gtk_style_context_restore (context);
4278 if (node == drag_highlight)
4280 /* Draw indicator for the drop
4282 gint highlight_y = -1;
4286 switch (tree_view->priv->drag_dest_pos)
4288 case PSPP_SHEET_VIEW_DROP_BEFORE:
4289 highlight_y = background_area.y - 1;
4290 if (highlight_y < 0)
4294 case PSPP_SHEET_VIEW_DROP_AFTER:
4295 highlight_y = background_area.y + background_area.height - 1;
4298 case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE:
4299 case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER:
4300 _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node);
4304 width = gdk_window_get_width (tree_view->priv->bin_window);
4306 if (row_ending_details)
4307 gtk_paint_focus (gtk_widget_get_style (widget),
4309 gtk_widget_get_state (widget),
4312 ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4313 : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4314 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4315 - focus_line_width / 2,
4316 width, ROW_HEIGHT (tree_view)
4317 - focus_line_width + 1);
4319 gtk_paint_focus (gtk_widget_get_style (widget),
4321 gtk_widget_get_state (widget),
4323 "treeview-drop-indicator",
4324 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4325 - focus_line_width / 2,
4326 width, ROW_HEIGHT (tree_view)
4327 - focus_line_width + 1);
4332 if (highlight_y >= 0)
4334 gdk_draw_line (event->window,
4335 widget->style->fg_gc[gtk_widget_get_state (widget)],
4338 rtl ? 0 : bin_window_width,
4344 y_offset += max_height;
4348 node = pspp_sheet_view_node_next (tree_view, node);
4351 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
4355 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
4362 while (y_offset < Zarea.height);
4365 pspp_sheet_view_draw_vertical_grid_lines (tree_view, cr, n_visible_columns,
4369 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4371 GdkRectangle *rectangles;
4374 gdk_region_get_rectangles (event->region,
4378 while (n_rectangles--)
4379 pspp_sheet_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
4381 g_free (rectangles);
4386 gtk_tree_path_free (cursor_path);
4389 gtk_tree_path_free (drag_dest_path);
4396 pspp_sheet_view_draw (GtkWidget *widget,
4399 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4400 GtkStyleContext *context;
4402 context = gtk_widget_get_style_context (widget);
4404 if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window))
4409 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->bin_window);
4410 pspp_sheet_view_draw_bin (widget, cr);
4413 /* We can't just chain up to Container::expose as it will try to send the
4414 * event to the headers, so we handle propagating it to our children
4415 * (eg. widgets being edited) ourselves.
4417 tmp_list = tree_view->priv->children;
4420 PsppSheetViewChild *child = tmp_list->data;
4421 tmp_list = tmp_list->next;
4423 gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
4428 gtk_render_background (context, cr,
4430 gtk_widget_get_allocated_width (widget),
4431 gtk_widget_get_allocated_height (widget));
4434 gtk_style_context_save (context);
4435 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_VIEW);
4437 if (gtk_cairo_should_draw_window (cr, tree_view->priv->header_window))
4439 gint n_visible_columns;
4442 for (list = tree_view->priv->columns; list != NULL; list = list->next)
4444 PsppSheetViewColumn *column = list->data;
4446 if (column == tree_view->priv->drag_column || !column->visible)
4449 if (span_intersects (column->allocation.x, column->allocation.width,
4450 (int) gtk_adjustment_get_value (tree_view->priv->hadjustment),
4451 (int) gtk_widget_get_allocated_width (widget))
4452 && column->button != NULL)
4453 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4454 column->button, cr);
4457 n_visible_columns = 0;
4458 for (list = tree_view->priv->columns; list; list = list->next)
4460 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4462 n_visible_columns ++;
4465 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->header_window);
4466 pspp_sheet_view_draw_vertical_grid_lines (tree_view,
4470 TREE_VIEW_HEADER_HEIGHT (tree_view));
4473 if (tree_view->priv->drag_window &&
4474 gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window))
4476 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4477 tree_view->priv->drag_column->button,
4481 gtk_style_context_restore (context);
4493 /* returns 0x1 when no column has been found -- yes it's hackish */
4494 static PsppSheetViewColumn *
4495 pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
4496 PsppSheetViewColumn *column,
4499 PsppSheetViewColumn *left_column = NULL;
4500 PsppSheetViewColumn *cur_column = NULL;
4503 if (!column->reorderable)
4504 return (PsppSheetViewColumn *)0x1;
4506 switch (drop_position)
4509 /* find first column where we can drop */
4510 tmp_list = tree_view->priv->columns;
4511 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4512 return (PsppSheetViewColumn *)0x1;
4516 g_assert (tmp_list);
4518 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4519 tmp_list = tmp_list->next;
4521 if (left_column && left_column->visible == FALSE)
4524 if (!tree_view->priv->column_drop_func)
4527 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4529 left_column = cur_column;
4536 if (!tree_view->priv->column_drop_func)
4539 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4542 return (PsppSheetViewColumn *)0x1;
4546 /* find first column after column where we can drop */
4547 tmp_list = tree_view->priv->columns;
4549 for (; tmp_list; tmp_list = tmp_list->next)
4550 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4553 if (!tmp_list || !tmp_list->next)
4554 return (PsppSheetViewColumn *)0x1;
4556 tmp_list = tmp_list->next;
4557 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4558 tmp_list = tmp_list->next;
4562 g_assert (tmp_list);
4564 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4565 tmp_list = tmp_list->next;
4567 if (left_column && left_column->visible == FALSE)
4569 left_column = cur_column;
4571 tmp_list = tmp_list->next;
4575 if (!tree_view->priv->column_drop_func)
4578 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4580 left_column = cur_column;
4587 if (!tree_view->priv->column_drop_func)
4590 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4593 return (PsppSheetViewColumn *)0x1;
4597 /* find first column before column where we can drop */
4598 tmp_list = tree_view->priv->columns;
4600 for (; tmp_list; tmp_list = tmp_list->next)
4601 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4604 if (!tmp_list || !tmp_list->prev)
4605 return (PsppSheetViewColumn *)0x1;
4607 tmp_list = tmp_list->prev;
4608 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4609 tmp_list = tmp_list->prev;
4613 g_assert (tmp_list);
4615 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4617 if (left_column && !left_column->visible)
4619 /*if (!tmp_list->prev)
4620 return (PsppSheetViewColumn *)0x1;
4623 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4624 tmp_list = tmp_list->prev->prev;
4627 cur_column = left_column;
4629 tmp_list = tmp_list->prev;
4633 if (!tree_view->priv->column_drop_func)
4636 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
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, NULL, cur_column, tree_view->priv->column_drop_func_data))
4649 return (PsppSheetViewColumn *)0x1;
4653 /* same as DROP_HOME case, but doing it backwards */
4654 tmp_list = g_list_last (tree_view->priv->columns);
4657 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4658 return (PsppSheetViewColumn *)0x1;
4662 g_assert (tmp_list);
4664 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4666 if (left_column && !left_column->visible)
4668 cur_column = left_column;
4669 tmp_list = tmp_list->prev;
4672 if (!tree_view->priv->column_drop_func)
4675 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4678 cur_column = left_column;
4679 tmp_list = tmp_list->prev;
4682 if (!tree_view->priv->column_drop_func)
4685 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4688 return (PsppSheetViewColumn *)0x1;
4692 return (PsppSheetViewColumn *)0x1;
4696 pspp_sheet_view_key_press (GtkWidget *widget,
4699 PsppSheetView *tree_view = (PsppSheetView *) widget;
4701 if (tree_view->priv->rubber_band_status)
4703 if (event->keyval == GDK_Escape)
4704 pspp_sheet_view_stop_rubber_band (tree_view);
4709 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4711 if (event->keyval == GDK_Escape)
4713 tree_view->priv->cur_reorder = NULL;
4714 pspp_sheet_view_button_release_drag_column (widget, NULL);
4719 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4721 GList *focus_column;
4724 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4726 for (focus_column = tree_view->priv->columns;
4728 focus_column = focus_column->next)
4730 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4732 if (column->button && gtk_widget_has_focus (column->button))
4737 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4738 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4739 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4741 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4743 if (!column->resizable)
4745 gtk_widget_error_bell (widget);
4749 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4750 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4752 gint old_width = column->resized_width;
4754 column->resized_width = MAX (column->resized_width,
4756 column->resized_width -= 2;
4757 if (column->resized_width < 0)
4758 column->resized_width = 0;
4760 if (column->min_width == -1)
4761 column->resized_width = MAX (column->button_request,
4762 column->resized_width);
4764 column->resized_width = MAX (column->min_width,
4765 column->resized_width);
4767 if (column->max_width != -1)
4768 column->resized_width = MIN (column->resized_width,
4771 column->use_resized_width = TRUE;
4773 if (column->resized_width != old_width)
4774 gtk_widget_queue_resize (widget);
4776 gtk_widget_error_bell (widget);
4778 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4779 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4781 gint old_width = column->resized_width;
4783 column->resized_width = MAX (column->resized_width,
4785 column->resized_width += 2;
4787 if (column->max_width != -1)
4788 column->resized_width = MIN (column->resized_width,
4791 column->use_resized_width = TRUE;
4793 if (column->resized_width != old_width)
4794 gtk_widget_queue_resize (widget);
4796 gtk_widget_error_bell (widget);
4803 (event->state & GDK_MOD1_MASK) &&
4804 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4805 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4806 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4807 || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4809 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4811 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4812 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4814 PsppSheetViewColumn *col;
4815 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
4816 if (col != (PsppSheetViewColumn *)0x1)
4817 pspp_sheet_view_move_column_after (tree_view, column, col);
4819 gtk_widget_error_bell (widget);
4821 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4822 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4824 PsppSheetViewColumn *col;
4825 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4826 if (col != (PsppSheetViewColumn *)0x1)
4827 pspp_sheet_view_move_column_after (tree_view, column, col);
4829 gtk_widget_error_bell (widget);
4831 else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4833 PsppSheetViewColumn *col;
4834 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4835 if (col != (PsppSheetViewColumn *)0x1)
4836 pspp_sheet_view_move_column_after (tree_view, column, col);
4838 gtk_widget_error_bell (widget);
4840 else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4842 PsppSheetViewColumn *col;
4843 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4844 if (col != (PsppSheetViewColumn *)0x1)
4845 pspp_sheet_view_move_column_after (tree_view, column, col);
4847 gtk_widget_error_bell (widget);
4854 /* Chain up to the parent class. It handles the keybindings. */
4855 if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4858 if (tree_view->priv->search_entry_avoid_unhandled_binding)
4860 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4864 /* We pass the event to the search_entry. If its text changes, then we start
4865 * the typeahead find capabilities. */
4866 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4867 && tree_view->priv->enable_search
4868 && !tree_view->priv->search_custom_entry_set)
4870 GdkEvent *new_event;
4872 const char *new_text;
4875 gboolean text_modified;
4876 gulong popup_menu_id;
4878 pspp_sheet_view_ensure_interactive_directory (tree_view);
4880 /* Make a copy of the current text */
4881 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4882 new_event = gdk_event_copy ((GdkEvent *) event);
4883 g_object_unref (((GdkEventKey *) new_event)->window);
4884 ((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window));
4885 gtk_widget_realize (tree_view->priv->search_window);
4887 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
4888 "popup-menu", G_CALLBACK (gtk_true),
4891 /* Move the entry off screen */
4892 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4893 gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4894 gdk_screen_get_width (screen) + 1,
4895 gdk_screen_get_height (screen) + 1);
4896 gtk_widget_show (tree_view->priv->search_window);
4898 /* Send the event to the window. If the preedit_changed signal is emitted
4899 * during this event, we will set priv->imcontext_changed */
4900 tree_view->priv->imcontext_changed = FALSE;
4901 retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4902 gdk_event_free (new_event);
4903 gtk_widget_hide (tree_view->priv->search_window);
4905 g_signal_handler_disconnect (tree_view->priv->search_entry,
4908 /* We check to make sure that the entry tried to handle the text, and that
4909 * the text has changed.
4911 new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
4912 text_modified = strcmp (old_text, new_text) != 0;
4914 if (tree_view->priv->imcontext_changed || /* we're in a preedit */
4915 (retval && text_modified)) /* ...or the text was modified */
4917 if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
4919 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
4924 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
4934 pspp_sheet_view_key_release (GtkWidget *widget,
4937 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4939 if (tree_view->priv->rubber_band_status)
4942 return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
4945 /* FIXME Is this function necessary? Can I get an enter_notify event
4946 * w/o either an expose event or a mouse motion event?
4949 pspp_sheet_view_enter_notify (GtkWidget *widget,
4950 GdkEventCrossing *event)
4952 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4956 /* Sanity check it */
4957 if (event->window != tree_view->priv->bin_window)
4960 if (tree_view->priv->row_count == 0)
4963 if (event->mode == GDK_CROSSING_GRAB ||
4964 event->mode == GDK_CROSSING_GTK_GRAB ||
4965 event->mode == GDK_CROSSING_GTK_UNGRAB ||
4966 event->mode == GDK_CROSSING_STATE_CHANGED)
4969 /* find the node internally */
4970 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
4973 pspp_sheet_view_find_offset (tree_view, new_y, &node);
4975 tree_view->priv->event_last_x = event->x;
4976 tree_view->priv->event_last_y = event->y;
4978 prelight_or_select (tree_view, node, event->x, event->y);
4984 pspp_sheet_view_leave_notify (GtkWidget *widget,
4985 GdkEventCrossing *event)
4987 PsppSheetView *tree_view;
4989 if (event->mode == GDK_CROSSING_GRAB)
4992 tree_view = PSPP_SHEET_VIEW (widget);
4994 if (tree_view->priv->prelight_node >= 0)
4995 _pspp_sheet_view_queue_draw_node (tree_view,
4996 tree_view->priv->prelight_node,
4999 tree_view->priv->event_last_x = -10000;
5000 tree_view->priv->event_last_y = -10000;
5002 prelight_or_select (tree_view,
5004 -1000, -1000); /* coords not possibly over an arrow */
5011 pspp_sheet_view_focus_out (GtkWidget *widget,
5012 GdkEventFocus *event)
5014 PsppSheetView *tree_view;
5016 tree_view = PSPP_SHEET_VIEW (widget);
5018 gtk_widget_queue_draw (widget);
5020 /* destroy interactive search dialog */
5021 if (tree_view->priv->search_window)
5022 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
5028 /* Incremental Reflow
5032 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
5035 GtkAllocation allocation;
5036 gint y = pspp_sheet_view_node_find_offset (tree_view, node)
5037 - gtk_adjustment_get_value (tree_view->priv->vadjustment)
5038 + TREE_VIEW_HEADER_HEIGHT (tree_view);
5040 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5042 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
5045 tree_view->priv->fixed_height);
5049 node_is_visible (PsppSheetView *tree_view,
5055 y = pspp_sheet_view_node_find_offset (tree_view, node);
5056 height = ROW_HEIGHT (tree_view);
5058 if (y >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5059 y + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5060 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5066 /* Returns the row height. */
5068 validate_row (PsppSheetView *tree_view,
5073 PsppSheetViewColumn *column;
5074 GList *list, *first_column, *last_column;
5076 gint horizontal_separator;
5077 gint vertical_separator;
5078 gint focus_line_width;
5079 gboolean draw_vgrid_lines, draw_hgrid_lines;
5081 gint grid_line_width;
5082 gboolean wide_separators;
5083 gint separator_height;
5085 gtk_widget_style_get (GTK_WIDGET (tree_view),
5086 "focus-padding", &focus_pad,
5087 "focus-line-width", &focus_line_width,
5088 "horizontal-separator", &horizontal_separator,
5089 "vertical-separator", &vertical_separator,
5090 "grid-line-width", &grid_line_width,
5091 "wide-separators", &wide_separators,
5092 "separator-height", &separator_height,
5096 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5097 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5099 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5100 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5102 for (last_column = g_list_last (tree_view->priv->columns);
5103 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5104 last_column = last_column->prev)
5107 for (first_column = g_list_first (tree_view->priv->columns);
5108 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5109 first_column = first_column->next)
5112 for (list = tree_view->priv->columns; list; list = list->next)
5117 column = list->data;
5119 if (! column->visible)
5122 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5123 pspp_sheet_view_column_cell_get_size (column,
5125 &tmp_width, &tmp_height);
5127 tmp_height += vertical_separator;
5128 height = MAX (height, tmp_height);
5130 tmp_width = tmp_width + horizontal_separator;
5132 if (draw_vgrid_lines)
5134 if (list->data == first_column || list->data == last_column)
5135 tmp_width += grid_line_width / 2.0;
5137 tmp_width += grid_line_width;
5140 if (tmp_width > column->requested_width)
5141 column->requested_width = tmp_width;
5144 if (draw_hgrid_lines)
5145 height += grid_line_width;
5147 tree_view->priv->post_validation_flag = TRUE;
5153 validate_visible_area (PsppSheetView *tree_view)
5155 GtkTreePath *path = NULL;
5156 GtkTreePath *above_path = NULL;
5159 gboolean size_changed = FALSE;
5161 gint area_above = 0;
5162 gint area_below = 0;
5163 GtkAllocation allocation;
5165 if (tree_view->priv->row_count == 0)
5168 if (tree_view->priv->scroll_to_path == NULL)
5171 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5173 total_height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5175 if (total_height == 0)
5178 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5181 /* we are going to scroll, and will update dy */
5182 _pspp_sheet_view_find_node (tree_view, path, &node);
5183 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5185 if (tree_view->priv->scroll_to_use_align)
5187 gint height = ROW_HEIGHT (tree_view);
5188 area_above = (total_height - height) *
5189 tree_view->priv->scroll_to_row_align;
5190 area_below = total_height - area_above - height;
5191 area_above = MAX (area_above, 0);
5192 area_below = MAX (area_below, 0);
5197 * 1) row not visible
5201 gint height = ROW_HEIGHT (tree_view);
5203 dy = pspp_sheet_view_node_find_offset (tree_view, node);
5205 if (dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5206 dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5207 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5209 /* row visible: keep the row at the same position */
5210 area_above = dy - gtk_adjustment_get_value (tree_view->priv->vadjustment);
5211 area_below = (gtk_adjustment_get_value (tree_view->priv->vadjustment) +
5212 gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5217 /* row not visible */
5219 && dy + height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5221 /* row at the beginning -- fixed */
5223 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment)
5224 - area_above - height;
5226 else if (dy >= (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5227 gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5229 /* row at the end -- fixed */
5230 area_above = dy - (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5231 gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
5232 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) -
5233 area_above - height;
5237 area_above = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - height;
5243 /* row somewhere in the middle, bring it to the top
5247 area_below = total_height - height;
5253 /* the scroll to isn't valid; ignore it.
5256 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5257 tree_view->priv->scroll_to_path = NULL;
5261 above_path = gtk_tree_path_copy (path);
5263 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5264 * backwards is much slower then forward, as there is no iter_prev function.
5265 * We go forwards first in case we run out of tree. Then we go backwards to
5268 while (node >= 0 && area_below > 0)
5270 gboolean done = FALSE;
5273 node = pspp_sheet_view_node_next (tree_view, node);
5276 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5278 gtk_tree_path_next (path);
5281 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5291 area_below -= ROW_HEIGHT (tree_view);
5293 gtk_tree_path_free (path);
5295 /* If we ran out of tree, and have extra area_below left, we need to add it
5298 area_above += area_below;
5300 _pspp_sheet_view_find_node (tree_view, above_path, &node);
5302 /* We walk backwards */
5303 while (area_above > 0)
5305 node = pspp_sheet_view_node_prev (tree_view, node);
5307 /* Always find the new path in the tree. We cannot just assume
5308 * a gtk_tree_path_prev() is enough here, as there might be children
5309 * in between this node and the previous sibling node. If this
5310 * appears to be a performance hotspot in profiles, we can look into
5311 * intrigate logic for keeping path, node and iter in sync like
5312 * we do for forward walks. (Which will be hard because of the lacking
5319 gtk_tree_path_free (above_path);
5320 above_path = _pspp_sheet_view_find_path (tree_view, node);
5322 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5324 area_above -= ROW_HEIGHT (tree_view);
5327 /* set the dy here to scroll to the path,
5328 * and sync the top row accordingly
5330 pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5331 pspp_sheet_view_top_row_to_dy (tree_view);
5333 /* update width/height and queue a resize */
5336 GtkRequisition requisition;
5338 /* We temporarily guess a size, under the assumption that it will be the
5339 * same when we get our next size_allocate. If we don't do this, we'll be
5340 * in an inconsistent state if we call top_row_to_dy. */
5342 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5343 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5344 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5345 gtk_adjustment_changed (tree_view->priv->hadjustment);
5346 gtk_adjustment_changed (tree_view->priv->vadjustment);
5347 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5350 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5351 tree_view->priv->scroll_to_path = NULL;
5354 gtk_tree_path_free (above_path);
5356 if (tree_view->priv->scroll_to_column)
5358 tree_view->priv->scroll_to_column = NULL;
5360 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5364 initialize_fixed_height_mode (PsppSheetView *tree_view)
5366 if (!tree_view->priv->row_count)
5369 if (tree_view->priv->fixed_height_set)
5372 if (tree_view->priv->fixed_height < 0)
5379 path = _pspp_sheet_view_find_path (tree_view, node);
5380 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5382 tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5384 gtk_tree_path_free (path);
5386 g_object_notify (G_OBJECT (tree_view), "fixed-height");
5390 /* Our strategy for finding nodes to validate is a little convoluted. We find
5391 * the left-most uninvalidated node. We then try walking right, validating
5392 * nodes. Once we find a valid node, we repeat the previous process of finding
5393 * the first invalid node.
5397 validate_rows_handler (PsppSheetView *tree_view)
5399 initialize_fixed_height_mode (tree_view);
5400 if (tree_view->priv->validate_rows_timer)
5402 g_source_remove (tree_view->priv->validate_rows_timer);
5403 tree_view->priv->validate_rows_timer = 0;
5410 do_presize_handler (PsppSheetView *tree_view)
5412 GtkRequisition requisition;
5414 validate_visible_area (tree_view);
5415 tree_view->priv->presize_handler_timer = 0;
5417 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5420 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5422 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5423 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5424 gtk_adjustment_changed (tree_view->priv->hadjustment);
5425 gtk_adjustment_changed (tree_view->priv->vadjustment);
5426 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5432 presize_handler_callback (gpointer data)
5434 do_presize_handler (PSPP_SHEET_VIEW (data));
5440 install_presize_handler (PsppSheetView *tree_view)
5442 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5445 if (! tree_view->priv->presize_handler_timer)
5447 tree_view->priv->presize_handler_timer =
5448 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5450 if (! tree_view->priv->validate_rows_timer)
5452 tree_view->priv->validate_rows_timer =
5453 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5458 scroll_sync_handler (PsppSheetView *tree_view)
5460 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5461 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5462 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5463 pspp_sheet_view_top_row_to_dy (tree_view);
5465 pspp_sheet_view_dy_to_top_row (tree_view);
5467 tree_view->priv->scroll_sync_timer = 0;
5473 install_scroll_sync_handler (PsppSheetView *tree_view)
5475 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5478 if (!tree_view->priv->scroll_sync_timer)
5480 tree_view->priv->scroll_sync_timer =
5481 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5486 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5490 gtk_tree_row_reference_free (tree_view->priv->top_row);
5494 tree_view->priv->top_row = NULL;
5495 tree_view->priv->top_row_dy = 0;
5499 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5500 tree_view->priv->top_row_dy = offset;
5504 /* Always call this iff dy is in the visible range. If the tree is empty, then
5505 * it's set to be NULL, and top_row_dy is 0;
5508 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5514 if (tree_view->priv->row_count == 0)
5516 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5520 offset = pspp_sheet_view_find_offset (tree_view,
5521 tree_view->priv->dy,
5526 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5530 path = _pspp_sheet_view_find_path (tree_view, node);
5531 pspp_sheet_view_set_top_row (tree_view, path, offset);
5532 gtk_tree_path_free (path);
5538 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5544 /* Avoid recursive calls */
5545 if (tree_view->priv->in_top_row_to_dy)
5548 if (tree_view->priv->top_row)
5549 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5556 _pspp_sheet_view_find_node (tree_view, path, &node);
5559 gtk_tree_path_free (path);
5563 /* keep dy and set new toprow */
5564 gtk_tree_row_reference_free (tree_view->priv->top_row);
5565 tree_view->priv->top_row = NULL;
5566 tree_view->priv->top_row_dy = 0;
5567 /* DO NOT install the idle handler */
5568 pspp_sheet_view_dy_to_top_row (tree_view);
5572 if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5574 /* new top row -- do NOT install the idle handler */
5575 pspp_sheet_view_dy_to_top_row (tree_view);
5579 new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5580 new_dy += tree_view->priv->top_row_dy;
5582 if (new_dy + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
5583 new_dy = tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
5585 new_dy = MAX (0, new_dy);
5587 tree_view->priv->in_top_row_to_dy = TRUE;
5588 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5589 tree_view->priv->in_top_row_to_dy = FALSE;
5594 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5596 install_presize_handler (tree_view);
5602 set_source_row (GdkDragContext *context,
5603 GtkTreeModel *model,
5604 GtkTreePath *source_row)
5606 g_object_set_data_full (G_OBJECT (context),
5607 "gtk-tree-view-source-row",
5608 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5609 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5613 get_source_row (GdkDragContext *context)
5615 GtkTreeRowReference *ref =
5616 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5619 return gtk_tree_row_reference_get_path (ref);
5626 GtkTreeRowReference *dest_row;
5627 guint path_down_mode : 1;
5628 guint empty_view_drop : 1;
5629 guint drop_append_mode : 1;
5634 dest_row_free (gpointer data)
5636 DestRow *dr = (DestRow *)data;
5638 gtk_tree_row_reference_free (dr->dest_row);
5639 g_slice_free (DestRow, dr);
5643 set_dest_row (GdkDragContext *context,
5644 GtkTreeModel *model,
5645 GtkTreePath *dest_row,
5646 gboolean path_down_mode,
5647 gboolean empty_view_drop,
5648 gboolean drop_append_mode)
5654 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5659 dr = g_slice_new (DestRow);
5661 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5662 dr->path_down_mode = path_down_mode != FALSE;
5663 dr->empty_view_drop = empty_view_drop != FALSE;
5664 dr->drop_append_mode = drop_append_mode != FALSE;
5666 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5667 dr, (GDestroyNotify) dest_row_free);
5671 get_dest_row (GdkDragContext *context,
5672 gboolean *path_down_mode)
5675 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5679 GtkTreePath *path = NULL;
5682 *path_down_mode = dr->path_down_mode;
5685 path = gtk_tree_row_reference_get_path (dr->dest_row);
5686 else if (dr->empty_view_drop)
5687 path = gtk_tree_path_new_from_indices (0, -1);
5691 if (path && dr->drop_append_mode)
5692 gtk_tree_path_next (path);
5700 /* Get/set whether drag_motion requested the drag data and
5701 * drag_data_received should thus not actually insert the data,
5702 * since the data doesn't result from a drop.
5705 set_status_pending (GdkDragContext *context,
5706 GdkDragAction suggested_action)
5708 g_object_set_data (G_OBJECT (context),
5709 "gtk-tree-view-status-pending",
5710 GINT_TO_POINTER (suggested_action));
5713 static GdkDragAction
5714 get_status_pending (GdkDragContext *context)
5716 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5717 "gtk-tree-view-status-pending"));
5720 static TreeViewDragInfo*
5721 get_info (PsppSheetView *tree_view)
5723 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5727 destroy_info (TreeViewDragInfo *di)
5729 g_slice_free (TreeViewDragInfo, di);
5732 static TreeViewDragInfo*
5733 ensure_info (PsppSheetView *tree_view)
5735 TreeViewDragInfo *di;
5737 di = get_info (tree_view);
5741 di = g_slice_new0 (TreeViewDragInfo);
5743 g_object_set_data_full (G_OBJECT (tree_view),
5744 "gtk-tree-view-drag-info",
5746 (GDestroyNotify) destroy_info);
5753 remove_info (PsppSheetView *tree_view)
5755 g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5760 drag_scan_timeout (gpointer data)
5762 PsppSheetView *tree_view;
5764 GdkModifierType state;
5765 GtkTreePath *path = NULL;
5766 PsppSheetViewColumn *column = NULL;
5767 GdkRectangle visible_rect;
5769 GDK_THREADS_ENTER ();
5771 tree_view = PSPP_SHEET_VIEW (data);
5773 gdk_window_get_pointer (tree_view->priv->bin_window,
5776 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5778 /* See if we are near the edge. */
5779 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5780 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5781 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5782 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5784 pspp_sheet_view_get_path_at_pos (tree_view,
5785 tree_view->priv->bin_window,
5794 pspp_sheet_view_scroll_to_cell (tree_view,
5800 gtk_tree_path_free (path);
5804 GDK_THREADS_LEAVE ();
5811 add_scroll_timeout (PsppSheetView *tree_view)
5813 if (tree_view->priv->scroll_timeout == 0)
5815 tree_view->priv->scroll_timeout =
5816 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5821 remove_scroll_timeout (PsppSheetView *tree_view)
5823 if (tree_view->priv->scroll_timeout != 0)
5825 g_source_remove (tree_view->priv->scroll_timeout);
5826 tree_view->priv->scroll_timeout = 0;
5831 check_model_dnd (GtkTreeModel *model,
5832 GType required_iface,
5833 const gchar *signal)
5835 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5837 g_warning ("You must override the default '%s' handler "
5838 "on PsppSheetView when using models that don't support "
5839 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5840 "is to connect to '%s' and call "
5841 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5842 "the default handler from running. Look at the source code "
5843 "for the default handler in gtktreeview.c to get an idea what "
5844 "your handler should do. (gtktreeview.c is in the GTK source "
5845 "code.) If you're using GTK from a language other than C, "
5846 "there may be a more natural way to override default handlers, e.g. via derivation.",
5847 signal, g_type_name (required_iface), signal);
5855 scroll_row_timeout (gpointer data)
5857 PsppSheetView *tree_view = data;
5859 pspp_sheet_view_horizontal_autoscroll (tree_view);
5860 pspp_sheet_view_vertical_autoscroll (tree_view);
5862 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5863 pspp_sheet_view_update_rubber_band (tree_view);
5868 /* Returns TRUE if event should not be propagated to parent widgets */
5870 set_destination_row (PsppSheetView *tree_view,
5871 GdkDragContext *context,
5872 /* coordinates relative to the widget */
5875 GdkDragAction *suggested_action,
5878 GtkTreePath *path = NULL;
5879 PsppSheetViewDropPosition pos;
5880 PsppSheetViewDropPosition old_pos;
5881 TreeViewDragInfo *di;
5883 GtkTreePath *old_dest_path = NULL;
5884 gboolean can_drop = FALSE;
5886 *suggested_action = 0;
5889 widget = GTK_WIDGET (tree_view);
5891 di = get_info (tree_view);
5893 if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5895 /* someone unset us as a drag dest, note that if
5896 * we return FALSE drag_leave isn't called
5899 pspp_sheet_view_set_drag_dest_row (tree_view,
5901 PSPP_SHEET_VIEW_DROP_BEFORE);
5903 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5905 return FALSE; /* no longer a drop site */
5908 *target = gtk_drag_dest_find_target (widget, context,
5909 gtk_drag_dest_get_target_list (widget));
5910 if (*target == GDK_NONE)
5915 if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
5921 GtkTreeModel *model;
5923 /* the row got dropped on empty space, let's setup a special case
5927 gtk_tree_path_free (path);
5929 model = pspp_sheet_view_get_model (tree_view);
5931 n_children = gtk_tree_model_iter_n_children (model, NULL);
5934 pos = PSPP_SHEET_VIEW_DROP_AFTER;
5935 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
5939 pos = PSPP_SHEET_VIEW_DROP_BEFORE;
5940 path = gtk_tree_path_new_from_indices (0, -1);
5950 /* If we left the current row's "open" zone, unset the timeout for
5953 pspp_sheet_view_get_drag_dest_row (tree_view,
5958 gtk_tree_path_free (old_dest_path);
5960 if (TRUE /* FIXME if the location droppable predicate */)
5968 GtkWidget *source_widget;
5970 *suggested_action = gdk_drag_context_get_suggested_action (context);
5971 source_widget = gtk_drag_get_source_widget (context);
5973 if (source_widget == widget)
5975 /* Default to MOVE, unless the user has
5976 * pressed ctrl or shift to affect available actions
5978 if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
5979 *suggested_action = GDK_ACTION_MOVE;
5982 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5987 /* can't drop here */
5988 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5990 PSPP_SHEET_VIEW_DROP_BEFORE);
5994 gtk_tree_path_free (path);
6000 get_logical_dest_row (PsppSheetView *tree_view,
6001 gboolean *path_down_mode,
6002 gboolean *drop_append_mode)
6004 /* adjust path to point to the row the drop goes in front of */
6005 GtkTreePath *path = NULL;
6006 PsppSheetViewDropPosition pos;
6008 g_return_val_if_fail (path_down_mode != NULL, NULL);
6009 g_return_val_if_fail (drop_append_mode != NULL, NULL);
6011 *path_down_mode = FALSE;
6012 *drop_append_mode = 0;
6014 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6019 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
6021 else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
6022 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
6023 *path_down_mode = TRUE;
6027 GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
6029 g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
6031 if (!gtk_tree_model_get_iter (model, &iter, path) ||
6032 !gtk_tree_model_iter_next (model, &iter))
6033 *drop_append_mode = 1;
6036 *drop_append_mode = 0;
6037 gtk_tree_path_next (path);
6045 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
6046 GdkEventMotion *event)
6048 GtkWidget *widget = GTK_WIDGET (tree_view);
6049 GdkDragContext *context;
6050 TreeViewDragInfo *di;
6051 GtkTreePath *path = NULL;
6053 gint cell_x, cell_y;
6054 GtkTreeModel *model;
6055 gboolean retval = FALSE;
6057 di = get_info (tree_view);
6059 if (di == NULL || !di->source_set)
6062 if (tree_view->priv->pressed_button < 0)
6065 if (!gtk_drag_check_threshold (widget,
6066 tree_view->priv->press_start_x,
6067 tree_view->priv->press_start_y,
6068 event->x, event->y))
6071 model = pspp_sheet_view_get_model (tree_view);
6076 button = tree_view->priv->pressed_button;
6077 tree_view->priv->pressed_button = -1;
6079 pspp_sheet_view_get_path_at_pos (tree_view,
6080 tree_view->priv->press_start_x,
6081 tree_view->priv->press_start_y,
6090 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6091 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6095 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6098 /* Now we can begin the drag */
6102 context = gtk_drag_begin (widget,
6103 gtk_drag_source_get_target_list (widget),
6108 set_source_row (context, model, path);
6112 gtk_tree_path_free (path);
6120 pspp_sheet_view_drag_begin (GtkWidget *widget,
6121 GdkDragContext *context)
6124 PsppSheetView *tree_view;
6125 GtkTreePath *path = NULL;
6126 gint cell_x, cell_y;
6128 TreeViewDragInfo *di;
6130 tree_view = PSPP_SHEET_VIEW (widget);
6132 /* if the user uses a custom DND source impl, we don't set the icon here */
6133 di = get_info (tree_view);
6135 if (di == NULL || !di->source_set)
6138 pspp_sheet_view_get_path_at_pos (tree_view,
6139 tree_view->priv->press_start_x,
6140 tree_view->priv->press_start_y,
6146 g_return_if_fail (path != NULL);
6148 row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6151 gtk_drag_set_icon_pixmap (context,
6152 gdk_drawable_get_colormap (row_pix),
6155 /* the + 1 is for the black border in the icon */
6156 tree_view->priv->press_start_x + 1,
6159 g_object_unref (row_pix);
6160 gtk_tree_path_free (path);
6166 pspp_sheet_view_drag_end (GtkWidget *widget,
6167 GdkDragContext *context)
6172 /* Default signal implementations for the drag signals */
6174 pspp_sheet_view_drag_data_get (GtkWidget *widget,
6175 GdkDragContext *context,
6176 GtkSelectionData *selection_data,
6180 PsppSheetView *tree_view;
6181 GtkTreeModel *model;
6182 TreeViewDragInfo *di;
6183 GtkTreePath *source_row;
6185 tree_view = PSPP_SHEET_VIEW (widget);
6187 model = pspp_sheet_view_get_model (tree_view);
6192 di = get_info (PSPP_SHEET_VIEW (widget));
6197 source_row = get_source_row (context);
6199 if (source_row == NULL)
6202 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6203 * any model; for DragSource models there are some other targets
6207 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6208 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6213 /* If drag_data_get does nothing, try providing row data. */
6214 if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6216 gtk_tree_set_row_drag_data (selection_data,
6222 gtk_tree_path_free (source_row);
6227 pspp_sheet_view_drag_data_delete (GtkWidget *widget,
6228 GdkDragContext *context)
6230 TreeViewDragInfo *di;
6231 GtkTreeModel *model;
6232 PsppSheetView *tree_view;
6233 GtkTreePath *source_row;
6235 tree_view = PSPP_SHEET_VIEW (widget);
6236 model = pspp_sheet_view_get_model (tree_view);
6238 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6241 di = get_info (tree_view);
6246 source_row = get_source_row (context);
6248 if (source_row == NULL)
6251 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6254 gtk_tree_path_free (source_row);
6256 set_source_row (context, NULL, NULL);
6260 pspp_sheet_view_drag_leave (GtkWidget *widget,
6261 GdkDragContext *context,
6264 /* unset any highlight row */
6265 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6267 PSPP_SHEET_VIEW_DROP_BEFORE);
6269 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6274 pspp_sheet_view_drag_motion (GtkWidget *widget,
6275 GdkDragContext *context,
6276 /* coordinates relative to the widget */
6282 GtkTreePath *path = NULL;
6283 PsppSheetViewDropPosition pos;
6284 PsppSheetView *tree_view;
6285 GdkDragAction suggested_action = 0;
6288 tree_view = PSPP_SHEET_VIEW (widget);
6290 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6293 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6295 /* we only know this *after* set_desination_row */
6296 empty = tree_view->priv->empty_view_drop;
6298 if (path == NULL && !empty)
6300 /* Can't drop here. */
6301 gdk_drag_status (context, 0, time);
6305 if (tree_view->priv->open_dest_timeout == 0 &&
6306 (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6307 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6313 add_scroll_timeout (tree_view);
6316 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6318 /* Request data so we can use the source row when
6319 * determining whether to accept the drop
6321 set_status_pending (context, suggested_action);
6322 gtk_drag_get_data (widget, context, target, time);
6326 set_status_pending (context, 0);
6327 gdk_drag_status (context, suggested_action, time);
6332 gtk_tree_path_free (path);
6339 pspp_sheet_view_drag_drop (GtkWidget *widget,
6340 GdkDragContext *context,
6341 /* coordinates relative to the widget */
6346 PsppSheetView *tree_view;
6348 GdkDragAction suggested_action = 0;
6349 GdkAtom target = GDK_NONE;
6350 TreeViewDragInfo *di;
6351 GtkTreeModel *model;
6352 gboolean path_down_mode;
6353 gboolean drop_append_mode;
6355 tree_view = PSPP_SHEET_VIEW (widget);
6357 model = pspp_sheet_view_get_model (tree_view);
6359 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6361 di = get_info (tree_view);
6366 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6369 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6372 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6374 if (target != GDK_NONE && path != NULL)
6376 /* in case a motion had requested drag data, change things so we
6377 * treat drag data receives as a drop.
6379 set_status_pending (context, 0);
6380 set_dest_row (context, model, path,
6381 path_down_mode, tree_view->priv->empty_view_drop,
6386 gtk_tree_path_free (path);
6388 /* Unset this thing */
6389 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6391 PSPP_SHEET_VIEW_DROP_BEFORE);
6393 if (target != GDK_NONE)
6395 gtk_drag_get_data (widget, context, target, time);
6403 pspp_sheet_view_drag_data_received (GtkWidget *widget,
6404 GdkDragContext *context,
6405 /* coordinates relative to the widget */
6408 GtkSelectionData *selection_data,
6413 TreeViewDragInfo *di;
6414 gboolean accepted = FALSE;
6415 GtkTreeModel *model;
6416 PsppSheetView *tree_view;
6417 GtkTreePath *dest_row;
6418 GdkDragAction suggested_action;
6419 gboolean path_down_mode;
6420 gboolean drop_append_mode;
6422 tree_view = PSPP_SHEET_VIEW (widget);
6424 model = pspp_sheet_view_get_model (tree_view);
6426 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6429 di = get_info (tree_view);
6434 suggested_action = get_status_pending (context);
6436 if (suggested_action)
6438 /* We are getting this data due to a request in drag_motion,
6439 * rather than due to a request in drag_drop, so we are just
6440 * supposed to call drag_status, not actually paste in the
6443 path = get_logical_dest_row (tree_view, &path_down_mode,
6447 suggested_action = 0;
6448 else if (path_down_mode)
6449 gtk_tree_path_down (path);
6451 if (suggested_action)
6453 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6459 path_down_mode = FALSE;
6460 gtk_tree_path_up (path);
6462 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6465 suggested_action = 0;
6468 suggested_action = 0;
6472 gdk_drag_status (context, suggested_action, time);
6475 gtk_tree_path_free (path);
6477 /* If you can't drop, remove user drop indicator until the next motion */
6478 if (suggested_action == 0)
6479 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6481 PSPP_SHEET_VIEW_DROP_BEFORE);
6486 dest_row = get_dest_row (context, &path_down_mode);
6488 if (dest_row == NULL)
6491 if (gtk_selection_data_get_length (selection_data) >= 0)
6495 gtk_tree_path_down (dest_row);
6496 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6497 dest_row, selection_data))
6498 gtk_tree_path_up (dest_row);
6502 if (gtk_selection_data_get_length (selection_data) >= 0)
6504 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6510 gtk_drag_finish (context,
6512 (gdk_drag_context_get_actions (context) == GDK_ACTION_MOVE),
6515 if (gtk_tree_path_get_depth (dest_row) == 1
6516 && gtk_tree_path_get_indices (dest_row)[0] == 0)
6518 /* special special case drag to "0", scroll to first item */
6519 if (!tree_view->priv->scroll_to_path)
6520 pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6523 gtk_tree_path_free (dest_row);
6526 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6531 /* GtkContainer Methods
6536 pspp_sheet_view_remove (GtkContainer *container,
6539 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6540 PsppSheetViewChild *child = NULL;
6543 tmp_list = tree_view->priv->children;
6546 child = tmp_list->data;
6547 if (child->widget == widget)
6549 gtk_widget_unparent (widget);
6551 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6552 g_list_free_1 (tmp_list);
6553 g_slice_free (PsppSheetViewChild, child);
6557 tmp_list = tmp_list->next;
6560 tmp_list = tree_view->priv->columns;
6564 PsppSheetViewColumn *column;
6565 column = tmp_list->data;
6566 if (column->button == widget)
6568 gtk_widget_unparent (widget);
6571 tmp_list = tmp_list->next;
6576 pspp_sheet_view_forall (GtkContainer *container,
6577 gboolean include_internals,
6578 GtkCallback callback,
6579 gpointer callback_data)
6581 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6582 PsppSheetViewChild *child = NULL;
6583 PsppSheetViewColumn *column;
6586 tmp_list = tree_view->priv->children;
6589 child = tmp_list->data;
6590 tmp_list = tmp_list->next;
6592 (* callback) (child->widget, callback_data);
6594 if (include_internals == FALSE)
6597 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6599 column = tmp_list->data;
6602 (* callback) (column->button, callback_data);
6606 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6607 * cells. If so we draw one big row-spanning focus rectangle.
6610 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6614 if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6615 return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6617 for (list = tree_view->priv->columns; list; list = list->next)
6619 if (!((PsppSheetViewColumn *)list->data)->visible)
6621 if (_pspp_sheet_view_column_count_special_cells (list->data))
6629 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6630 PsppSheetViewColumn *focus_column,
6631 gboolean clamp_column_visible)
6633 g_return_if_fail (focus_column != NULL);
6635 tree_view->priv->focus_column = focus_column;
6637 if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view)) != focus_column->button)
6638 gtk_widget_grab_focus (focus_column->button);
6640 if (clamp_column_visible)
6641 pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6644 /* Returns TRUE if the focus is within the headers, after the focus operation is
6648 pspp_sheet_view_header_focus (PsppSheetView *tree_view,
6649 GtkDirectionType dir,
6650 gboolean clamp_column_visible)
6652 GtkWidget *focus_child;
6653 PsppSheetViewColumn *focus_column;
6654 GList *last_column, *first_column;
6658 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6661 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
6663 first_column = tree_view->priv->columns;
6664 while (first_column)
6666 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6668 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6670 first_column = first_column->next;
6673 /* No headers are visible, or are focusable. We can't focus in or out.
6675 if (first_column == NULL)
6678 last_column = g_list_last (tree_view->priv->columns);
6681 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6683 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6685 last_column = last_column->prev;
6689 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6693 case GTK_DIR_TAB_BACKWARD:
6694 case GTK_DIR_TAB_FORWARD:
6697 if (focus_child == NULL)
6699 if (tree_view->priv->focus_column != NULL &&
6700 pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6701 focus_column = tree_view->priv->focus_column;
6703 focus_column = first_column->data;
6704 pspp_sheet_view_focus_column (tree_view, focus_column,
6705 clamp_column_visible);
6712 if (focus_child == NULL)
6714 if (tree_view->priv->focus_column != NULL)
6715 focus_column = tree_view->priv->focus_column;
6716 else if (dir == GTK_DIR_LEFT)
6717 focus_column = last_column->data;
6719 focus_column = first_column->data;
6720 pspp_sheet_view_focus_column (tree_view, focus_column,
6721 clamp_column_visible);
6725 if (gtk_widget_child_focus (focus_child, dir))
6727 /* The focus moves inside the button. */
6728 /* This is probably a great example of bad UI */
6729 if (clamp_column_visible)
6730 pspp_sheet_view_clamp_column_visible (tree_view,
6731 tree_view->priv->focus_column,
6736 /* We need to move the focus among the row of buttons. */
6737 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6738 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6741 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6742 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6744 gtk_widget_error_bell (GTK_WIDGET (tree_view));
6750 PsppSheetViewColumn *column;
6752 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6753 tmp_list = tmp_list->next;
6755 tmp_list = tmp_list->prev;
6757 if (tmp_list == NULL)
6759 g_warning ("Internal button not found");
6762 column = tmp_list->data;
6763 if (column->visible &&
6764 pspp_sheet_view_column_can_focus (column))
6768 pspp_sheet_view_focus_column (tree_view, column,
6769 clamp_column_visible);
6777 g_assert_not_reached ();
6784 /* This function returns in 'path' the first focusable path, if the given path
6785 * is already focusable, it's the returned one.
6789 search_first_focusable_path (PsppSheetView *tree_view,
6791 gboolean search_forward,
6794 /* XXX this function is trivial given that the sheetview doesn't support
6798 if (!path || !*path)
6801 _pspp_sheet_view_find_node (tree_view, *path, &node);
6809 return (*path != NULL);
6813 pspp_sheet_view_focus (GtkWidget *widget,
6814 GtkDirectionType direction)
6816 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6817 GtkContainer *container = GTK_CONTAINER (widget);
6818 GtkWidget *focus_child;
6820 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6823 focus_child = gtk_container_get_focus_child (container);
6825 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6826 /* Case 1. Headers currently have focus. */
6833 pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6835 case GTK_DIR_TAB_BACKWARD:
6838 case GTK_DIR_TAB_FORWARD:
6840 gtk_widget_grab_focus (widget);
6843 g_assert_not_reached ();
6848 /* Case 2. We don't have focus at all. */
6849 if (!gtk_widget_has_focus (widget))
6851 if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6852 gtk_widget_grab_focus (widget);
6856 /* Case 3. We have focus already. */
6857 if (direction == GTK_DIR_TAB_BACKWARD)
6858 return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6859 else if (direction == GTK_DIR_TAB_FORWARD)
6862 /* Other directions caught by the keybindings */
6863 gtk_widget_grab_focus (widget);
6868 pspp_sheet_view_grab_focus (GtkWidget *widget)
6870 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6872 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6876 pspp_sheet_view_style_updated (GtkWidget *widget)
6878 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6880 PsppSheetViewColumn *column;
6881 GtkStyleContext *context;
6883 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->style_updated (widget);
6885 if (gtk_widget_get_realized (widget))
6887 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
6888 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
6889 gtk_style_context_set_background (context, tree_view->priv->header_window);
6890 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6893 gtk_widget_style_get (widget,
6894 "expander-size", &tree_view->priv->expander_size,
6896 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6898 for (list = tree_view->priv->columns; list; list = list->next)
6900 column = list->data;
6901 _pspp_sheet_view_column_cell_set_dirty (column);
6904 tree_view->priv->fixed_height = -1;
6906 /* Invalidate cached button style. */
6907 if (tree_view->priv->button_style)
6909 g_object_unref (tree_view->priv->button_style);
6910 tree_view->priv->button_style = NULL;
6913 gtk_widget_queue_resize (widget);
6918 pspp_sheet_view_set_focus_child (GtkContainer *container,
6921 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6924 for (list = tree_view->priv->columns; list; list = list->next)
6926 if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
6928 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
6933 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
6937 pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
6938 GtkAdjustment *hadj,
6939 GtkAdjustment *vadj)
6941 gboolean need_adjust = FALSE;
6943 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
6946 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
6948 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6950 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
6952 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6954 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
6956 g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
6957 pspp_sheet_view_adjustment_changed,
6959 g_object_unref (tree_view->priv->hadjustment);
6962 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
6964 g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
6965 pspp_sheet_view_adjustment_changed,
6967 g_object_unref (tree_view->priv->vadjustment);
6970 if (tree_view->priv->hadjustment != hadj)
6972 tree_view->priv->hadjustment = hadj;
6973 g_object_ref_sink (tree_view->priv->hadjustment);
6975 g_signal_connect (tree_view->priv->hadjustment, "value-changed",
6976 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6981 if (tree_view->priv->vadjustment != vadj)
6983 tree_view->priv->vadjustment = vadj;
6984 g_object_ref_sink (tree_view->priv->vadjustment);
6986 g_signal_connect (tree_view->priv->vadjustment, "value-changed",
6987 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6993 pspp_sheet_view_adjustment_changed (NULL, tree_view);
6998 pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
6999 GtkMovementStep step,
7002 PsppSheetSelectMode mode;
7003 GdkModifierType state;
7005 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
7006 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
7007 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
7008 step == GTK_MOVEMENT_DISPLAY_LINES ||
7009 step == GTK_MOVEMENT_PAGES ||
7010 step == GTK_MOVEMENT_BUFFER_ENDS ||
7011 step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
7013 if (tree_view->priv->row_count == 0)
7015 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7018 pspp_sheet_view_stop_editing (tree_view, FALSE);
7019 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7020 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7023 if (gtk_get_current_event_state (&state))
7025 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7026 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
7027 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
7028 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
7030 /* else we assume not pressed */
7034 case GTK_MOVEMENT_LOGICAL_POSITIONS:
7035 pspp_sheet_view_move_cursor_tab (tree_view, count);
7037 case GTK_MOVEMENT_VISUAL_POSITIONS:
7038 pspp_sheet_view_move_cursor_left_right (tree_view, count, mode);
7040 case GTK_MOVEMENT_DISPLAY_LINES:
7041 pspp_sheet_view_move_cursor_up_down (tree_view, count, mode);
7043 case GTK_MOVEMENT_PAGES:
7044 pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode);
7046 case GTK_MOVEMENT_BUFFER_ENDS:
7047 pspp_sheet_view_move_cursor_start_end (tree_view, count, mode);
7049 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7050 pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode);
7053 g_assert_not_reached ();
7060 pspp_sheet_view_put (PsppSheetView *tree_view,
7061 GtkWidget *child_widget,
7063 PsppSheetViewColumn *column)
7065 PsppSheetViewChild *child;
7067 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7068 g_return_if_fail (GTK_IS_WIDGET (child_widget));
7070 child = g_slice_new (PsppSheetViewChild);
7072 child->widget = child_widget;
7073 _pspp_sheet_view_find_node (tree_view, path, &child->node);
7074 if (child->node < 0)
7076 g_assert_not_reached ();
7078 child->column = column;
7080 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7082 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7083 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7085 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7088 /* TreeModel Callbacks
7092 pspp_sheet_view_row_changed (GtkTreeModel *model,
7097 PsppSheetView *tree_view = (PsppSheetView *)data;
7099 gboolean free_path = FALSE;
7100 GtkTreePath *cursor_path;
7102 g_return_if_fail (path != NULL || iter != NULL);
7104 if (tree_view->priv->cursor != NULL)
7105 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7109 if (tree_view->priv->edited_column &&
7110 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7111 pspp_sheet_view_stop_editing (tree_view, TRUE);
7113 if (cursor_path != NULL)
7114 gtk_tree_path_free (cursor_path);
7118 path = gtk_tree_model_get_path (model, iter);
7121 else if (iter == NULL)
7122 gtk_tree_model_get_iter (model, iter, path);
7124 _pspp_sheet_view_find_node (tree_view,
7130 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7131 pspp_sheet_view_node_queue_redraw (tree_view, node);
7135 gtk_tree_path_free (path);
7139 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7144 PsppSheetView *tree_view = (PsppSheetView *) data;
7147 gint height = tree_view->priv->fixed_height;
7148 gboolean free_path = FALSE;
7149 gboolean node_visible = TRUE;
7151 g_return_if_fail (path != NULL || iter != NULL);
7155 path = gtk_tree_model_get_path (model, iter);
7158 else if (iter == NULL)
7159 gtk_tree_model_get_iter (model, iter, path);
7161 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7163 /* Update all row-references */
7164 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7165 indices = gtk_tree_path_get_indices (path);
7166 tmpnode = indices[0];
7168 range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7172 if (node_visible && node_is_visible (tree_view, tmpnode))
7173 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7175 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7178 install_presize_handler (tree_view);
7180 gtk_tree_path_free (path);
7184 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7188 PsppSheetView *tree_view = (PsppSheetView *)data;
7191 g_return_if_fail (path != NULL);
7193 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7195 _pspp_sheet_view_find_node (tree_view, path, &node);
7200 range_tower_delete (tree_view->priv->selected, node, 1);
7202 /* Ensure we don't have a dangling pointer to a dead node */
7203 ensure_unprelighted (tree_view);
7205 /* Cancel editting if we've started */
7206 pspp_sheet_view_stop_editing (tree_view, TRUE);
7208 if (tree_view->priv->destroy_count_func)
7210 gint child_count = 0;
7211 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7214 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7216 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7218 gtk_tree_row_reference_free (tree_view->priv->top_row);
7219 tree_view->priv->top_row = NULL;
7222 install_scroll_sync_handler (tree_view);
7224 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7227 if (helper_data.changed)
7228 g_signal_emit_by_name (tree_view->priv->selection, "changed");
7233 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7234 GtkTreePath *parent,
7239 PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7242 /* XXX need to adjust selection */
7243 len = gtk_tree_model_iter_n_children (model, iter);
7248 gtk_tree_row_reference_reordered (G_OBJECT (data),
7253 if (gtk_tree_path_get_depth (parent) != 0)
7256 if (tree_view->priv->edited_column)
7257 pspp_sheet_view_stop_editing (tree_view, TRUE);
7259 /* we need to be unprelighted */
7260 ensure_unprelighted (tree_view);
7262 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7264 pspp_sheet_view_dy_to_top_row (tree_view);
7268 /* Internal tree functions
7273 pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view,
7274 PsppSheetViewColumn *column,
7278 PsppSheetViewColumn *tmp_column = NULL;
7289 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7292 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7294 list = (rtl ? list->prev : list->next))
7296 tmp_column = list->data;
7298 if (tmp_column == column)
7301 if (tmp_column->visible)
7302 total_width += tmp_column->width;
7305 if (tmp_column != column)
7307 g_warning (G_STRLOC": passed-in column isn't in the tree");
7316 if (column->visible)
7317 *x2 = total_width + column->width;
7319 *x2 = total_width; /* width of 0 */
7323 /* Make sure the node is visible vertically */
7325 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7328 gint node_dy, height;
7329 GtkTreePath *path = NULL;
7331 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7334 /* just return if the node is visible, avoiding a costly expose */
7335 node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7336 height = ROW_HEIGHT (tree_view);
7337 if (node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment)
7338 && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
7339 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
7342 path = _pspp_sheet_view_find_path (tree_view, node);
7345 /* We process updates because we want to clear old selected items when we scroll.
7346 * if this is removed, we get a "selection streak" at the bottom. */
7347 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7348 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7349 gtk_tree_path_free (path);
7354 pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
7355 PsppSheetViewColumn *column,
7356 gboolean focus_to_cell)
7363 x = column->allocation.x;
7364 width = column->allocation.width;
7366 if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7368 /* The column is larger than the horizontal page size. If the
7369 * column has cells which can be focussed individually, then we make
7370 * sure the cell which gets focus is fully visible (if even the
7371 * focus cell is bigger than the page size, we make sure the
7372 * left-hand side of the cell is visible).
7374 * If the column does not have those so-called special cells, we
7375 * make sure the left-hand side of the column is visible.
7378 if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7380 GtkTreePath *cursor_path;
7381 GdkRectangle background_area, cell_area, focus_area;
7383 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7385 pspp_sheet_view_get_cell_area (tree_view,
7386 cursor_path, column, &cell_area);
7387 pspp_sheet_view_get_background_area (tree_view,
7388 cursor_path, column,
7391 gtk_tree_path_free (cursor_path);
7393 _pspp_sheet_view_column_get_focus_area (column,
7399 width = focus_area.width;
7401 if (width < gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7403 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7404 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7405 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7406 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7407 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7411 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7413 gtk_adjustment_get_lower (tree_view->priv->hadjustment),
7414 gtk_adjustment_get_upper (tree_view->priv->hadjustment)
7415 - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)));
7419 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7420 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7421 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7422 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7423 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7428 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7433 path = gtk_tree_path_new ();
7435 gtk_tree_path_append_index (path, node);
7440 _pspp_sheet_view_find_node (PsppSheetView *tree_view,
7444 gint *indices = gtk_tree_path_get_indices (path);
7445 gint depth = gtk_tree_path_get_depth (path);
7448 if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7454 pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
7457 gboolean add_shifted_binding,
7458 GtkMovementStep step,
7462 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7467 if (add_shifted_binding)
7468 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7473 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7476 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7481 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7488 pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view,
7489 PsppSheetViewColumn *column)
7491 PsppSheetViewColumn *left_column;
7492 PsppSheetViewColumn *cur_column = NULL;
7493 PsppSheetViewColumnReorder *reorder;
7498 /* We want to precalculate the motion list such that we know what column slots
7502 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7504 /* First, identify all possible drop spots */
7506 tmp_list = g_list_last (tree_view->priv->columns);
7508 tmp_list = g_list_first (tree_view->priv->columns);
7512 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7513 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7515 if (cur_column->visible == FALSE)
7518 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7519 if (left_column != column && cur_column != column &&
7520 tree_view->priv->column_drop_func &&
7521 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7523 left_column = cur_column;
7526 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7527 reorder->left_column = left_column;
7528 left_column = reorder->right_column = cur_column;
7530 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7533 /* Add the last one */
7534 if (tree_view->priv->column_drop_func == NULL ||
7535 ((left_column != column) &&
7536 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7538 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7539 reorder->left_column = left_column;
7540 reorder->right_column = NULL;
7541 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7544 /* We quickly check to see if it even makes sense to reorder columns. */
7545 /* If there is nothing that can be moved, then we return */
7547 if (tree_view->priv->column_drag_info == NULL)
7550 /* We know there are always 2 slots possbile, as you can always return column. */
7551 /* If that's all there is, return */
7552 if (tree_view->priv->column_drag_info->next == NULL ||
7553 (tree_view->priv->column_drag_info->next->next == NULL &&
7554 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7555 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7557 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7558 g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7559 g_list_free (tree_view->priv->column_drag_info);
7560 tree_view->priv->column_drag_info = NULL;
7563 /* We fill in the ranges for the columns, now that we've isolated them */
7564 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7566 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7568 reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7570 reorder->left_align = left;
7571 if (tmp_list->next != NULL)
7573 g_assert (tmp_list->next->data);
7574 left = reorder->right_align = (reorder->right_column->allocation.x +
7575 reorder->right_column->allocation.width +
7576 ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7580 gint width = gdk_window_get_width (tree_view->priv->header_window);
7581 reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7587 _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view,
7588 PsppSheetViewColumn *column)
7590 GdkEvent *send_event;
7591 GtkAllocation allocation;
7593 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7594 GdkDisplay *display = gdk_screen_get_display (screen);
7596 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7597 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7598 g_return_if_fail (column->button);
7600 pspp_sheet_view_set_column_drag_info (tree_view, column);
7602 if (tree_view->priv->column_drag_info == NULL)
7605 if (tree_view->priv->drag_window == NULL)
7607 GdkWindowAttr attributes;
7608 guint attributes_mask;
7610 attributes.window_type = GDK_WINDOW_CHILD;
7611 attributes.wclass = GDK_INPUT_OUTPUT;
7612 attributes.x = column->allocation.x;
7614 attributes.width = column->allocation.width;
7615 attributes.height = column->allocation.height;
7616 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7617 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7618 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL ;
7620 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7623 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7626 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7627 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7629 gtk_grab_remove (column->button);
7631 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7632 send_event->crossing.send_event = TRUE;
7633 send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button)));
7634 send_event->crossing.subwindow = NULL;
7635 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7636 send_event->crossing.time = GDK_CURRENT_TIME;
7638 gtk_propagate_event (column->button, send_event);
7639 gdk_event_free (send_event);
7641 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7642 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7643 send_event->button.send_event = TRUE;
7644 send_event->button.time = GDK_CURRENT_TIME;
7645 send_event->button.x = -1;
7646 send_event->button.y = -1;
7647 send_event->button.axes = NULL;
7648 send_event->button.state = 0;
7649 send_event->button.button = 1;
7650 send_event->button.device =
7651 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
7653 send_event->button.x_root = 0;
7654 send_event->button.y_root = 0;
7656 gtk_propagate_event (column->button, send_event);
7657 gdk_event_free (send_event);
7659 /* Kids, don't try this at home */
7660 g_object_ref (column->button);
7661 gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7662 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7663 gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7664 g_object_unref (column->button);
7666 tree_view->priv->drag_column_x = column->allocation.x;
7667 allocation = column->allocation;
7669 gtk_widget_size_allocate (column->button, &allocation);
7670 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7672 tree_view->priv->drag_column = column;
7673 gdk_window_show (tree_view->priv->drag_window);
7675 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7677 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7678 while (gtk_events_pending ())
7679 gtk_main_iteration ();
7681 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7682 gdk_pointer_grab (tree_view->priv->drag_window,
7684 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7685 NULL, NULL, GDK_CURRENT_TIME);
7686 gdk_keyboard_grab (tree_view->priv->drag_window,
7692 _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view,
7694 const GdkRectangle *clip_rect)
7697 GtkAllocation allocation;
7699 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7702 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
7704 rect.width = MAX (tree_view->priv->width, allocation.width);
7706 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7707 rect.height = ROW_HEIGHT (tree_view);
7711 GdkRectangle new_rect;
7713 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7715 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7719 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7724 pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
7726 const GdkRectangle *clip_rect)
7730 _pspp_sheet_view_find_node (tree_view, path, &node);
7733 _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7737 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7740 GtkTreePath *cursor_path;
7742 if ((tree_view->priv->row_count == 0) ||
7743 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7747 if (tree_view->priv->cursor)
7748 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7750 if (cursor_path == NULL)
7752 /* There's no cursor. Move the cursor to the first selected row, if any
7753 * are selected, otherwise to the first row in the sheetview.
7755 GList *selected_rows;
7756 GtkTreeModel *model;
7757 PsppSheetSelection *selection;
7759 selection = pspp_sheet_view_get_selection (tree_view);
7760 selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7764 /* XXX we could avoid doing O(n) work to get this result */
7765 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7766 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7767 g_list_free (selected_rows);
7771 cursor_path = gtk_tree_path_new_first ();
7772 search_first_focusable_path (tree_view, &cursor_path,
7776 gtk_tree_row_reference_free (tree_view->priv->cursor);
7777 tree_view->priv->cursor = NULL;
7781 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7782 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7783 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0);
7785 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0);
7791 /* Now find a column for the cursor. */
7792 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7794 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7795 gtk_tree_path_free (cursor_path);
7797 if (tree_view->priv->focus_column == NULL)
7800 for (list = tree_view->priv->columns; list; list = list->next)
7802 if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7804 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7805 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7806 pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7816 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7818 PsppSheetSelectMode mode)
7820 gint selection_count;
7821 int cursor_node = -1;
7822 int new_cursor_node = -1;
7823 GtkTreePath *cursor_path = NULL;
7824 gboolean grab_focus = TRUE;
7826 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7830 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7831 /* FIXME: we lost the cursor; should we get the first? */
7834 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7835 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7837 if (cursor_node < 0)
7838 /* FIXME: we lost the cursor; should we get the first? */
7841 selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7843 if (selection_count == 0
7844 && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7845 && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
7847 /* Don't move the cursor, but just select the current node */
7848 new_cursor_node = cursor_node;
7853 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7855 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7858 gtk_tree_path_free (cursor_path);
7860 if (new_cursor_node)
7862 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7864 search_first_focusable_path (tree_view, &cursor_path,
7869 gtk_tree_path_free (cursor_path);
7873 * If the list has only one item and multi-selection is set then select
7874 * the row (if not yet selected).
7876 if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7877 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7878 new_cursor_node < 0)
7881 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7883 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7885 if (new_cursor_node < 0
7886 && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
7888 new_cursor_node = cursor_node;
7892 new_cursor_node = -1;
7896 if (new_cursor_node >= 0)
7898 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7899 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode);
7900 gtk_tree_path_free (cursor_path);
7904 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7906 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
7908 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
7910 GTK_DIR_UP : GTK_DIR_DOWN))
7912 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
7915 gtk_widget_child_focus (toplevel,
7917 GTK_DIR_TAB_BACKWARD :
7918 GTK_DIR_TAB_FORWARD);
7925 gtk_widget_error_bell (GTK_WIDGET (tree_view));
7930 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7932 return new_cursor_node >= 0;
7936 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
7938 PsppSheetSelectMode mode)
7940 int cursor_node = -1;
7941 GtkTreePath *old_cursor_path = NULL;
7942 GtkTreePath *cursor_path = NULL;
7943 int start_cursor_node = -1;
7946 gint vertical_separator;
7948 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7951 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
7952 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7954 /* This is sorta weird. Focus in should give us a cursor */
7957 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
7958 _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
7960 if (cursor_node < 0)
7962 /* FIXME: we lost the cursor. Should we try to get one? */
7963 gtk_tree_path_free (old_cursor_path);
7967 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7968 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
7969 y += tree_view->priv->cursor_offset;
7970 y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment);
7971 y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment), (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator);
7973 if (y >= tree_view->priv->height)
7974 y = tree_view->priv->height - 1;
7976 tree_view->priv->cursor_offset =
7977 pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
7979 if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
7981 cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7982 tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
7985 y -= tree_view->priv->cursor_offset;
7986 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7988 start_cursor_node = cursor_node;
7990 if (! search_first_focusable_path (tree_view, &cursor_path,
7994 /* It looks like we reached the end of the view without finding
7995 * a focusable row. We will step backwards to find the last
7998 cursor_node = start_cursor_node;
7999 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8001 search_first_focusable_path (tree_view, &cursor_path,
8010 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8012 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode);
8015 pspp_sheet_view_scroll_to_point (tree_view, -1, y);
8016 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8017 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8019 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
8020 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8022 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8025 gtk_tree_path_free (old_cursor_path);
8026 gtk_tree_path_free (cursor_path);
8030 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8032 PsppSheetSelectMode mode)
8034 int cursor_node = -1;
8035 GtkTreePath *cursor_path = NULL;
8036 PsppSheetViewColumn *column;
8039 gboolean found_column = FALSE;
8042 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8044 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8047 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8048 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8052 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8053 if (cursor_node < 0)
8055 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8057 gtk_tree_path_free (cursor_path);
8060 gtk_tree_path_free (cursor_path);
8062 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8063 if (tree_view->priv->focus_column)
8065 for (; list; list = (rtl ? list->prev : list->next))
8067 if (list->data == tree_view->priv->focus_column)
8074 gboolean left, right;
8076 column = list->data;
8077 if (column->visible == FALSE || column->row_head)
8080 pspp_sheet_view_column_cell_set_cell_data (column,
8081 tree_view->priv->model,
8086 right = list->prev ? TRUE : FALSE;
8087 left = list->next ? TRUE : FALSE;
8091 left = list->prev ? TRUE : FALSE;
8092 right = list->next ? TRUE : FALSE;
8095 if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8097 tree_view->priv->focus_column = column;
8098 found_column = TRUE;
8103 list = rtl ? list->prev : list->next;
8105 list = rtl ? list->next : list->prev;
8110 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8111 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8112 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8116 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8119 pspp_sheet_view_clamp_column_visible (tree_view,
8120 tree_view->priv->focus_column, TRUE);
8124 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8126 PsppSheetSelectMode mode)
8128 int cursor_node = -1;
8129 GtkTreePath *cursor_path = NULL;
8130 PsppSheetViewColumn *column;
8131 PsppSheetViewColumn *found_column;
8136 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8138 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8141 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8142 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8146 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8147 if (cursor_node < 0)
8149 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8151 gtk_tree_path_free (cursor_path);
8154 gtk_tree_path_free (cursor_path);
8156 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8157 if (tree_view->priv->focus_column)
8159 for (; list; list = (rtl ? list->prev : list->next))
8161 if (list->data == tree_view->priv->focus_column)
8166 found_column = NULL;
8169 gboolean left, right;
8171 column = list->data;
8172 if (column->visible == FALSE || column->row_head)
8175 pspp_sheet_view_column_cell_set_cell_data (column,
8176 tree_view->priv->model,
8181 right = list->prev ? TRUE : FALSE;
8182 left = list->next ? TRUE : FALSE;
8186 left = list->prev ? TRUE : FALSE;
8187 right = list->next ? TRUE : FALSE;
8190 if (column->tabbable
8191 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8192 found_column = column;
8196 list = rtl ? list->prev : list->next;
8198 list = rtl ? list->next : list->prev;
8203 tree_view->priv->focus_column = found_column;
8204 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8205 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8206 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8209 pspp_sheet_view_clamp_column_visible (tree_view,
8210 tree_view->priv->focus_column, TRUE);
8214 try_move_cursor_tab (PsppSheetView *tree_view,
8215 gboolean start_at_focus_column,
8218 PsppSheetViewColumn *column;
8220 int cursor_node = -1;
8221 GtkTreePath *cursor_path = NULL;
8225 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8226 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8230 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8231 if (cursor_node < 0)
8233 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8235 gtk_tree_path_free (cursor_path);
8238 gtk_tree_path_free (cursor_path);
8240 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8241 if (start_at_focus_column)
8244 ? g_list_last (tree_view->priv->columns)
8245 : g_list_first (tree_view->priv->columns));
8246 if (tree_view->priv->focus_column)
8248 for (; list; list = (rtl ? list->prev : list->next))
8250 if (list->data == tree_view->priv->focus_column)
8257 list = (rtl ^ (count == 1)
8258 ? g_list_first (tree_view->priv->columns)
8259 : g_list_last (tree_view->priv->columns));
8264 gboolean left, right;
8266 column = list->data;
8267 if (column->visible == FALSE || !column->tabbable)
8270 pspp_sheet_view_column_cell_set_cell_data (column,
8271 tree_view->priv->model,
8276 right = list->prev ? TRUE : FALSE;
8277 left = list->next ? TRUE : FALSE;
8281 left = list->prev ? TRUE : FALSE;
8282 right = list->next ? TRUE : FALSE;
8285 if (column->tabbable
8286 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8288 tree_view->priv->focus_column = column;
8289 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8290 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8291 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8296 list = rtl ? list->prev : list->next;
8298 list = rtl ? list->next : list->prev;
8305 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8308 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8311 if (!try_move_cursor_tab (tree_view, TRUE, count))
8313 if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0)
8314 && !try_move_cursor_tab (tree_view, FALSE, count))
8315 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8318 pspp_sheet_view_clamp_column_visible (tree_view,
8319 tree_view->priv->focus_column, TRUE);
8323 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8325 PsppSheetSelectMode mode)
8329 GtkTreePath *old_path;
8331 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8334 g_return_if_fail (tree_view->priv->row_count > 0);
8336 pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8340 /* Now go forward to find the first focusable row. */
8341 path = _pspp_sheet_view_find_path (tree_view, 0);
8342 search_first_focusable_path (tree_view, &path,
8343 TRUE, &cursor_node);
8347 /* Now go backwards to find last focusable row. */
8348 path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8349 search_first_focusable_path (tree_view, &path,
8350 FALSE, &cursor_node);
8356 if (gtk_tree_path_compare (old_path, path))
8358 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
8359 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8363 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8367 gtk_tree_path_free (old_path);
8368 gtk_tree_path_free (path);
8372 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8374 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8377 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8378 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8381 pspp_sheet_selection_select_all (tree_view->priv->selection);
8387 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8389 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8392 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8393 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8396 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8402 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8403 gboolean start_editing,
8404 PsppSheetSelectMode mode)
8407 int cursor_node = -1;
8408 GtkTreePath *cursor_path = NULL;
8410 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8413 if (tree_view->priv->cursor)
8414 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8416 if (cursor_path == NULL)
8419 _pspp_sheet_view_find_node (tree_view, cursor_path,
8422 if (cursor_node < 0)
8424 gtk_tree_path_free (cursor_path);
8428 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing &&
8429 tree_view->priv->focus_column)
8431 if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8433 gtk_tree_path_free (cursor_path);
8438 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8444 /* We bail out if the original (tree, node) don't exist anymore after
8445 * handling the selection-changed callback. We do return TRUE because
8446 * the key press has been handled at this point.
8448 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8450 if (cursor_node != new_node)
8453 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8455 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8456 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8458 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8459 pspp_sheet_view_row_activated (tree_view, cursor_path,
8460 tree_view->priv->focus_column);
8462 gtk_tree_path_free (cursor_path);
8468 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8471 int cursor_node = -1;
8472 GtkTreePath *cursor_path = NULL;
8474 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8478 if (tree_view->priv->cursor)
8479 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8481 if (cursor_path == NULL)
8484 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8485 if (cursor_node < 0)
8487 gtk_tree_path_free (cursor_path);
8491 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8494 PSPP_SHEET_SELECT_MODE_TOGGLE,
8497 /* We bail out if the original (tree, node) don't exist anymore after
8498 * handling the selection-changed callback. We do return TRUE because
8499 * the key press has been handled at this point.
8501 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8503 if (cursor_node != new_node)
8506 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8508 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8509 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8510 gtk_tree_path_free (cursor_path);
8516 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8518 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8519 tree_view->priv->typeselect_flush_timeout = 0;
8524 /* Cut and paste from gtkwindow.c */
8526 send_focus_change (GtkWidget *widget,
8529 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8531 fevent->focus_change.type = GDK_FOCUS_CHANGE;
8532 fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
8533 fevent->focus_change.in = in;
8535 gtk_widget_send_focus_change (widget, fevent);
8536 gdk_event_free (fevent);
8540 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8542 GtkWidget *frame, *vbox, *toplevel;
8545 if (tree_view->priv->search_custom_entry_set)
8548 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8549 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8551 if (tree_view->priv->search_window != NULL)
8553 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8554 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8555 GTK_WINDOW (tree_view->priv->search_window));
8556 else if (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)))
8557 gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
8558 GTK_WINDOW (tree_view->priv->search_window));
8559 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8563 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8564 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8566 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8567 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8568 GTK_WINDOW (tree_view->priv->search_window));
8570 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8571 GDK_WINDOW_TYPE_HINT_UTILITY);
8572 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8573 g_signal_connect (tree_view->priv->search_window, "delete-event",
8574 G_CALLBACK (pspp_sheet_view_search_delete_event),
8576 g_signal_connect (tree_view->priv->search_window, "key-press-event",
8577 G_CALLBACK (pspp_sheet_view_search_key_press_event),
8579 g_signal_connect (tree_view->priv->search_window, "button-press-event",
8580 G_CALLBACK (pspp_sheet_view_search_button_press_event),
8582 g_signal_connect (tree_view->priv->search_window, "scroll-event",
8583 G_CALLBACK (pspp_sheet_view_search_scroll_event),
8586 frame = gtk_frame_new (NULL);
8587 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8588 gtk_widget_show (frame);
8589 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8591 vbox = gtk_vbox_new (FALSE, 0);
8592 gtk_widget_show (vbox);
8593 gtk_container_add (GTK_CONTAINER (frame), vbox);
8594 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8597 tree_view->priv->search_entry = gtk_entry_new ();
8598 gtk_widget_show (tree_view->priv->search_entry);
8599 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8600 G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8602 g_signal_connect (tree_view->priv->search_entry,
8603 "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8607 g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8609 G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8613 gtk_container_add (GTK_CONTAINER (vbox),
8614 tree_view->priv->search_entry);
8616 gtk_widget_realize (tree_view->priv->search_entry);
8619 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8620 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8623 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8624 gboolean keybinding)
8626 /* We only start interactive search if we have focus or the columns
8627 * have focus. If one of our children have focus, we don't want to
8631 gboolean found_focus = FALSE;
8632 GtkWidgetClass *entry_parent_class;
8634 if (!tree_view->priv->enable_search && !keybinding)
8637 if (tree_view->priv->search_custom_entry_set)
8640 if (tree_view->priv->search_window != NULL &&
8641 gtk_widget_get_visible (tree_view->priv->search_window))
8644 for (list = tree_view->priv->columns; list; list = list->next)
8646 PsppSheetViewColumn *column;
8648 column = list->data;
8649 if (! column->visible)
8652 if (column->button && gtk_widget_has_focus (column->button))
8659 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8665 if (tree_view->priv->search_column < 0)
8668 pspp_sheet_view_ensure_interactive_directory (tree_view);
8671 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8674 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8675 gtk_widget_show (tree_view->priv->search_window);
8676 if (tree_view->priv->search_entry_changed_id == 0)
8678 tree_view->priv->search_entry_changed_id =
8679 g_signal_connect (tree_view->priv->search_entry, "changed",
8680 G_CALLBACK (pspp_sheet_view_search_init),
8684 tree_view->priv->typeselect_flush_timeout =
8685 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8686 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8689 /* Grab focus will select all the text. We don't want that to happen, so we
8690 * call the parent instance and bypass the selection change. This is probably
8691 * really non-kosher. */
8692 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8693 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8695 /* send focus-in event */
8696 send_focus_change (tree_view->priv->search_entry, TRUE);
8698 /* search first matching iter */
8699 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8705 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8707 return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8710 /* this function returns the new width of the column being resized given
8711 * the column and x position of the cursor; the x cursor position is passed
8712 * in as a pointer and automagicly corrected if it's beyond min/max limits
8715 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8719 PsppSheetViewColumn *column;
8723 /* first translate the x position from gtk_widget_get_window (widget)
8724 * to clist->clist_window
8726 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8727 column = g_list_nth (tree_view->priv->columns, i)->data;
8728 width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8730 /* Clamp down the value */
8731 if (column->min_width == -1)
8732 width = MAX (column->button_request, width);
8734 width = MAX (column->min_width, width);
8735 if (column->max_width != -1)
8736 width = MIN (width, column->max_width);
8738 *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8744 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8748 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8749 PsppSheetView *tree_view)
8751 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8755 gdk_window_move (tree_view->priv->bin_window,
8756 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8757 TREE_VIEW_HEADER_HEIGHT (tree_view));
8758 gdk_window_move (tree_view->priv->header_window,
8759 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8761 dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8763 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8767 /* update our dy and top_row */
8768 tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8770 update_prelight (tree_view,
8771 tree_view->priv->event_last_x,
8772 tree_view->priv->event_last_y);
8774 if (!tree_view->priv->in_top_row_to_dy)
8775 pspp_sheet_view_dy_to_top_row (tree_view);
8778 update_childrens_allocation(tree_view);
8786 * pspp_sheet_view_new:
8788 * Creates a new #PsppSheetView widget.
8790 * Return value: A newly created #PsppSheetView widget.
8793 pspp_sheet_view_new (void)
8795 return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
8799 * pspp_sheet_view_new_with_model:
8800 * @model: the model.
8802 * Creates a new #PsppSheetView widget with the model initialized to @model.
8804 * Return value: A newly created #PsppSheetView widget.
8807 pspp_sheet_view_new_with_model (GtkTreeModel *model)
8809 return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
8816 * pspp_sheet_view_get_model:
8817 * @tree_view: a #PsppSheetView
8819 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
8822 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
8825 pspp_sheet_view_get_model (PsppSheetView *tree_view)
8827 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8829 return tree_view->priv->model;
8833 * pspp_sheet_view_set_model:
8834 * @tree_view: A #GtkTreeNode.
8835 * @model: (allow-none): The model.
8837 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
8838 * set, it will remove it before setting the new model. If @model is %NULL,
8839 * then it will unset the old model.
8842 pspp_sheet_view_set_model (PsppSheetView *tree_view,
8843 GtkTreeModel *model)
8845 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8846 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
8848 if (model == tree_view->priv->model)
8851 if (tree_view->priv->scroll_to_path)
8853 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8854 tree_view->priv->scroll_to_path = NULL;
8857 if (tree_view->priv->model)
8859 GList *tmplist = tree_view->priv->columns;
8861 if (tree_view->priv->selected)
8862 range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
8863 pspp_sheet_view_stop_editing (tree_view, TRUE);
8865 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8866 pspp_sheet_view_row_changed,
8868 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8869 pspp_sheet_view_row_inserted,
8871 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8872 pspp_sheet_view_row_deleted,
8874 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8875 pspp_sheet_view_rows_reordered,
8878 for (; tmplist; tmplist = tmplist->next)
8879 _pspp_sheet_view_column_unset_model (tmplist->data,
8880 tree_view->priv->model);
8882 tree_view->priv->prelight_node = -1;
8884 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
8885 tree_view->priv->drag_dest_row = NULL;
8886 gtk_tree_row_reference_free (tree_view->priv->cursor);
8887 tree_view->priv->cursor = NULL;
8888 gtk_tree_row_reference_free (tree_view->priv->anchor);
8889 tree_view->priv->anchor = NULL;
8890 gtk_tree_row_reference_free (tree_view->priv->top_row);
8891 tree_view->priv->top_row = NULL;
8892 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8893 tree_view->priv->scroll_to_path = NULL;
8895 tree_view->priv->scroll_to_column = NULL;
8897 g_object_unref (tree_view->priv->model);
8899 tree_view->priv->search_column = -1;
8900 tree_view->priv->fixed_height = -1;
8901 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
8902 tree_view->priv->last_button_x = -1;
8903 tree_view->priv->last_button_y = -1;
8906 tree_view->priv->model = model;
8908 if (tree_view->priv->model)
8912 if (tree_view->priv->search_column == -1)
8914 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
8916 GType type = gtk_tree_model_get_column_type (model, i);
8918 if (g_value_type_transformable (type, G_TYPE_STRING))
8920 tree_view->priv->search_column = i;
8926 g_object_ref (tree_view->priv->model);
8927 g_signal_connect (tree_view->priv->model,
8929 G_CALLBACK (pspp_sheet_view_row_changed),
8931 g_signal_connect (tree_view->priv->model,
8933 G_CALLBACK (pspp_sheet_view_row_inserted),
8935 g_signal_connect (tree_view->priv->model,
8937 G_CALLBACK (pspp_sheet_view_row_deleted),
8939 g_signal_connect (tree_view->priv->model,
8941 G_CALLBACK (pspp_sheet_view_rows_reordered),
8944 tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
8946 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
8947 install_presize_handler (tree_view);
8950 g_object_notify (G_OBJECT (tree_view), "model");
8952 if (tree_view->priv->selection)
8953 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
8955 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8956 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8960 * pspp_sheet_view_get_selection:
8961 * @tree_view: A #PsppSheetView.
8963 * Gets the #PsppSheetSelection associated with @tree_view.
8965 * Return value: A #PsppSheetSelection object.
8967 PsppSheetSelection *
8968 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
8970 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8972 return tree_view->priv->selection;
8976 * pspp_sheet_view_get_hadjustment:
8977 * @tree_view: A #PsppSheetView
8979 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
8981 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
8985 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
8987 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8989 return pspp_sheet_view_do_get_hadjustment (tree_view);
8992 static GtkAdjustment *
8993 pspp_sheet_view_do_get_hadjustment (PsppSheetView *tree_view)
8995 return tree_view->priv->hadjustment;
8999 * pspp_sheet_view_set_hadjustment:
9000 * @tree_view: A #PsppSheetView
9001 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9003 * Sets the #GtkAdjustment for the current horizontal aspect.
9006 pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view,
9007 GtkAdjustment *adjustment)
9009 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9011 pspp_sheet_view_set_adjustments (tree_view,
9013 tree_view->priv->vadjustment);
9015 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9019 pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
9020 GtkAdjustment *adjustment)
9022 PsppSheetViewPrivate *priv = tree_view->priv;
9024 if (adjustment && priv->hadjustment == adjustment)
9027 if (priv->hadjustment != NULL)
9029 g_signal_handlers_disconnect_by_func (priv->hadjustment,
9030 pspp_sheet_view_adjustment_changed,
9032 g_object_unref (priv->hadjustment);
9035 if (adjustment == NULL)
9036 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9039 g_signal_connect (adjustment, "value-changed",
9040 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9041 priv->hadjustment = g_object_ref_sink (adjustment);
9042 /* FIXME: Adjustment should probably be populated here with fresh values, but
9043 * internal details are too complicated for me to decipher right now.
9045 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9047 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9051 * pspp_sheet_view_get_vadjustment:
9052 * @tree_view: A #PsppSheetView
9054 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9056 * Return value: (transfer none): A #GtkAdjustment object, or %NULL
9057 * if none is currently being used.
9059 * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
9062 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9064 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9066 return pspp_sheet_view_do_get_vadjustment (tree_view);
9069 static GtkAdjustment *
9070 pspp_sheet_view_do_get_vadjustment (PsppSheetView *tree_view)
9072 return tree_view->priv->vadjustment;
9076 * pspp_sheet_view_set_vadjustment:
9077 * @tree_view: A #PsppSheetView
9078 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9080 * Sets the #GtkAdjustment for the current vertical aspect.
9082 * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
9085 pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view,
9086 GtkAdjustment *adjustment)
9088 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9089 g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
9091 pspp_sheet_view_do_set_vadjustment (tree_view, adjustment);
9095 pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
9096 GtkAdjustment *adjustment)
9098 PsppSheetViewPrivate *priv = tree_view->priv;
9100 if (adjustment && priv->vadjustment == adjustment)
9103 if (priv->vadjustment != NULL)
9105 g_signal_handlers_disconnect_by_func (priv->vadjustment,
9106 pspp_sheet_view_adjustment_changed,
9108 g_object_unref (priv->vadjustment);
9111 if (adjustment == NULL)
9112 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9115 g_signal_connect (adjustment, "value-changed",
9116 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9117 priv->vadjustment = g_object_ref_sink (adjustment);
9118 /* FIXME: Adjustment should probably be populated here with fresh values, but
9119 * internal details are too complicated for me to decipher right now.
9121 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9122 g_object_notify (G_OBJECT (tree_view), "vadjustment");
9125 /* Column and header operations */
9128 * pspp_sheet_view_get_headers_visible:
9129 * @tree_view: A #PsppSheetView.
9131 * Returns %TRUE if the headers on the @tree_view are visible.
9133 * Return value: Whether the headers are visible or not.
9136 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9138 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9140 return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9144 * pspp_sheet_view_set_headers_visible:
9145 * @tree_view: A #PsppSheetView.
9146 * @headers_visible: %TRUE if the headers are visible
9148 * Sets the visibility state of the headers.
9151 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9152 gboolean headers_visible)
9156 PsppSheetViewColumn *column;
9157 GtkAllocation allocation;
9159 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9161 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9163 headers_visible = !! headers_visible;
9165 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9168 if (headers_visible)
9169 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9171 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9173 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9175 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9176 if (headers_visible)
9178 gdk_window_move_resize (tree_view->priv->bin_window, x, y + TREE_VIEW_HEADER_HEIGHT (tree_view),
9179 tree_view->priv->width, allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view));
9181 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9182 pspp_sheet_view_map_buttons (tree_view);
9186 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9188 for (list = tree_view->priv->columns; list; list = list->next)
9190 column = list->data;
9192 gtk_widget_unmap (column->button);
9194 gdk_window_hide (tree_view->priv->header_window);
9198 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view));
9199 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2);
9200 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
9201 gtk_adjustment_set_upper (tree_view->priv->vadjustment, tree_view->priv->height);
9202 gtk_adjustment_changed (tree_view->priv->vadjustment);
9204 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9206 g_object_notify (G_OBJECT (tree_view), "headers-visible");
9210 * pspp_sheet_view_columns_autosize:
9211 * @tree_view: A #PsppSheetView.
9213 * Resizes all columns to their optimal width. Only works after the
9214 * treeview has been realized.
9217 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9219 gboolean dirty = FALSE;
9221 PsppSheetViewColumn *column;
9223 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9225 for (list = tree_view->priv->columns; list; list = list->next)
9227 column = list->data;
9228 _pspp_sheet_view_column_cell_set_dirty (column);
9233 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9237 * pspp_sheet_view_set_headers_clickable:
9238 * @tree_view: A #PsppSheetView.
9239 * @setting: %TRUE if the columns are clickable.
9241 * Allow the column title buttons to be clicked.
9244 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9249 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9251 for (list = tree_view->priv->columns; list; list = list->next)
9252 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9254 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9259 * pspp_sheet_view_get_headers_clickable:
9260 * @tree_view: A #PsppSheetView.
9262 * Returns whether all header columns are clickable.
9264 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9269 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9273 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9275 for (list = tree_view->priv->columns; list; list = list->next)
9276 if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9283 * pspp_sheet_view_set_rules_hint
9284 * @tree_view: a #PsppSheetView
9285 * @setting: %TRUE if the tree requires reading across rows
9287 * This function tells GTK+ that the user interface for your
9288 * application requires users to read across tree rows and associate
9289 * cells with one another. By default, GTK+ will then render the tree
9290 * with alternating row colors. Do <emphasis>not</emphasis> use it
9291 * just because you prefer the appearance of the ruled tree; that's a
9292 * question for the theme. Some themes will draw tree rows in
9293 * alternating colors even when rules are turned off, and users who
9294 * prefer that appearance all the time can choose those themes. You
9295 * should call this function only as a <emphasis>semantic</emphasis>
9296 * hint to the theme engine that your tree makes alternating colors
9297 * useful from a functional standpoint (since it has lots of columns,
9302 pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view,
9305 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9307 setting = setting != FALSE;
9309 if (tree_view->priv->has_rules != setting)
9311 tree_view->priv->has_rules = setting;
9312 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9315 g_object_notify (G_OBJECT (tree_view), "rules-hint");
9319 * pspp_sheet_view_get_rules_hint
9320 * @tree_view: a #PsppSheetView
9322 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9324 * Return value: %TRUE if rules are useful for the user of this tree
9327 pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view)
9329 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9331 return tree_view->priv->has_rules;
9334 /* Public Column functions
9338 * pspp_sheet_view_append_column:
9339 * @tree_view: A #PsppSheetView.
9340 * @column: The #PsppSheetViewColumn to add.
9342 * Appends @column to the list of columns.
9344 * Return value: The number of columns in @tree_view after appending.
9347 pspp_sheet_view_append_column (PsppSheetView *tree_view,
9348 PsppSheetViewColumn *column)
9350 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9351 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9352 g_return_val_if_fail (column->tree_view == NULL, -1);
9354 return pspp_sheet_view_insert_column (tree_view, column, -1);
9359 * pspp_sheet_view_remove_column:
9360 * @tree_view: A #PsppSheetView.
9361 * @column: The #PsppSheetViewColumn to remove.
9363 * Removes @column from @tree_view.
9365 * Return value: The number of columns in @tree_view after removing.
9368 pspp_sheet_view_remove_column (PsppSheetView *tree_view,
9369 PsppSheetViewColumn *column)
9371 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9372 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9373 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9375 if (tree_view->priv->focus_column == column)
9376 tree_view->priv->focus_column = NULL;
9378 if (tree_view->priv->edited_column == column)
9380 pspp_sheet_view_stop_editing (tree_view, TRUE);
9382 /* no need to, but just to be sure ... */
9383 tree_view->priv->edited_column = NULL;
9386 _pspp_sheet_view_column_unset_tree_view (column);
9388 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9389 tree_view->priv->n_columns--;
9391 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9395 _pspp_sheet_view_column_unrealize_button (column);
9396 for (list = tree_view->priv->columns; list; list = list->next)
9398 PsppSheetViewColumn *tmp_column;
9400 tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9401 if (tmp_column->visible)
9402 _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9405 if (tree_view->priv->n_columns == 0 &&
9406 pspp_sheet_view_get_headers_visible (tree_view) &&
9407 tree_view->priv->header_window)
9408 gdk_window_hide (tree_view->priv->header_window);
9410 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9413 g_object_unref (column);
9414 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9416 return tree_view->priv->n_columns;
9420 * pspp_sheet_view_insert_column:
9421 * @tree_view: A #PsppSheetView.
9422 * @column: The #PsppSheetViewColumn to be inserted.
9423 * @position: The position to insert @column in.
9425 * This inserts the @column into the @tree_view at @position. If @position is
9426 * -1, then the column is inserted at the end.
9428 * Return value: The number of columns in @tree_view after insertion.
9431 pspp_sheet_view_insert_column (PsppSheetView *tree_view,
9432 PsppSheetViewColumn *column,
9435 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9436 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9437 g_return_val_if_fail (column->tree_view == NULL, -1);
9439 g_object_ref_sink (column);
9441 if (tree_view->priv->n_columns == 0 &&
9442 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9443 pspp_sheet_view_get_headers_visible (tree_view))
9445 gdk_window_show (tree_view->priv->header_window);
9448 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9450 tree_view->priv->n_columns++;
9452 _pspp_sheet_view_column_set_tree_view (column, tree_view);
9454 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9458 _pspp_sheet_view_column_realize_button (column);
9460 for (list = tree_view->priv->columns; list; list = list->next)
9462 column = PSPP_SHEET_VIEW_COLUMN (list->data);
9463 if (column->visible)
9464 _pspp_sheet_view_column_cell_set_dirty (column);
9466 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9469 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9471 return tree_view->priv->n_columns;
9475 * pspp_sheet_view_insert_column_with_attributes:
9476 * @tree_view: A #PsppSheetView
9477 * @position: The position to insert the new column in.
9478 * @title: The title to set the header to.
9479 * @cell: The #GtkCellRenderer.
9480 * @Varargs: A %NULL-terminated list of attributes.
9482 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9483 * @position. If @position is -1, then the newly created column is inserted at
9484 * the end. The column is initialized with the attributes given.
9486 * Return value: The number of columns in @tree_view after insertion.
9489 pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view,
9492 GtkCellRenderer *cell,
9495 PsppSheetViewColumn *column;
9500 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9502 column = pspp_sheet_view_column_new ();
9503 pspp_sheet_view_column_set_title (column, title);
9504 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9506 va_start (args, cell);
9508 attribute = va_arg (args, gchar *);
9510 while (attribute != NULL)
9512 column_id = va_arg (args, gint);
9513 pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9514 attribute = va_arg (args, gchar *);
9519 pspp_sheet_view_insert_column (tree_view, column, position);
9521 return tree_view->priv->n_columns;
9525 * pspp_sheet_view_insert_column_with_data_func:
9526 * @tree_view: a #PsppSheetView
9527 * @position: Position to insert, -1 for append
9528 * @title: column title
9529 * @cell: cell renderer for column
9530 * @func: function to set attributes of cell renderer
9531 * @data: data for @func
9532 * @dnotify: destroy notifier for @data
9534 * Convenience function that inserts a new column into the #PsppSheetView
9535 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9536 * attributes (normally using data from the model). See also
9537 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9539 * Return value: number of columns in the tree view post-insert
9542 pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view,
9545 GtkCellRenderer *cell,
9546 PsppSheetCellDataFunc func,
9548 GDestroyNotify dnotify)
9550 PsppSheetViewColumn *column;
9552 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9554 column = pspp_sheet_view_column_new ();
9555 pspp_sheet_view_column_set_title (column, title);
9556 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9557 pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9559 pspp_sheet_view_insert_column (tree_view, column, position);
9561 return tree_view->priv->n_columns;
9565 * pspp_sheet_view_get_column:
9566 * @tree_view: A #PsppSheetView.
9567 * @n: The position of the column, counting from 0.
9569 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9571 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9574 PsppSheetViewColumn *
9575 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9578 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9580 if (n < 0 || n >= tree_view->priv->n_columns)
9583 if (tree_view->priv->columns == NULL)
9586 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9590 * pspp_sheet_view_get_columns:
9591 * @tree_view: A #PsppSheetView
9593 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9594 * The returned list must be freed with g_list_free ().
9596 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9599 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9601 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9603 return g_list_copy (tree_view->priv->columns);
9607 * pspp_sheet_view_move_column_after:
9608 * @tree_view: A #PsppSheetView
9609 * @column: The #PsppSheetViewColumn to be moved.
9610 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9612 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9613 * @column is placed in the first position.
9616 pspp_sheet_view_move_column_after (PsppSheetView *tree_view,
9617 PsppSheetViewColumn *column,
9618 PsppSheetViewColumn *base_column)
9620 GList *column_list_el, *base_el = NULL;
9622 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9624 column_list_el = g_list_find (tree_view->priv->columns, column);
9625 g_return_if_fail (column_list_el != NULL);
9629 base_el = g_list_find (tree_view->priv->columns, base_column);
9630 g_return_if_fail (base_el != NULL);
9633 if (column_list_el->prev == base_el)
9636 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9637 if (base_el == NULL)
9639 column_list_el->prev = NULL;
9640 column_list_el->next = tree_view->priv->columns;
9641 if (column_list_el->next)
9642 column_list_el->next->prev = column_list_el;
9643 tree_view->priv->columns = column_list_el;
9647 column_list_el->prev = base_el;
9648 column_list_el->next = base_el->next;
9649 if (column_list_el->next)
9650 column_list_el->next->prev = column_list_el;
9651 base_el->next = column_list_el;
9654 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9656 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9657 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9660 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9664 * pspp_sheet_view_set_column_drag_function:
9665 * @tree_view: A #PsppSheetView.
9666 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9667 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9668 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9670 * Sets a user function for determining where a column may be dropped when
9671 * dragged. This function is called on every column pair in turn at the
9672 * beginning of a column drag to determine where a drop can take place. The
9673 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9674 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9675 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9676 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9677 * @tree_view reverts to the default behavior of allowing all columns to be
9678 * dropped everywhere.
9681 pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view,
9682 PsppSheetViewColumnDropFunc func,
9684 GDestroyNotify destroy)
9686 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9688 if (tree_view->priv->column_drop_func_data_destroy)
9689 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9691 tree_view->priv->column_drop_func = func;
9692 tree_view->priv->column_drop_func_data = user_data;
9693 tree_view->priv->column_drop_func_data_destroy = destroy;
9697 * pspp_sheet_view_scroll_to_point:
9698 * @tree_view: a #PsppSheetView
9699 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9700 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9702 * Scrolls the tree view such that the top-left corner of the visible
9703 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9704 * in tree coordinates. The @tree_view must be realized before
9705 * this function is called. If it isn't, you probably want to be
9706 * using pspp_sheet_view_scroll_to_cell().
9708 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9711 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9715 GtkAdjustment *hadj;
9716 GtkAdjustment *vadj;
9718 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9719 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9721 hadj = tree_view->priv->hadjustment;
9722 vadj = tree_view->priv->vadjustment;
9725 gtk_adjustment_set_value (hadj, CLAMP (tree_x, gtk_adjustment_get_lower (hadj), gtk_adjustment_get_upper (hadj) - gtk_adjustment_get_page_size (hadj)));
9727 gtk_adjustment_set_value (vadj, CLAMP (tree_y, gtk_adjustment_get_lower (vadj), gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj)));
9731 * pspp_sheet_view_scroll_to_cell:
9732 * @tree_view: A #PsppSheetView.
9733 * @path: (allow-none): The path of the row to move to, or %NULL.
9734 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9735 * @use_align: whether to use alignment arguments, or %FALSE.
9736 * @row_align: The vertical alignment of the row specified by @path.
9737 * @col_align: The horizontal alignment of the column specified by @column.
9739 * Moves the alignments of @tree_view to the position specified by @column and
9740 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9741 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9742 * or @path need to be non-%NULL. @row_align determines where the row is
9743 * placed, and @col_align determines where @column is placed. Both are expected
9744 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9745 * right/bottom alignment, 0.5 means center.
9747 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9748 * tree does the minimum amount of work to scroll the cell onto the screen.
9749 * This means that the cell will be scrolled to the edge closest to its current
9750 * position. If the cell is currently visible on the screen, nothing is done.
9752 * This function only works if the model is set, and @path is a valid row on the
9753 * model. If the model changes before the @tree_view is realized, the centered
9754 * path will be modified to reflect this change.
9757 pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view,
9759 PsppSheetViewColumn *column,
9764 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9765 g_return_if_fail (tree_view->priv->model != NULL);
9766 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9767 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9768 g_return_if_fail (path != NULL || column != NULL);
9771 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9772 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9774 row_align = CLAMP (row_align, 0.0, 1.0);
9775 col_align = CLAMP (col_align, 0.0, 1.0);
9778 /* Note: Despite the benefits that come from having one code path for the
9779 * scrolling code, we short-circuit validate_visible_area's immplementation as
9780 * it is much slower than just going to the point.
9782 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9783 !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9784 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9786 if (tree_view->priv->scroll_to_path)
9787 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9789 tree_view->priv->scroll_to_path = NULL;
9790 tree_view->priv->scroll_to_column = NULL;
9793 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
9795 tree_view->priv->scroll_to_column = column;
9796 tree_view->priv->scroll_to_use_align = use_align;
9797 tree_view->priv->scroll_to_row_align = row_align;
9798 tree_view->priv->scroll_to_col_align = col_align;
9800 install_presize_handler (tree_view);
9804 GdkRectangle cell_rect;
9805 GdkRectangle vis_rect;
9806 gint dest_x, dest_y;
9808 pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
9809 pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
9811 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
9813 dest_x = vis_rect.x;
9814 dest_y = vis_rect.y;
9820 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
9824 if (cell_rect.x < vis_rect.x)
9825 dest_x = cell_rect.x;
9826 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
9827 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
9835 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
9836 dest_y = MAX (dest_y, 0);
9840 if (cell_rect.y < vis_rect.y)
9841 dest_y = cell_rect.y;
9842 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
9843 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
9847 pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
9852 * pspp_sheet_view_row_activated:
9853 * @tree_view: A #PsppSheetView
9854 * @path: The #GtkTreePath to be activated.
9855 * @column: The #PsppSheetViewColumn to be activated.
9857 * Activates the cell determined by @path and @column.
9860 pspp_sheet_view_row_activated (PsppSheetView *tree_view,
9862 PsppSheetViewColumn *column)
9864 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9866 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
9871 * pspp_sheet_view_get_reorderable:
9872 * @tree_view: a #PsppSheetView
9874 * Retrieves whether the user can reorder the tree via drag-and-drop. See
9875 * pspp_sheet_view_set_reorderable().
9877 * Return value: %TRUE if the tree can be reordered.
9880 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
9882 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9884 return tree_view->priv->reorderable;
9888 * pspp_sheet_view_set_reorderable:
9889 * @tree_view: A #PsppSheetView.
9890 * @reorderable: %TRUE, if the tree can be reordered.
9892 * This function is a convenience function to allow you to reorder
9893 * models that support the #GtkDragSourceIface and the
9894 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
9895 * these. If @reorderable is %TRUE, then the user can reorder the
9896 * model by dragging and dropping rows. The developer can listen to
9897 * these changes by connecting to the model's row_inserted and
9898 * row_deleted signals. The reordering is implemented by setting up
9899 * the tree view as a drag source and destination. Therefore, drag and
9900 * drop can not be used in a reorderable view for any other purpose.
9902 * This function does not give you any degree of control over the order -- any
9903 * reordering is allowed. If more control is needed, you should probably
9904 * handle drag and drop manually.
9907 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
9908 gboolean reorderable)
9910 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9912 reorderable = reorderable != FALSE;
9914 if (tree_view->priv->reorderable == reorderable)
9919 const GtkTargetEntry row_targets[] = {
9920 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
9923 pspp_sheet_view_enable_model_drag_source (tree_view,
9926 G_N_ELEMENTS (row_targets),
9928 pspp_sheet_view_enable_model_drag_dest (tree_view,
9930 G_N_ELEMENTS (row_targets),
9935 pspp_sheet_view_unset_rows_drag_source (tree_view);
9936 pspp_sheet_view_unset_rows_drag_dest (tree_view);
9939 tree_view->priv->reorderable = reorderable;
9941 g_object_notify (G_OBJECT (tree_view), "reorderable");
9944 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
9945 is pressed, other rows will be unselected.
9947 If CLAMP_NODE is true, then the sheetview will scroll to make the row
9950 pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
9952 gboolean clear_and_select,
9953 gboolean clamp_node,
9954 PsppSheetSelectMode mode)
9958 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9960 GtkTreePath *cursor_path;
9961 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9962 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
9963 gtk_tree_path_free (cursor_path);
9966 gtk_tree_row_reference_free (tree_view->priv->cursor);
9967 tree_view->priv->cursor = NULL;
9969 _pspp_sheet_view_find_node (tree_view, path, &node);
9970 tree_view->priv->cursor =
9971 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
9972 tree_view->priv->model,
9975 if (tree_view->priv->row_count > 0)
9979 if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
9980 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
9984 /* We have to re-find tree and node here again, somebody might have
9985 * cleared the node or the whole tree in the PsppSheetSelection::changed
9986 * callback. If the nodes differ we bail out here.
9988 _pspp_sheet_view_find_node (tree_view, path, &new_node);
9990 if (node != new_node)
9995 pspp_sheet_view_clamp_node_visible (tree_view, node);
9996 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
10000 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
10004 * pspp_sheet_view_get_cursor:
10005 * @tree_view: A #PsppSheetView
10006 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
10007 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
10009 * Fills in @path and @focus_column with the current path and focus column. If
10010 * the cursor isn't currently set, then *@path will be %NULL. If no column
10011 * currently has focus, then *@focus_column will be %NULL.
10013 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
10014 * you are done with it.
10017 pspp_sheet_view_get_cursor (PsppSheetView *tree_view,
10018 GtkTreePath **path,
10019 PsppSheetViewColumn **focus_column)
10021 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10025 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10026 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10033 *focus_column = tree_view->priv->focus_column;
10038 * pspp_sheet_view_set_cursor:
10039 * @tree_view: A #PsppSheetView
10040 * @path: A #GtkTreePath
10041 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10042 * @start_editing: %TRUE if the specified cell should start being edited.
10044 * Sets the current keyboard focus to be at @path, and selects it. This is
10045 * useful when you want to focus the user's attention on a particular row. If
10046 * @focus_column is not %NULL, then focus is given to the column specified by
10047 * it. Additionally, if @focus_column is specified, and @start_editing is
10048 * %TRUE, then editing should be started in the specified cell.
10049 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10050 * in order to give keyboard focus to the widget. Please note that editing
10051 * can only happen when the widget is realized.
10053 * If @path is invalid for @model, the current cursor (if any) will be unset
10054 * and the function will return without failing.
10057 pspp_sheet_view_set_cursor (PsppSheetView *tree_view,
10059 PsppSheetViewColumn *focus_column,
10060 gboolean start_editing)
10062 pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10063 NULL, start_editing);
10067 * pspp_sheet_view_set_cursor_on_cell:
10068 * @tree_view: A #PsppSheetView
10069 * @path: A #GtkTreePath
10070 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10071 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10072 * @start_editing: %TRUE if the specified cell should start being edited.
10074 * Sets the current keyboard focus to be at @path, and selects it. This is
10075 * useful when you want to focus the user's attention on a particular row. If
10076 * @focus_column is not %NULL, then focus is given to the column specified by
10077 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10078 * contains 2 or more editable or activatable cells, then focus is given to
10079 * the cell specified by @focus_cell. Additionally, if @focus_column is
10080 * specified, and @start_editing is %TRUE, then editing should be started in
10081 * the specified cell. This function is often followed by
10082 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10083 * widget. Please note that editing can only happen when the widget is
10086 * If @path is invalid for @model, the current cursor (if any) will be unset
10087 * and the function will return without failing.
10092 pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view,
10094 PsppSheetViewColumn *focus_column,
10095 GtkCellRenderer *focus_cell,
10096 gboolean start_editing)
10098 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10099 g_return_if_fail (path != NULL);
10100 g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10102 if (!tree_view->priv->model)
10107 g_return_if_fail (focus_column);
10108 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10111 /* cancel the current editing, if it exists */
10112 if (tree_view->priv->edited_column &&
10113 tree_view->priv->edited_column->editable_widget)
10114 pspp_sheet_view_stop_editing (tree_view, TRUE);
10116 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
10118 if (focus_column && focus_column->visible)
10121 gboolean column_in_tree = FALSE;
10123 for (list = tree_view->priv->columns; list; list = list->next)
10124 if (list->data == focus_column)
10126 column_in_tree = TRUE;
10129 g_return_if_fail (column_in_tree);
10130 tree_view->priv->focus_column = focus_column;
10132 pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10134 pspp_sheet_view_start_editing (tree_view, path);
10136 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10137 pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10143 * pspp_sheet_view_get_bin_window:
10144 * @tree_view: A #PsppSheetView
10146 * Returns the window that @tree_view renders to. This is used primarily to
10147 * compare to <literal>event->window</literal> to confirm that the event on
10148 * @tree_view is on the right window.
10150 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10153 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10155 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10157 return tree_view->priv->bin_window;
10161 * pspp_sheet_view_get_path_at_pos:
10162 * @tree_view: A #PsppSheetView.
10163 * @x: The x position to be identified (relative to bin_window).
10164 * @y: The y position to be identified (relative to bin_window).
10165 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10166 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10167 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10168 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10170 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10171 * (please see pspp_sheet_view_get_bin_window()).
10172 * That is, @x and @y are relative to an events coordinates. @x and @y must
10173 * come from an event on the @tree_view only where <literal>event->window ==
10174 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10175 * things like popup menus. If @path is non-%NULL, then it will be filled
10176 * with the #GtkTreePath at that point. This path should be freed with
10177 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10178 * with the column at that point. @cell_x and @cell_y return the coordinates
10179 * relative to the cell background (i.e. the @background_area passed to
10180 * gtk_cell_renderer_render()). This function is only meaningful if
10181 * @tree_view is realized. Therefore this function will always return %FALSE
10182 * if @tree_view is not realized or does not have a model.
10184 * For converting widget coordinates (eg. the ones you get from
10185 * GtkWidget::query-tooltip), please see
10186 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10188 * Return value: %TRUE if a row exists at that coordinate.
10191 pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view,
10194 GtkTreePath **path,
10195 PsppSheetViewColumn **column,
10202 g_return_val_if_fail (tree_view != NULL, FALSE);
10209 if (tree_view->priv->bin_window == NULL)
10212 if (tree_view->priv->row_count == 0)
10215 if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment))
10218 if (x < 0 || y < 0)
10221 if (column || cell_x)
10223 PsppSheetViewColumn *tmp_column;
10224 PsppSheetViewColumn *last_column = NULL;
10226 gint remaining_x = x;
10227 gboolean found = FALSE;
10230 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10231 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10233 list = (rtl ? list->prev : list->next))
10235 tmp_column = list->data;
10237 if (tmp_column->visible == FALSE)
10240 last_column = tmp_column;
10241 if (remaining_x <= tmp_column->width)
10246 *column = tmp_column;
10249 *cell_x = remaining_x;
10253 remaining_x -= tmp_column->width;
10256 /* If found is FALSE and there is a last_column, then it the remainder
10257 * space is in that area
10264 *column = last_column;
10267 *cell_x = last_column->width + remaining_x;
10276 y_offset = pspp_sheet_view_find_offset (tree_view,
10277 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10284 *cell_y = y_offset;
10287 *path = _pspp_sheet_view_find_path (tree_view, node);
10292 /* Computes 'cell_area' from 'background_area', which must be the background
10293 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10294 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10295 the cell area as passed to _pspp_sheet_view_column_cell_render().
10297 'column' is required to properly adjust 'cell_area->x' and
10298 'cell_area->width'. It may be set to NULL if these values are not of
10299 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10302 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
10303 PsppSheetViewColumn *column,
10304 const GdkRectangle *background_area,
10305 gboolean subtract_focus_rect,
10306 GdkRectangle *cell_area)
10308 gint vertical_separator;
10309 gint horizontal_separator;
10311 *cell_area = *background_area;
10313 gtk_widget_style_get (GTK_WIDGET (tree_view),
10314 "vertical-separator", &vertical_separator,
10315 "horizontal-separator", &horizontal_separator,
10317 cell_area->x += horizontal_separator / 2;
10318 cell_area->y += vertical_separator / 2;
10319 cell_area->width -= horizontal_separator;
10320 cell_area->height -= vertical_separator;
10322 if (subtract_focus_rect)
10324 int focus_line_width;
10326 gtk_widget_style_get (GTK_WIDGET (tree_view),
10327 "focus-line-width", &focus_line_width,
10329 cell_area->x += focus_line_width;
10330 cell_area->y += focus_line_width;
10331 cell_area->width -= 2 * focus_line_width;
10332 cell_area->height -= 2 * focus_line_width;
10335 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10337 gint grid_line_width;
10338 gtk_widget_style_get (GTK_WIDGET (tree_view),
10339 "grid-line-width", &grid_line_width,
10342 if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10343 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10346 PsppSheetViewColumn *first_column, *last_column;
10349 /* Find the last visible column. */
10350 last_column = NULL;
10351 for (list = g_list_last (tree_view->priv->columns);
10355 PsppSheetViewColumn *c = list->data;
10363 /* Find the first visible column. */
10364 first_column = NULL;
10365 for (list = g_list_first (tree_view->priv->columns);
10369 PsppSheetViewColumn *c = list->data;
10377 if (column == first_column)
10379 cell_area->width -= grid_line_width / 2;
10381 else if (column == last_column)
10383 cell_area->x += grid_line_width / 2;
10384 cell_area->width -= grid_line_width / 2;
10388 cell_area->x += grid_line_width / 2;
10389 cell_area->width -= grid_line_width;
10393 if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10394 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10396 cell_area->y += grid_line_width / 2;
10397 cell_area->height -= grid_line_width;
10401 if (column == NULL)
10404 cell_area->width = 0;
10409 * pspp_sheet_view_get_cell_area:
10410 * @tree_view: a #PsppSheetView
10411 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10412 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10413 * @rect: rectangle to fill with cell rect
10415 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10416 * row specified by @path and the column specified by @column. If @path is
10417 * %NULL, or points to a path not currently displayed, the @y and @height fields
10418 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10419 * fields will be filled with 0. The sum of all cell rects does not cover the
10420 * entire tree; there are extra pixels in between rows, for example. The
10421 * returned rectangle is equivalent to the @cell_area passed to
10422 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10426 pspp_sheet_view_get_cell_area (PsppSheetView *tree_view,
10428 PsppSheetViewColumn *column,
10429 GdkRectangle *rect)
10431 GdkRectangle background_area;
10433 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10434 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10435 g_return_if_fail (rect != NULL);
10436 g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10437 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10439 pspp_sheet_view_get_background_area (tree_view, path, column,
10441 pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10446 * pspp_sheet_view_get_background_area:
10447 * @tree_view: a #PsppSheetView
10448 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10449 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10450 * @rect: rectangle to fill with cell background rect
10452 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10453 * row specified by @path and the column specified by @column. If @path is
10454 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10455 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10456 * fields will be filled with 0. The returned rectangle is equivalent to the
10457 * @background_area passed to gtk_cell_renderer_render(). These background
10458 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10459 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10460 * itself, excluding surrounding borders.
10464 pspp_sheet_view_get_background_area (PsppSheetView *tree_view,
10466 PsppSheetViewColumn *column,
10467 GdkRectangle *rect)
10471 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10472 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10473 g_return_if_fail (rect != NULL);
10482 /* Get vertical coords */
10484 _pspp_sheet_view_find_node (tree_view, path, &node);
10488 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10490 rect->height = ROW_HEIGHT (tree_view);
10497 pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10498 rect->width = x2 - rect->x;
10503 * pspp_sheet_view_get_visible_rect:
10504 * @tree_view: a #PsppSheetView
10505 * @visible_rect: rectangle to fill
10507 * Fills @visible_rect with the currently-visible region of the
10508 * buffer, in tree coordinates. Convert to bin_window coordinates with
10509 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10510 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10511 * scrollable area of the tree.
10514 pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view,
10515 GdkRectangle *visible_rect)
10519 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10521 widget = GTK_WIDGET (tree_view);
10525 GtkAllocation allocation;
10526 gtk_widget_get_allocation (widget, &allocation);
10527 visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
10528 visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
10529 visible_rect->width = allocation.width;
10530 visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10535 * pspp_sheet_view_widget_to_tree_coords:
10536 * @tree_view: a #PsppSheetView
10537 * @wx: X coordinate relative to bin_window
10538 * @wy: Y coordinate relative to bin_window
10539 * @tx: return location for tree X coordinate
10540 * @ty: return location for tree Y coordinate
10542 * Converts bin_window coordinates to coordinates for the
10543 * tree (the full scrollable area of the tree).
10545 * Deprecated: 2.12: Due to historial reasons the name of this function is
10546 * incorrect. For converting coordinates relative to the widget to
10547 * bin_window coordinates, please see
10548 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10552 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10558 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10561 *tx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10563 *ty = wy + tree_view->priv->dy;
10567 * pspp_sheet_view_tree_to_widget_coords:
10568 * @tree_view: a #PsppSheetView
10569 * @tx: tree X coordinate
10570 * @ty: tree Y coordinate
10571 * @wx: return location for X coordinate relative to bin_window
10572 * @wy: return location for Y coordinate relative to bin_window
10574 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10575 * to bin_window coordinates.
10577 * Deprecated: 2.12: Due to historial reasons the name of this function is
10578 * incorrect. For converting bin_window coordinates to coordinates relative
10579 * to bin_window, please see
10580 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10584 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10590 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10593 *wx = tx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10595 *wy = ty - tree_view->priv->dy;
10600 * pspp_sheet_view_convert_widget_to_tree_coords:
10601 * @tree_view: a #PsppSheetView
10602 * @wx: X coordinate relative to the widget
10603 * @wy: Y coordinate relative to the widget
10604 * @tx: return location for tree X coordinate
10605 * @ty: return location for tree Y coordinate
10607 * Converts widget coordinates to coordinates for the
10608 * tree (the full scrollable area of the tree).
10613 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10621 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10623 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10626 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10632 * pspp_sheet_view_convert_tree_to_widget_coords:
10633 * @tree_view: a #PsppSheetView
10634 * @tx: X coordinate relative to the tree
10635 * @ty: Y coordinate relative to the tree
10636 * @wx: return location for widget X coordinate
10637 * @wy: return location for widget Y coordinate
10639 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10640 * to widget coordinates.
10645 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10653 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10655 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10658 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10664 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10665 * @tree_view: a #PsppSheetView
10666 * @wx: X coordinate relative to the widget
10667 * @wy: Y coordinate relative to the widget
10668 * @bx: return location for bin_window X coordinate
10669 * @by: return location for bin_window Y coordinate
10671 * Converts widget coordinates to coordinates for the bin_window
10672 * (see pspp_sheet_view_get_bin_window()).
10677 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10683 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10686 *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10688 *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10692 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10693 * @tree_view: a #PsppSheetView
10694 * @bx: bin_window X coordinate
10695 * @by: bin_window Y coordinate
10696 * @wx: return location for widget X coordinate
10697 * @wy: return location for widget Y coordinate
10699 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10700 * to widget relative coordinates.
10705 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10711 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10714 *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10716 *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10720 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10721 * @tree_view: a #PsppSheetView
10722 * @tx: tree X coordinate
10723 * @ty: tree Y coordinate
10724 * @bx: return location for X coordinate relative to bin_window
10725 * @by: return location for Y coordinate relative to bin_window
10727 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10728 * to bin_window coordinates.
10733 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10739 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10744 *by = ty - tree_view->priv->dy;
10748 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10749 * @tree_view: a #PsppSheetView
10750 * @bx: X coordinate relative to bin_window
10751 * @by: Y coordinate relative to bin_window
10752 * @tx: return location for tree X coordinate
10753 * @ty: return location for tree Y coordinate
10755 * Converts bin_window coordinates to coordinates for the
10756 * tree (the full scrollable area of the tree).
10761 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10767 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10772 *ty = by + tree_view->priv->dy;
10778 * pspp_sheet_view_get_visible_range:
10779 * @tree_view: A #PsppSheetView
10780 * @start_path: (allow-none): Return location for start of region, or %NULL.
10781 * @end_path: (allow-none): Return location for end of region, or %NULL.
10783 * Sets @start_path and @end_path to be the first and last visible path.
10784 * Note that there may be invisible paths in between.
10786 * The paths should be freed with gtk_tree_path_free() after use.
10788 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
10793 pspp_sheet_view_get_visible_range (PsppSheetView *tree_view,
10794 GtkTreePath **start_path,
10795 GtkTreePath **end_path)
10800 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10802 if (!tree_view->priv->row_count)
10809 pspp_sheet_view_find_offset (tree_view,
10810 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
10813 *start_path = _pspp_sheet_view_find_path (tree_view, node);
10822 if (tree_view->priv->height < gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
10823 y = tree_view->priv->height - 1;
10825 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1;
10827 pspp_sheet_view_find_offset (tree_view, y, &node);
10829 *end_path = _pspp_sheet_view_find_path (tree_view, node);
10838 unset_reorderable (PsppSheetView *tree_view)
10840 if (tree_view->priv->reorderable)
10842 tree_view->priv->reorderable = FALSE;
10843 g_object_notify (G_OBJECT (tree_view), "reorderable");
10848 * pspp_sheet_view_enable_model_drag_source:
10849 * @tree_view: a #PsppSheetView
10850 * @start_button_mask: Mask of allowed buttons to start drag
10851 * @targets: the table of targets that the drag will support
10852 * @n_targets: the number of items in @targets
10853 * @actions: the bitmask of possible actions for a drag from this
10856 * Turns @tree_view into a drag source for automatic DND. Calling this
10857 * method sets #PsppSheetView:reorderable to %FALSE.
10860 pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view,
10861 GdkModifierType start_button_mask,
10862 const GtkTargetEntry *targets,
10864 GdkDragAction actions)
10866 TreeViewDragInfo *di;
10868 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10870 gtk_drag_source_set (GTK_WIDGET (tree_view),
10876 di = ensure_info (tree_view);
10878 di->start_button_mask = start_button_mask;
10879 di->source_actions = actions;
10880 di->source_set = TRUE;
10882 unset_reorderable (tree_view);
10886 * pspp_sheet_view_enable_model_drag_dest:
10887 * @tree_view: a #PsppSheetView
10888 * @targets: the table of targets that the drag will support
10889 * @n_targets: the number of items in @targets
10890 * @actions: the bitmask of possible actions for a drag from this
10893 * Turns @tree_view into a drop destination for automatic DND. Calling
10894 * this method sets #PsppSheetView:reorderable to %FALSE.
10897 pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view,
10898 const GtkTargetEntry *targets,
10900 GdkDragAction actions)
10902 TreeViewDragInfo *di;
10904 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10906 gtk_drag_dest_set (GTK_WIDGET (tree_view),
10912 di = ensure_info (tree_view);
10913 di->dest_set = TRUE;
10915 unset_reorderable (tree_view);
10919 * pspp_sheet_view_unset_rows_drag_source:
10920 * @tree_view: a #PsppSheetView
10922 * Undoes the effect of
10923 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
10924 * #PsppSheetView:reorderable to %FALSE.
10927 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
10929 TreeViewDragInfo *di;
10931 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10933 di = get_info (tree_view);
10937 if (di->source_set)
10939 gtk_drag_source_unset (GTK_WIDGET (tree_view));
10940 di->source_set = FALSE;
10943 if (!di->dest_set && !di->source_set)
10944 remove_info (tree_view);
10947 unset_reorderable (tree_view);
10951 * pspp_sheet_view_unset_rows_drag_dest:
10952 * @tree_view: a #PsppSheetView
10954 * Undoes the effect of
10955 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
10956 * #PsppSheetView:reorderable to %FALSE.
10959 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
10961 TreeViewDragInfo *di;
10963 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10965 di = get_info (tree_view);
10971 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
10972 di->dest_set = FALSE;
10975 if (!di->dest_set && !di->source_set)
10976 remove_info (tree_view);
10979 unset_reorderable (tree_view);
10983 * pspp_sheet_view_set_drag_dest_row:
10984 * @tree_view: a #PsppSheetView
10985 * @path: (allow-none): The path of the row to highlight, or %NULL.
10986 * @pos: Specifies whether to drop before, after or into the row
10988 * Sets the row that is highlighted for feedback.
10991 pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view,
10993 PsppSheetViewDropPosition pos)
10995 GtkTreePath *current_dest;
10997 /* Note; this function is exported to allow a custom DND
10998 * implementation, so it can't touch TreeViewDragInfo
11001 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11003 current_dest = NULL;
11005 if (tree_view->priv->drag_dest_row)
11007 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11008 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
11011 /* special case a drop on an empty model */
11012 tree_view->priv->empty_view_drop = 0;
11014 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
11015 && gtk_tree_path_get_depth (path) == 1
11016 && gtk_tree_path_get_indices (path)[0] == 0)
11020 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
11024 tree_view->priv->empty_view_drop = 1;
11027 tree_view->priv->drag_dest_pos = pos;
11031 tree_view->priv->drag_dest_row =
11032 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
11033 pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
11036 tree_view->priv->drag_dest_row = NULL;
11040 int node, new_node;
11042 _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11043 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11047 new_node = pspp_sheet_view_node_next (tree_view, node);
11049 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11051 new_node = pspp_sheet_view_node_prev (tree_view, node);
11053 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11055 gtk_tree_path_free (current_dest);
11060 * pspp_sheet_view_get_drag_dest_row:
11061 * @tree_view: a #PsppSheetView
11062 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11063 * @pos: (allow-none): Return location for the drop position, or %NULL
11065 * Gets information about the row that is highlighted for feedback.
11068 pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view,
11069 GtkTreePath **path,
11070 PsppSheetViewDropPosition *pos)
11072 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11076 if (tree_view->priv->drag_dest_row)
11077 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11080 if (tree_view->priv->empty_view_drop)
11081 *path = gtk_tree_path_new_from_indices (0, -1);
11088 *pos = tree_view->priv->drag_dest_pos;
11092 * pspp_sheet_view_get_dest_row_at_pos:
11093 * @tree_view: a #PsppSheetView
11094 * @drag_x: the position to determine the destination row for
11095 * @drag_y: the position to determine the destination row for
11096 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11097 * @pos: (allow-none): Return location for the drop position, or %NULL
11099 * Determines the destination row for a given position. @drag_x and
11100 * @drag_y are expected to be in widget coordinates. This function is only
11101 * meaningful if @tree_view is realized. Therefore this function will always
11102 * return %FALSE if @tree_view is not realized or does not have a model.
11104 * Return value: whether there is a row at the given position, %TRUE if this
11105 * is indeed the case.
11108 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view,
11111 GtkTreePath **path,
11112 PsppSheetViewDropPosition *pos)
11116 gdouble offset_into_row;
11119 PsppSheetViewColumn *column = NULL;
11120 GtkTreePath *tmp_path = NULL;
11122 /* Note; this function is exported to allow a custom DND
11123 * implementation, so it can't touch TreeViewDragInfo
11126 g_return_val_if_fail (tree_view != NULL, FALSE);
11127 g_return_val_if_fail (drag_x >= 0, FALSE);
11128 g_return_val_if_fail (drag_y >= 0, FALSE);
11133 if (tree_view->priv->bin_window == NULL)
11136 if (tree_view->priv->row_count == 0)
11139 /* If in the top third of a row, we drop before that row; if
11140 * in the bottom third, drop after that row; if in the middle,
11141 * and the row has children, drop into the row.
11143 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11146 if (!pspp_sheet_view_get_path_at_pos (tree_view,
11155 pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11158 offset_into_row = cell_y;
11163 gtk_tree_path_free (tmp_path);
11167 third = cell.height / 3.0;
11171 if (offset_into_row < third)
11173 *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11175 else if (offset_into_row < (cell.height / 2.0))
11177 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11179 else if (offset_into_row < third * 2.0)
11181 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11185 *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11193 #if GTK3_TRANSITION
11194 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11196 * pspp_sheet_view_create_row_drag_icon:
11197 * @tree_view: a #PsppSheetView
11198 * @path: a #GtkTreePath in @tree_view
11200 * Creates a #GdkPixmap representation of the row at @path.
11201 * This image is used for a drag icon.
11203 * Return value: a newly-allocated pixmap of the drag icon.
11206 pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view,
11213 GdkRectangle background_area;
11214 GdkRectangle expose_area;
11216 /* start drawing inside the black outline */
11218 GdkDrawable *drawable;
11219 gint bin_window_width;
11222 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11223 g_return_val_if_fail (path != NULL, NULL);
11225 widget = GTK_WIDGET (tree_view);
11227 if (!gtk_widget_get_realized (widget))
11230 _pspp_sheet_view_find_node (tree_view,
11237 if (!gtk_tree_model_get_iter (tree_view->priv->model,
11244 background_area.y = y;
11245 background_area.height = ROW_HEIGHT (tree_view);
11247 bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
11249 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11250 bin_window_width + 2,
11251 background_area.height + 2,
11256 expose_area.width = bin_window_width + 2;
11257 expose_area.height = background_area.height + 2;
11259 #if GTK3_TRANSITION
11260 gdk_draw_rectangle (drawable,
11261 widget->style->base_gc [gtk_widget_get_state (widget)],
11264 bin_window_width + 2,
11265 background_area.height + 2);
11268 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11270 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11272 list = (rtl ? list->prev : list->next))
11274 PsppSheetViewColumn *column = list->data;
11275 GdkRectangle cell_area;
11276 gint vertical_separator;
11278 if (!column->visible)
11281 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11283 background_area.x = cell_offset;
11284 background_area.width = column->width;
11286 gtk_widget_style_get (widget,
11287 "vertical-separator", &vertical_separator,
11290 cell_area = background_area;
11292 cell_area.y += vertical_separator / 2;
11293 cell_area.height -= vertical_separator;
11295 if (pspp_sheet_view_column_cell_is_visible (column))
11296 _pspp_sheet_view_column_cell_render (column,
11302 cell_offset += column->width;
11305 #if GTK3_TRANSITION
11306 gdk_draw_rectangle (drawable,
11307 widget->style->black_gc,
11310 bin_window_width + 1,
11311 background_area.height + 1);
11319 * pspp_sheet_view_set_destroy_count_func:
11320 * @tree_view: A #PsppSheetView
11321 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11322 * @data: (allow-none): User data to be passed to @func, or %NULL
11323 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11325 * This function should almost never be used. It is meant for private use by
11326 * ATK for determining the number of visible children that are removed when a row is deleted.
11329 pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view,
11330 PsppSheetDestroyCountFunc func,
11332 GDestroyNotify destroy)
11334 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11336 if (tree_view->priv->destroy_count_destroy)
11337 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11339 tree_view->priv->destroy_count_func = func;
11340 tree_view->priv->destroy_count_data = data;
11341 tree_view->priv->destroy_count_destroy = destroy;
11346 * Interactive search
11350 * pspp_sheet_view_set_enable_search:
11351 * @tree_view: A #PsppSheetView
11352 * @enable_search: %TRUE, if the user can search interactively
11354 * If @enable_search is set, then the user can type in text to search through
11355 * the tree interactively (this is sometimes called "typeahead find").
11357 * Note that even if this is %FALSE, the user can still initiate a search
11358 * using the "start-interactive-search" key binding.
11361 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11362 gboolean enable_search)
11364 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11366 enable_search = !!enable_search;
11368 if (tree_view->priv->enable_search != enable_search)
11370 tree_view->priv->enable_search = enable_search;
11371 g_object_notify (G_OBJECT (tree_view), "enable-search");
11376 * pspp_sheet_view_get_enable_search:
11377 * @tree_view: A #PsppSheetView
11379 * Returns whether or not the tree allows to start interactive searching
11380 * by typing in text.
11382 * Return value: whether or not to let the user search interactively
11385 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11387 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11389 return tree_view->priv->enable_search;
11394 * pspp_sheet_view_get_search_column:
11395 * @tree_view: A #PsppSheetView
11397 * Gets the column searched on by the interactive search code.
11399 * Return value: the column the interactive search code searches in.
11402 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11404 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11406 return (tree_view->priv->search_column);
11410 * pspp_sheet_view_set_search_column:
11411 * @tree_view: A #PsppSheetView
11412 * @column: the column of the model to search in, or -1 to disable searching
11414 * Sets @column as the column where the interactive search code should
11415 * search in for the current model.
11417 * If the search column is set, users can use the "start-interactive-search"
11418 * key binding to bring up search popup. The enable-search property controls
11419 * whether simply typing text will also start an interactive search.
11421 * Note that @column refers to a column of the current model. The search
11422 * column is reset to -1 when the model is changed.
11425 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11428 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11429 g_return_if_fail (column >= -1);
11431 if (tree_view->priv->search_column == column)
11434 tree_view->priv->search_column = column;
11435 g_object_notify (G_OBJECT (tree_view), "search-column");
11439 * pspp_sheet_view_get_search_equal_func:
11440 * @tree_view: A #PsppSheetView
11442 * Returns the compare function currently in use.
11444 * Return value: the currently used compare function for the search code.
11447 PsppSheetViewSearchEqualFunc
11448 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11450 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11452 return tree_view->priv->search_equal_func;
11456 * pspp_sheet_view_set_search_equal_func:
11457 * @tree_view: A #PsppSheetView
11458 * @search_equal_func: the compare function to use during the search
11459 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11460 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11462 * Sets the compare function for the interactive search capabilities; note
11463 * that somewhat like strcmp() returning 0 for equality
11464 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11467 pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view,
11468 PsppSheetViewSearchEqualFunc search_equal_func,
11469 gpointer search_user_data,
11470 GDestroyNotify search_destroy)
11472 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11473 g_return_if_fail (search_equal_func != NULL);
11475 if (tree_view->priv->search_destroy)
11476 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11478 tree_view->priv->search_equal_func = search_equal_func;
11479 tree_view->priv->search_user_data = search_user_data;
11480 tree_view->priv->search_destroy = search_destroy;
11481 if (tree_view->priv->search_equal_func == NULL)
11482 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11486 * pspp_sheet_view_get_search_entry:
11487 * @tree_view: A #PsppSheetView
11489 * Returns the #GtkEntry which is currently in use as interactive search
11490 * entry for @tree_view. In case the built-in entry is being used, %NULL
11491 * will be returned.
11493 * Return value: the entry currently in use as search entry.
11498 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11500 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11502 if (tree_view->priv->search_custom_entry_set)
11503 return GTK_ENTRY (tree_view->priv->search_entry);
11509 * pspp_sheet_view_set_search_entry:
11510 * @tree_view: A #PsppSheetView
11511 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11513 * Sets the entry which the interactive search code will use for this
11514 * @tree_view. This is useful when you want to provide a search entry
11515 * in our interface at all time at a fixed position. Passing %NULL for
11516 * @entry will make the interactive search code use the built-in popup
11522 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11525 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11526 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11528 if (tree_view->priv->search_custom_entry_set)
11530 if (tree_view->priv->search_entry_changed_id)
11532 g_signal_handler_disconnect (tree_view->priv->search_entry,
11533 tree_view->priv->search_entry_changed_id);
11534 tree_view->priv->search_entry_changed_id = 0;
11536 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11537 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11540 g_object_unref (tree_view->priv->search_entry);
11542 else if (tree_view->priv->search_window)
11544 gtk_widget_destroy (tree_view->priv->search_window);
11546 tree_view->priv->search_window = NULL;
11551 tree_view->priv->search_entry = g_object_ref (entry);
11552 tree_view->priv->search_custom_entry_set = TRUE;
11554 if (tree_view->priv->search_entry_changed_id == 0)
11556 tree_view->priv->search_entry_changed_id =
11557 g_signal_connect (tree_view->priv->search_entry, "changed",
11558 G_CALLBACK (pspp_sheet_view_search_init),
11562 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11563 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11566 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11570 tree_view->priv->search_entry = NULL;
11571 tree_view->priv->search_custom_entry_set = FALSE;
11576 * pspp_sheet_view_set_search_position_func:
11577 * @tree_view: A #PsppSheetView
11578 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11579 * to use the default search position function
11580 * @data: (allow-none): user data to pass to @func, or %NULL
11581 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11583 * Sets the function to use when positioning the search dialog.
11588 pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view,
11589 PsppSheetViewSearchPositionFunc func,
11590 gpointer user_data,
11591 GDestroyNotify destroy)
11593 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11595 if (tree_view->priv->search_position_destroy)
11596 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11598 tree_view->priv->search_position_func = func;
11599 tree_view->priv->search_position_user_data = user_data;
11600 tree_view->priv->search_position_destroy = destroy;
11601 if (tree_view->priv->search_position_func == NULL)
11602 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11606 * pspp_sheet_view_get_search_position_func:
11607 * @tree_view: A #PsppSheetView
11609 * Returns the positioning function currently in use.
11611 * Return value: the currently used function for positioning the search dialog.
11615 PsppSheetViewSearchPositionFunc
11616 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11618 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11620 return tree_view->priv->search_position_func;
11625 pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
11626 PsppSheetView *tree_view)
11628 if (tree_view->priv->disable_popdown)
11631 if (tree_view->priv->search_entry_changed_id)
11633 g_signal_handler_disconnect (tree_view->priv->search_entry,
11634 tree_view->priv->search_entry_changed_id);
11635 tree_view->priv->search_entry_changed_id = 0;
11637 if (tree_view->priv->typeselect_flush_timeout)
11639 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11640 tree_view->priv->typeselect_flush_timeout = 0;
11643 if (gtk_widget_get_visible (search_dialog))
11645 /* send focus-in event */
11646 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11647 gtk_widget_hide (search_dialog);
11648 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11649 send_focus_change (GTK_WIDGET (tree_view), TRUE);
11654 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11655 GtkWidget *search_dialog,
11656 gpointer user_data)
11659 gint tree_x, tree_y;
11660 gint tree_width, tree_height;
11661 GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
11662 GdkScreen *screen = gdk_window_get_screen (tree_window);
11663 GtkRequisition requisition;
11665 GdkRectangle monitor;
11667 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11668 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11670 gtk_widget_realize (search_dialog);
11672 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11673 tree_width = gdk_window_get_width (tree_window);
11674 tree_height = gdk_window_get_height (tree_window);
11676 gtk_widget_size_request (search_dialog, &requisition);
11678 if (tree_x + tree_width > gdk_screen_get_width (screen))
11679 x = gdk_screen_get_width (screen) - requisition.width;
11680 else if (tree_x + tree_width - requisition.width < 0)
11683 x = tree_x + tree_width - requisition.width;
11685 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11686 y = gdk_screen_get_height (screen) - requisition.height;
11687 else if (tree_y + tree_height < 0) /* isn't really possible ... */
11690 y = tree_y + tree_height;
11692 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11696 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11700 PsppSheetView *tree_view = (PsppSheetView *)data;
11702 tree_view->priv->disable_popdown = 1;
11703 g_signal_connect (menu, "hide",
11704 G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11707 #if GTK3_TRANSITION
11708 /* Because we're visible but offscreen, we just set a flag in the preedit
11712 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11713 PsppSheetView *tree_view)
11715 tree_view->priv->imcontext_changed = 1;
11716 if (tree_view->priv->typeselect_flush_timeout)
11718 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11719 tree_view->priv->typeselect_flush_timeout =
11720 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11721 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11729 pspp_sheet_view_search_activate (GtkEntry *entry,
11730 PsppSheetView *tree_view)
11735 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11738 /* If we have a row selected and it's the cursor row, we activate
11740 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11742 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11744 _pspp_sheet_view_find_node (tree_view, path, &node);
11746 if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11747 pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11749 gtk_tree_path_free (path);
11754 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11756 PsppSheetView *tree_view = (PsppSheetView *)data;
11758 tree_view->priv->disable_popdown = 0;
11764 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11767 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11771 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11772 GdkEventAny *event,
11773 PsppSheetView *tree_view)
11775 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11777 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11783 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11784 GdkEventButton *event,
11785 PsppSheetView *tree_view)
11787 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11789 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11791 if (event->window == tree_view->priv->bin_window)
11792 pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
11798 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
11799 GdkEventScroll *event,
11800 PsppSheetView *tree_view)
11802 gboolean retval = FALSE;
11804 if (event->direction == GDK_SCROLL_UP)
11806 pspp_sheet_view_search_move (widget, tree_view, TRUE);
11809 else if (event->direction == GDK_SCROLL_DOWN)
11811 pspp_sheet_view_search_move (widget, tree_view, FALSE);
11815 /* renew the flush timeout */
11816 if (retval && tree_view->priv->typeselect_flush_timeout
11817 && !tree_view->priv->search_custom_entry_set)
11819 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11820 tree_view->priv->typeselect_flush_timeout =
11821 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11822 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11830 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
11831 GdkEventKey *event,
11832 PsppSheetView *tree_view)
11834 gboolean retval = FALSE;
11836 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11837 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11839 /* close window and cancel the search */
11840 if (!tree_view->priv->search_custom_entry_set
11841 && (event->keyval == GDK_Escape ||
11842 event->keyval == GDK_Tab ||
11843 event->keyval == GDK_KP_Tab ||
11844 event->keyval == GDK_ISO_Left_Tab))
11846 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11850 /* select previous matching iter */
11851 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
11853 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11854 gtk_widget_error_bell (widget);
11859 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
11860 && (event->keyval == GDK_g || event->keyval == GDK_G))
11862 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11863 gtk_widget_error_bell (widget);
11868 /* select next matching iter */
11869 if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
11871 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11872 gtk_widget_error_bell (widget);
11877 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
11878 && (event->keyval == GDK_g || event->keyval == GDK_G))
11880 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11881 gtk_widget_error_bell (widget);
11886 /* renew the flush timeout */
11887 if (retval && tree_view->priv->typeselect_flush_timeout
11888 && !tree_view->priv->search_custom_entry_set)
11890 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11891 tree_view->priv->typeselect_flush_timeout =
11892 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11893 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11900 /* this function returns FALSE if there is a search string but
11901 * nothing was found, and TRUE otherwise.
11904 pspp_sheet_view_search_move (GtkWidget *window,
11905 PsppSheetView *tree_view,
11913 GtkTreeModel *model;
11914 PsppSheetSelection *selection;
11916 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
11918 g_return_val_if_fail (text != NULL, FALSE);
11920 len = strlen (text);
11922 if (up && tree_view->priv->selected_iter == 1)
11923 return strlen (text) < 1;
11925 len = strlen (text);
11930 model = pspp_sheet_view_get_model (tree_view);
11931 selection = pspp_sheet_view_get_selection (tree_view);
11934 pspp_sheet_selection_unselect_all (selection);
11935 if (!gtk_tree_model_get_iter_first (model, &iter))
11938 ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
11939 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
11944 tree_view->priv->selected_iter += up?(-1):(1);
11949 /* return to old iter */
11951 gtk_tree_model_get_iter_first (model, &iter);
11952 pspp_sheet_view_search_iter (model, selection,
11954 &count, tree_view->priv->selected_iter);
11960 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
11964 gpointer search_data)
11966 gboolean retval = TRUE;
11968 gchar *normalized_string;
11969 gchar *normalized_key;
11970 gchar *case_normalized_string = NULL;
11971 gchar *case_normalized_key = NULL;
11972 GValue value = {0,};
11973 GValue transformed = {0,};
11975 gtk_tree_model_get_value (model, iter, column, &value);
11977 g_value_init (&transformed, G_TYPE_STRING);
11979 if (!g_value_transform (&value, &transformed))
11981 g_value_unset (&value);
11985 g_value_unset (&value);
11987 str = g_value_get_string (&transformed);
11990 g_value_unset (&transformed);
11994 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
11995 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
11997 if (normalized_string && normalized_key)
11999 case_normalized_string = g_utf8_casefold (normalized_string, -1);
12000 case_normalized_key = g_utf8_casefold (normalized_key, -1);
12002 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
12006 g_value_unset (&transformed);
12007 g_free (normalized_key);
12008 g_free (normalized_string);
12009 g_free (case_normalized_key);
12010 g_free (case_normalized_string);
12016 pspp_sheet_view_search_iter (GtkTreeModel *model,
12017 PsppSheetSelection *selection,
12026 PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
12028 path = gtk_tree_model_get_path (model, iter);
12029 _pspp_sheet_view_find_node (tree_view, path, &node);
12033 gboolean done = FALSE;
12035 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
12040 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
12042 pspp_sheet_selection_select_iter (selection, iter);
12043 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12046 gtk_tree_path_free (path);
12055 node = pspp_sheet_view_node_next (tree_view, node);
12061 has_next = gtk_tree_model_iter_next (model, iter);
12064 gtk_tree_path_next (path);
12067 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12072 gtk_tree_path_free (path);
12074 /* we've run out of tree, done with this func */
12086 pspp_sheet_view_search_init (GtkWidget *entry,
12087 PsppSheetView *tree_view)
12093 GtkTreeModel *model;
12094 PsppSheetSelection *selection;
12096 g_return_if_fail (GTK_IS_ENTRY (entry));
12097 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12099 text = gtk_entry_get_text (GTK_ENTRY (entry));
12101 model = pspp_sheet_view_get_model (tree_view);
12102 selection = pspp_sheet_view_get_selection (tree_view);
12105 pspp_sheet_selection_unselect_all (selection);
12106 if (tree_view->priv->typeselect_flush_timeout
12107 && !tree_view->priv->search_custom_entry_set)
12109 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12110 tree_view->priv->typeselect_flush_timeout =
12111 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12112 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12119 if (!gtk_tree_model_get_iter_first (model, &iter))
12122 ret = pspp_sheet_view_search_iter (model, selection,
12127 tree_view->priv->selected_iter = 1;
12131 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12132 PsppSheetView *tree_view)
12134 if (tree_view->priv->edited_column == NULL)
12137 _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12138 tree_view->priv->edited_column = NULL;
12140 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12141 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12143 g_signal_handlers_disconnect_by_func (cell_editable,
12144 pspp_sheet_view_remove_widget,
12146 g_signal_handlers_disconnect_by_func (cell_editable,
12147 pspp_sheet_view_editable_button_press_event,
12149 g_signal_handlers_disconnect_by_func (cell_editable,
12150 pspp_sheet_view_editable_clicked,
12153 gtk_container_remove (GTK_CONTAINER (tree_view),
12154 GTK_WIDGET (cell_editable));
12156 /* FIXME should only redraw a single node */
12157 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12161 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12162 GtkTreePath *cursor_path)
12165 GdkRectangle background_area;
12166 GdkRectangle cell_area;
12167 GtkCellEditable *editable_widget = NULL;
12168 gchar *path_string;
12169 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12170 gint retval = FALSE;
12173 g_assert (tree_view->priv->focus_column);
12175 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12178 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12179 if (cursor_node < 0)
12182 path_string = gtk_tree_path_to_string (cursor_path);
12183 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12185 pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12186 tree_view->priv->model,
12188 pspp_sheet_view_get_background_area (tree_view,
12190 tree_view->priv->focus_column,
12192 pspp_sheet_view_get_cell_area (tree_view,
12194 tree_view->priv->focus_column,
12197 if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12206 if (editable_widget != NULL)
12210 GtkCellRenderer *cell;
12213 cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12215 _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12218 area.width -= right + left;
12220 pspp_sheet_view_real_start_editing (tree_view,
12221 tree_view->priv->focus_column,
12230 g_free (path_string);
12235 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12236 GdkEventButton *event,
12237 PsppSheetView *sheet_view)
12241 node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12242 "pspp-sheet-view-node"));
12243 return pspp_sheet_view_row_head_clicked (sheet_view,
12245 sheet_view->priv->edited_column,
12250 pspp_sheet_view_editable_clicked (GtkButton *button,
12251 PsppSheetView *sheet_view)
12253 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12258 is_all_selected (GtkWidget *widget)
12260 GtkEntryBuffer *buffer;
12261 gint start_pos, end_pos;
12263 if (!GTK_IS_ENTRY (widget))
12266 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12267 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12268 &start_pos, &end_pos)
12270 && end_pos == gtk_entry_buffer_get_length (buffer));
12274 is_at_left (GtkWidget *widget)
12276 return (GTK_IS_ENTRY (widget)
12277 && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12281 is_at_right (GtkWidget *widget)
12283 GtkEntryBuffer *buffer;
12286 if (!GTK_IS_ENTRY (widget))
12289 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12290 length = gtk_entry_buffer_get_length (buffer);
12291 return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12295 pspp_sheet_view_event (GtkWidget *widget,
12296 GdkEventKey *event,
12297 PsppSheetView *tree_view)
12299 PsppSheetViewColumn *column;
12306 /* Intercept only key press events.
12307 It would make sense to use "key-press-event" instead of "event", but
12308 GtkEntry attaches its own signal handler to "key-press-event" that runs
12309 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12311 if (event->type != GDK_KEY_PRESS)
12314 keyval = event->keyval;
12316 switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12319 switch (event->keyval)
12321 case GDK_Left: case GDK_KP_Left:
12322 case GDK_Home: case GDK_KP_Home:
12323 if (!is_all_selected (widget) && !is_at_left (widget))
12327 case GDK_Right: case GDK_KP_Right:
12328 case GDK_End: case GDK_KP_End:
12329 if (!is_all_selected (widget) && !is_at_right (widget))
12333 case GDK_Up: case GDK_KP_Up:
12334 case GDK_Down: case GDK_KP_Down:
12337 case GDK_Page_Up: case GDK_KP_Page_Up:
12338 case GDK_Page_Down: case GDK_KP_Page_Down:
12349 case GDK_Tab: case GDK_KP_Tab:
12350 case GDK_ISO_Left_Tab:
12359 case GDK_SHIFT_MASK:
12360 switch (event->keyval)
12363 case GDK_ISO_Left_Tab:
12372 case GDK_CONTROL_MASK:
12373 switch (event->keyval)
12375 case GDK_Left: case GDK_KP_Left:
12376 if (!is_all_selected (widget) && !is_at_left (widget))
12380 case GDK_Right: case GDK_KP_Right:
12381 if (!is_all_selected (widget) && !is_at_right (widget))
12385 case GDK_Up: case GDK_KP_Up:
12386 case GDK_Down: case GDK_KP_Down:
12398 row = tree_view->priv->edited_row;
12399 column = tree_view->priv->edited_column;
12400 path = gtk_tree_path_new_from_indices (row, -1);
12402 pspp_sheet_view_stop_editing (tree_view, cancel);
12403 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12405 pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12406 gtk_tree_path_free (path);
12408 handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12409 G_OBJECT (tree_view));
12411 g_signal_stop_emission_by_name (widget, "event");
12413 pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12414 pspp_sheet_view_start_editing (tree_view, path);
12415 gtk_tree_path_free (path);
12421 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12424 PsppSheetView *sheet_view = data;
12426 g_signal_connect (widget, "event",
12427 G_CALLBACK (pspp_sheet_view_event),
12430 if (GTK_IS_CONTAINER (widget))
12431 gtk_container_foreach (GTK_CONTAINER (widget),
12432 pspp_sheet_view_override_cell_keypresses,
12437 pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
12438 PsppSheetViewColumn *column,
12440 GtkCellEditable *cell_editable,
12441 GdkRectangle *cell_area,
12445 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12446 gint pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
12447 GtkRequisition requisition;
12450 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12452 tree_view->priv->edited_column = column;
12453 _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12455 row = gtk_tree_path_get_indices (path)[0];
12456 tree_view->priv->edited_row = row;
12457 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12458 cell_area->y += pre_val - (int)gtk_adjustment_get_value (tree_view->priv->vadjustment);
12460 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12461 pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12462 tree_view->priv->anchor_column = column;
12464 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12466 pspp_sheet_view_put (tree_view,
12467 GTK_WIDGET (cell_editable),
12471 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12472 (GdkEvent *)event);
12474 gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12475 g_signal_connect (cell_editable, "remove-widget",
12476 G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12477 if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12478 GTK_IS_BUTTON (cell_editable))
12480 g_signal_connect (cell_editable, "button-press-event",
12481 G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12483 g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12484 GINT_TO_POINTER (row));
12485 g_signal_connect (cell_editable, "clicked",
12486 G_CALLBACK (pspp_sheet_view_editable_clicked),
12490 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12495 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12496 gboolean cancel_editing)
12498 PsppSheetViewColumn *column;
12499 GtkCellRenderer *cell;
12501 if (tree_view->priv->edited_column == NULL)
12505 * This is very evil. We need to do this, because
12506 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12507 * later on. If pspp_sheet_view_row_changed notices
12508 * tree_view->priv->edited_column != NULL, it'll call
12509 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12511 * Please read that again if you intend to modify anything here.
12514 column = tree_view->priv->edited_column;
12515 tree_view->priv->edited_column = NULL;
12517 cell = _pspp_sheet_view_column_get_edited_cell (column);
12518 gtk_cell_renderer_stop_editing (cell, cancel_editing);
12520 if (!cancel_editing)
12521 gtk_cell_editable_editing_done (column->editable_widget);
12523 tree_view->priv->edited_column = column;
12525 gtk_cell_editable_remove_widget (column->editable_widget);
12530 * pspp_sheet_view_set_hover_selection:
12531 * @tree_view: a #PsppSheetView
12532 * @hover: %TRUE to enable hover selection mode
12534 * Enables of disables the hover selection mode of @tree_view.
12535 * Hover selection makes the selected row follow the pointer.
12536 * Currently, this works only for the selection modes
12537 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12542 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12545 hover = hover != FALSE;
12547 if (hover != tree_view->priv->hover_selection)
12549 tree_view->priv->hover_selection = hover;
12551 g_object_notify (G_OBJECT (tree_view), "hover-selection");
12556 * pspp_sheet_view_get_hover_selection:
12557 * @tree_view: a #PsppSheetView
12559 * Returns whether hover selection mode is turned on for @tree_view.
12561 * Return value: %TRUE if @tree_view is in hover selection mode
12566 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12568 return tree_view->priv->hover_selection;
12572 * pspp_sheet_view_set_rubber_banding:
12573 * @tree_view: a #PsppSheetView
12574 * @enable: %TRUE to enable rubber banding
12576 * Enables or disables rubber banding in @tree_view. If the selection mode is
12577 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12578 * banding will allow the user to select multiple rows by dragging the mouse.
12583 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12586 enable = enable != FALSE;
12588 if (enable != tree_view->priv->rubber_banding_enable)
12590 tree_view->priv->rubber_banding_enable = enable;
12592 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12597 * pspp_sheet_view_get_rubber_banding:
12598 * @tree_view: a #PsppSheetView
12600 * Returns whether rubber banding is turned on for @tree_view. If the
12601 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12602 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12603 * select multiple rows by dragging the mouse.
12605 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12610 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12612 return tree_view->priv->rubber_banding_enable;
12616 * pspp_sheet_view_is_rubber_banding_active:
12617 * @tree_view: a #PsppSheetView
12619 * Returns whether a rubber banding operation is currently being done
12622 * Return value: %TRUE if a rubber banding operation is currently being
12623 * done in @tree_view.
12628 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12630 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12632 if (tree_view->priv->rubber_banding_enable
12633 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12640 pspp_sheet_view_grab_notify (GtkWidget *widget,
12641 gboolean was_grabbed)
12643 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12645 tree_view->priv->in_grab = !was_grabbed;
12649 tree_view->priv->pressed_button = -1;
12651 if (tree_view->priv->rubber_band_status)
12652 pspp_sheet_view_stop_rubber_band (tree_view);
12657 pspp_sheet_view_state_changed (GtkWidget *widget,
12658 GtkStateType previous_state)
12660 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12662 if (gtk_widget_get_realized (widget))
12664 GtkStyle *style = gtk_widget_get_style (widget);
12665 gdk_window_set_background (tree_view->priv->bin_window, &style->base[gtk_widget_get_state (widget)]);
12668 gtk_widget_queue_draw (widget);
12672 * pspp_sheet_view_get_grid_lines:
12673 * @tree_view: a #PsppSheetView
12675 * Returns which grid lines are enabled in @tree_view.
12677 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12682 PsppSheetViewGridLines
12683 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12685 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12687 return tree_view->priv->grid_lines;
12691 * pspp_sheet_view_set_grid_lines:
12692 * @tree_view: a #PsppSheetView
12693 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12696 * Sets which grid lines to draw in @tree_view.
12701 pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view,
12702 PsppSheetViewGridLines grid_lines)
12704 PsppSheetViewPrivate *priv;
12705 PsppSheetViewGridLines old_grid_lines;
12707 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12709 priv = tree_view->priv;
12711 old_grid_lines = priv->grid_lines;
12712 priv->grid_lines = grid_lines;
12714 if (old_grid_lines != grid_lines)
12716 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12718 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12723 * pspp_sheet_view_get_special_cells:
12724 * @tree_view: a #PsppSheetView
12726 * Returns which grid lines are enabled in @tree_view.
12728 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12729 * the sheet view contain special cells.
12731 PsppSheetViewSpecialCells
12732 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12734 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12736 return tree_view->priv->special_cells;
12740 * pspp_sheet_view_set_special_cells:
12741 * @tree_view: a #PsppSheetView
12742 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12743 * the sheet view contain special cells.
12745 * Sets whether rows in the sheet view contain special cells, controlling the
12746 * rendering of row selections.
12749 pspp_sheet_view_set_special_cells (PsppSheetView *tree_view,
12750 PsppSheetViewSpecialCells special_cells)
12752 PsppSheetViewPrivate *priv;
12754 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12756 priv = tree_view->priv;
12758 if (priv->special_cells != special_cells)
12760 priv->special_cells = special_cells;
12761 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12762 g_object_notify (G_OBJECT (tree_view), "special-cells");
12767 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12769 /* XXX (re)calculate fixed_height if necessary */
12770 return tree_view->priv->fixed_height;
12774 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
12777 g_return_if_fail (fixed_height > 0);
12779 if (tree_view->priv->fixed_height != fixed_height)
12781 tree_view->priv->fixed_height = fixed_height;
12782 g_object_notify (G_OBJECT (tree_view), "fixed-height");
12784 if (!tree_view->priv->fixed_height_set)
12786 tree_view->priv->fixed_height_set = TRUE;
12787 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
12792 * pspp_sheet_view_set_tooltip_row:
12793 * @tree_view: a #PsppSheetView
12794 * @tooltip: a #GtkTooltip
12795 * @path: a #GtkTreePath
12797 * Sets the tip area of @tooltip to be the area covered by the row at @path.
12798 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12799 * See also gtk_tooltip_set_tip_area().
12804 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
12805 GtkTooltip *tooltip,
12808 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12809 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12811 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
12815 * pspp_sheet_view_set_tooltip_cell:
12816 * @tree_view: a #PsppSheetView
12817 * @tooltip: a #GtkTooltip
12818 * @path: (allow-none): a #GtkTreePath or %NULL
12819 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
12820 * @cell: (allow-none): a #GtkCellRenderer or %NULL
12822 * Sets the tip area of @tooltip to the area @path, @column and @cell have
12823 * in common. For example if @path is %NULL and @column is set, the tip
12824 * area will be set to the full area covered by @column. See also
12825 * gtk_tooltip_set_tip_area().
12827 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12832 pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view,
12833 GtkTooltip *tooltip,
12835 PsppSheetViewColumn *column,
12836 GtkCellRenderer *cell)
12840 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12841 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12842 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
12843 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
12845 /* Determine x values. */
12846 if (column && cell)
12851 pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
12852 pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
12854 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12857 rect.width = width;
12863 pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
12864 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12867 rect.width = tmp.width;
12871 GtkAllocation allocation;
12872 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
12874 rect.width = allocation.width;
12877 /* Determine y values. */
12882 pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
12883 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12886 rect.height = tmp.height;
12891 rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
12894 gtk_tooltip_set_tip_area (tooltip, &rect);
12898 * pspp_sheet_view_get_tooltip_context:
12899 * @tree_view: a #PsppSheetView
12900 * @x: the x coordinate (relative to widget coordinates)
12901 * @y: the y coordinate (relative to widget coordinates)
12902 * @keyboard_tip: whether this is a keyboard tooltip or not
12903 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
12904 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
12905 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
12907 * This function is supposed to be used in a #GtkWidget::query-tooltip
12908 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
12909 * which are received in the signal handler, should be passed to this
12910 * function without modification.
12912 * The return value indicates whether there is a tree view row at the given
12913 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
12914 * tooltips the row returned will be the cursor row. When %TRUE, then any of
12915 * @model, @path and @iter which have been provided will be set to point to
12916 * that row and the corresponding model. @x and @y will always be converted
12917 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
12919 * Return value: whether or not the given tooltip context points to a row.
12924 pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view,
12927 gboolean keyboard_tip,
12928 GtkTreeModel **model,
12929 GtkTreePath **path,
12932 GtkTreePath *tmppath = NULL;
12934 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12935 g_return_val_if_fail (x != NULL, FALSE);
12936 g_return_val_if_fail (y != NULL, FALSE);
12940 pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
12947 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
12950 if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
12951 &tmppath, NULL, NULL, NULL))
12956 *model = pspp_sheet_view_get_model (tree_view);
12959 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
12965 gtk_tree_path_free (tmppath);
12971 pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget,
12974 gboolean keyboard_tip,
12975 GtkTooltip *tooltip,
12978 GValue value = { 0, };
12979 GValue transformed = { 0, };
12982 GtkTreeModel *model;
12983 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12985 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
12988 &model, &path, &iter))
12991 gtk_tree_model_get_value (model, &iter,
12992 tree_view->priv->tooltip_column, &value);
12994 g_value_init (&transformed, G_TYPE_STRING);
12996 if (!g_value_transform (&value, &transformed))
12998 g_value_unset (&value);
12999 gtk_tree_path_free (path);
13004 g_value_unset (&value);
13006 if (!g_value_get_string (&transformed))
13008 g_value_unset (&transformed);
13009 gtk_tree_path_free (path);
13014 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
13015 pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
13017 gtk_tree_path_free (path);
13018 g_value_unset (&transformed);
13024 * pspp_sheet_view_set_tooltip_column:
13025 * @tree_view: a #PsppSheetView
13026 * @column: an integer, which is a valid column number for @tree_view's model
13028 * If you only plan to have simple (text-only) tooltips on full rows, you
13029 * can use this function to have #PsppSheetView handle these automatically
13030 * for you. @column should be set to the column in @tree_view's model
13031 * containing the tooltip texts, or -1 to disable this feature.
13033 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
13034 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
13036 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
13037 * so &, <, etc have to be escaped in the text.
13042 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
13045 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13047 if (column == tree_view->priv->tooltip_column)
13052 g_signal_handlers_disconnect_by_func (tree_view,
13053 pspp_sheet_view_set_tooltip_query_cb,
13055 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13059 if (tree_view->priv->tooltip_column == -1)
13061 g_signal_connect (tree_view, "query-tooltip",
13062 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13063 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13067 tree_view->priv->tooltip_column = column;
13068 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13072 * pspp_sheet_view_get_tooltip_column:
13073 * @tree_view: a #PsppSheetView
13075 * Returns the column of @tree_view's model which is being used for
13076 * displaying tooltips on @tree_view's rows.
13078 * Return value: the index of the tooltip column that is currently being
13079 * used, or -1 if this is disabled.
13084 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13086 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13088 return tree_view->priv->tooltip_column;
13092 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13093 GValue *return_accu,
13094 const GValue *handler_return,
13097 gboolean continue_emission;
13098 gboolean signal_handled;
13100 signal_handled = g_value_get_boolean (handler_return);
13101 g_value_set_boolean (return_accu, signal_handled);
13102 continue_emission = !signal_handled;
13104 return continue_emission;
13109 pspp_sheet_view_grid_lines_get_type (void)
13111 static GType etype = 0;
13112 if (G_UNLIKELY(etype == 0)) {
13113 static const GEnumValue values[] = {
13114 { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13115 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13116 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13117 { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13120 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13126 pspp_sheet_view_special_cells_get_type (void)
13128 static GType etype = 0;
13129 if (G_UNLIKELY(etype == 0)) {
13130 static const GEnumValue values[] = {
13131 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13132 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13133 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13136 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);