1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012, 2013, 2015 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Library General Public
22 * License as published by the Free Software Foundation; either
23 * version 2 of the License, or (at your option) any later version.
25 * This library is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Library General Public License for more details.
30 * You should have received a copy of the GNU Library General Public
31 * License along with this library; if not, write to the
32 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
33 * Boston, MA 02111-1307, USA.
38 #include "ui/gui/pspp-sheet-private.h"
42 #include <gdk/gdkkeysyms.h>
43 #include <gdk/gdkkeysyms-compat.h>
46 #include "ui/gui/psppire-marshal.h"
47 #include "ui/gui/pspp-sheet-selection.h"
49 #define P_(STRING) STRING
50 #define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
51 #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
53 /* Many keyboard shortcuts for Mac are the same as for X
54 * except they use Command key instead of Control (e.g. Cut,
55 * Copy, Paste). This symbol is for those simple cases. */
56 #ifndef GDK_WINDOWING_QUARTZ
57 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_CONTROL_MASK
59 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_META_MASK
62 #define PSPP_SHEET_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
63 #define PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC (PSPP_SHEET_VIEW_PRIORITY_VALIDATE + 2)
64 #define PSPP_SHEET_VIEW_TIME_MS_PER_IDLE 30
65 #define SCROLL_EDGE_SIZE 15
66 #define EXPANDER_EXTRA_PADDING 4
67 #define PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT 5000
69 /* The "background" areas of all rows/cells add up to cover the entire tree.
70 * The background includes all inter-row and inter-cell spacing.
71 * The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
72 * i.e. just the cells, no spacing.
75 #define BACKGROUND_HEIGHT(tree_view) (tree_view->priv->fixed_height)
76 #define CELL_HEIGHT(tree_view, separator) ((BACKGROUND_HEIGHT (tree_view)) - (separator))
78 /* Translate from bin_window coordinates to rbtree (tree coordinates) and
81 #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->priv->dy)
82 #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->priv->dy)
84 /* This is in bin_window coordinates */
85 #define BACKGROUND_FIRST_PIXEL(tree_view,node) (RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, pspp_sheet_view_node_find_offset (tree_view, (node))))
86 #define CELL_FIRST_PIXEL(tree_view,node,separator) (BACKGROUND_FIRST_PIXEL (tree_view,node) + separator/2)
88 #define ROW_HEIGHT(tree_view) \
89 ((tree_view->priv->fixed_height > 0) ? (tree_view->priv->fixed_height) : (tree_view)->priv->expander_size)
92 typedef struct _PsppSheetViewChild PsppSheetViewChild;
93 struct _PsppSheetViewChild
96 PsppSheetViewColumn *column;
101 typedef struct _TreeViewDragInfo TreeViewDragInfo;
102 struct _TreeViewDragInfo
104 GdkModifierType start_button_mask;
105 GtkTargetList *_unused_source_target_list;
106 GdkDragAction source_actions;
108 GtkTargetList *_unused_dest_target_list;
110 guint source_set : 1;
126 START_INTERACTIVE_SEARCH,
138 PROP_HEADERS_VISIBLE,
139 PROP_HEADERS_CLICKABLE,
144 PROP_HOVER_SELECTION,
146 PROP_ENABLE_GRID_LINES,
150 PROP_FIXED_HEIGHT_SET
154 static void pspp_sheet_view_finalize (GObject *object);
155 static void pspp_sheet_view_set_property (GObject *object,
159 static void pspp_sheet_view_get_property (GObject *object,
164 static void pspp_sheet_view_dispose (GObject *object);
166 /* gtkwidget signals */
167 static void pspp_sheet_view_realize (GtkWidget *widget);
168 static void pspp_sheet_view_unrealize (GtkWidget *widget);
169 static void pspp_sheet_view_map (GtkWidget *widget);
170 static void pspp_sheet_view_size_request (GtkWidget *widget,
171 GtkRequisition *requisition);
172 static void pspp_sheet_view_size_allocate (GtkWidget *widget,
173 GtkAllocation *allocation);
174 static gboolean pspp_sheet_view_draw (GtkWidget *widget,
176 static gboolean pspp_sheet_view_key_press (GtkWidget *widget,
178 static gboolean pspp_sheet_view_key_release (GtkWidget *widget,
180 static gboolean pspp_sheet_view_motion (GtkWidget *widget,
181 GdkEventMotion *event);
182 static gboolean pspp_sheet_view_enter_notify (GtkWidget *widget,
183 GdkEventCrossing *event);
184 static gboolean pspp_sheet_view_leave_notify (GtkWidget *widget,
185 GdkEventCrossing *event);
186 static gboolean pspp_sheet_view_button_press (GtkWidget *widget,
187 GdkEventButton *event);
188 static gboolean pspp_sheet_view_button_release (GtkWidget *widget,
189 GdkEventButton *event);
190 static gboolean pspp_sheet_view_grab_broken (GtkWidget *widget,
191 GdkEventGrabBroken *event);
193 static void pspp_sheet_view_set_focus_child (GtkContainer *container,
195 static gint pspp_sheet_view_focus_out (GtkWidget *widget,
196 GdkEventFocus *event);
197 static gint pspp_sheet_view_focus (GtkWidget *widget,
198 GtkDirectionType direction);
199 static void pspp_sheet_view_grab_focus (GtkWidget *widget);
200 static void pspp_sheet_view_style_updated (GtkWidget *widget);
201 static void pspp_sheet_view_grab_notify (GtkWidget *widget,
202 gboolean was_grabbed);
203 static void pspp_sheet_view_state_changed (GtkWidget *widget,
204 GtkStateType previous_state);
206 /* container signals */
207 static void pspp_sheet_view_remove (GtkContainer *container,
209 static void pspp_sheet_view_forall (GtkContainer *container,
210 gboolean include_internals,
211 GtkCallback callback,
212 gpointer callback_data);
214 /* Source side drag signals */
215 static void pspp_sheet_view_drag_begin (GtkWidget *widget,
216 GdkDragContext *context);
217 static void pspp_sheet_view_drag_end (GtkWidget *widget,
218 GdkDragContext *context);
219 static void pspp_sheet_view_drag_data_get (GtkWidget *widget,
220 GdkDragContext *context,
221 GtkSelectionData *selection_data,
224 static void pspp_sheet_view_drag_data_delete (GtkWidget *widget,
225 GdkDragContext *context);
227 /* Target side drag signals */
228 static void pspp_sheet_view_drag_leave (GtkWidget *widget,
229 GdkDragContext *context,
231 static gboolean pspp_sheet_view_drag_motion (GtkWidget *widget,
232 GdkDragContext *context,
236 static gboolean pspp_sheet_view_drag_drop (GtkWidget *widget,
237 GdkDragContext *context,
241 static void pspp_sheet_view_drag_data_received (GtkWidget *widget,
242 GdkDragContext *context,
245 GtkSelectionData *selection_data,
249 /* tree_model signals */
250 static void pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
252 GtkAdjustment *vadj);
253 static gboolean pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
254 GtkMovementStep step,
256 static gboolean pspp_sheet_view_real_select_all (PsppSheetView *tree_view);
257 static gboolean pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view);
258 static gboolean pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
259 gboolean start_editing,
260 PsppSheetSelectMode mode);
261 static gboolean pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view);
262 static void pspp_sheet_view_row_changed (GtkTreeModel *model,
266 static void pspp_sheet_view_row_inserted (GtkTreeModel *model,
270 static void pspp_sheet_view_row_deleted (GtkTreeModel *model,
273 static void pspp_sheet_view_rows_reordered (GtkTreeModel *model,
279 /* Incremental reflow */
280 static gint validate_row (PsppSheetView *tree_view,
284 static void validate_visible_area (PsppSheetView *tree_view);
285 static gboolean validate_rows_handler (PsppSheetView *tree_view);
286 static gboolean presize_handler_callback (gpointer data);
287 static void install_presize_handler (PsppSheetView *tree_view);
288 static void install_scroll_sync_handler (PsppSheetView *tree_view);
289 static void pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
292 static void pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view);
293 static void pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view);
294 static void invalidate_empty_focus (PsppSheetView *tree_view);
296 /* Internal functions */
297 static GtkAdjustment *pspp_sheet_view_do_get_hadjustment (PsppSheetView *);
298 static GtkAdjustment *pspp_sheet_view_do_get_vadjustment (PsppSheetView *);
299 static void pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
300 GtkAdjustment *adjustment);
301 static void pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
302 GtkAdjustment *adjustment);
303 static void pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
306 gboolean add_shifted_binding,
307 GtkMovementStep step,
309 static void pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
311 const GdkRectangle *clip_rect);
312 static gint pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
315 static void pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
316 PsppSheetView *tree_view);
317 static void pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
319 static void pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
320 PsppSheetViewColumn *column,
321 gboolean focus_to_cell);
322 static gboolean pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
323 GdkEventMotion *event);
324 static void pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view);
325 static gboolean pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
327 PsppSheetSelectMode mode);
328 static void pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
330 PsppSheetSelectMode mode);
331 static void pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
333 PsppSheetSelectMode mode);
334 static void pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
336 PsppSheetSelectMode mode);
337 static void pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
339 static void pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
341 PsppSheetSelectMode mode);
342 static void pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
344 gboolean clear_and_select,
346 PsppSheetSelectMode mode);
347 static gboolean pspp_sheet_view_has_special_cell (PsppSheetView *tree_view);
348 static void pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view);
349 static void update_prelight (PsppSheetView *tree_view,
352 static void initialize_fixed_height_mode (PsppSheetView *tree_view);
354 /* interactive search */
355 static void pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view);
356 static void pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
357 PsppSheetView *tree_view);
358 static void pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
359 GtkWidget *search_dialog,
361 static void pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
365 static void pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
366 PsppSheetView *tree_view);
368 static void pspp_sheet_view_search_activate (GtkEntry *entry,
369 PsppSheetView *tree_view);
370 static gboolean pspp_sheet_view_real_search_enable_popdown(gpointer data);
371 static void pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
373 static gboolean pspp_sheet_view_search_delete_event (GtkWidget *widget,
375 PsppSheetView *tree_view);
376 static gboolean pspp_sheet_view_search_button_press_event (GtkWidget *widget,
377 GdkEventButton *event,
378 PsppSheetView *tree_view);
379 static gboolean pspp_sheet_view_search_scroll_event (GtkWidget *entry,
380 GdkEventScroll *event,
381 PsppSheetView *tree_view);
382 static gboolean pspp_sheet_view_search_key_press_event (GtkWidget *entry,
384 PsppSheetView *tree_view);
385 static gboolean pspp_sheet_view_search_move (GtkWidget *window,
386 PsppSheetView *tree_view,
388 static gboolean pspp_sheet_view_search_equal_func (GtkTreeModel *model,
392 gpointer search_data);
393 static gboolean pspp_sheet_view_search_iter (GtkTreeModel *model,
394 PsppSheetSelection *selection,
399 static void pspp_sheet_view_search_init (GtkWidget *entry,
400 PsppSheetView *tree_view);
401 static void pspp_sheet_view_put (PsppSheetView *tree_view,
402 GtkWidget *child_widget,
404 PsppSheetViewColumn *column);
405 static gboolean pspp_sheet_view_start_editing (PsppSheetView *tree_view,
406 GtkTreePath *cursor_path);
407 static gboolean pspp_sheet_view_editable_button_press_event (GtkWidget *,
410 static void pspp_sheet_view_editable_clicked (GtkButton *, PsppSheetView *);
411 static void pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
412 PsppSheetViewColumn *column,
414 GtkCellEditable *cell_editable,
415 GdkRectangle *cell_area,
418 static gboolean pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
419 gboolean keybinding);
420 static gboolean pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view);
421 static PsppSheetViewColumn *pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
422 PsppSheetViewColumn *column,
425 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
426 PsppSheetViewColumn *column,
427 const GdkRectangle *background_area,
428 gboolean subtract_focus_rect,
429 GdkRectangle *cell_area);
430 static gint pspp_sheet_view_find_offset (PsppSheetView *tree_view,
435 static void pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
439 static void pspp_sheet_view_buildable_init (GtkBuildableIface *iface);
442 static gboolean scroll_row_timeout (gpointer data);
443 static void add_scroll_timeout (PsppSheetView *tree_view);
444 static void remove_scroll_timeout (PsppSheetView *tree_view);
446 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
448 static GtkBindingSet *edit_bindings;
455 G_DEFINE_TYPE_WITH_CODE (PsppSheetView, pspp_sheet_view, GTK_TYPE_CONTAINER,
456 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
457 pspp_sheet_view_buildable_init)
458 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
461 pspp_sheet_view_get_preferred_width (GtkWidget *widget,
465 GtkRequisition requisition;
467 pspp_sheet_view_size_request (widget, &requisition);
469 *minimal_width = *natural_width = requisition.width;
473 pspp_sheet_view_get_preferred_height (GtkWidget *widget,
474 gint *minimal_height,
475 gint *natural_height)
477 GtkRequisition requisition;
479 pspp_sheet_view_size_request (widget, &requisition);
481 *minimal_height = *natural_height = requisition.height;
485 pspp_sheet_view_class_init (PsppSheetViewClass *class)
487 GObjectClass *o_class;
488 GtkWidgetClass *widget_class;
489 GtkContainerClass *container_class;
490 GtkBindingSet *binding_set[2];
493 binding_set[0] = gtk_binding_set_by_class (class);
495 binding_set[1] = gtk_binding_set_new ("PsppSheetViewEditing");
496 edit_bindings = binding_set[1];
498 o_class = (GObjectClass *) class;
499 widget_class = (GtkWidgetClass *) class;
500 container_class = (GtkContainerClass *) class;
502 /* GObject signals */
503 o_class->set_property = pspp_sheet_view_set_property;
504 o_class->get_property = pspp_sheet_view_get_property;
505 o_class->finalize = pspp_sheet_view_finalize;
506 o_class->dispose = pspp_sheet_view_dispose;
508 /* GtkWidget signals */
509 widget_class->map = pspp_sheet_view_map;
510 widget_class->realize = pspp_sheet_view_realize;
511 widget_class->unrealize = pspp_sheet_view_unrealize;
512 widget_class->get_preferred_width = pspp_sheet_view_get_preferred_width;
513 widget_class->get_preferred_height = pspp_sheet_view_get_preferred_height;
514 widget_class->size_allocate = pspp_sheet_view_size_allocate;
515 widget_class->button_press_event = pspp_sheet_view_button_press;
516 widget_class->button_release_event = pspp_sheet_view_button_release;
517 widget_class->grab_broken_event = pspp_sheet_view_grab_broken;
518 /*widget_class->configure_event = pspp_sheet_view_configure;*/
519 widget_class->motion_notify_event = pspp_sheet_view_motion;
520 widget_class->draw = pspp_sheet_view_draw;
521 widget_class->key_press_event = pspp_sheet_view_key_press;
522 widget_class->key_release_event = pspp_sheet_view_key_release;
523 widget_class->enter_notify_event = pspp_sheet_view_enter_notify;
524 widget_class->leave_notify_event = pspp_sheet_view_leave_notify;
525 widget_class->focus_out_event = pspp_sheet_view_focus_out;
526 widget_class->drag_begin = pspp_sheet_view_drag_begin;
527 widget_class->drag_end = pspp_sheet_view_drag_end;
528 widget_class->drag_data_get = pspp_sheet_view_drag_data_get;
529 widget_class->drag_data_delete = pspp_sheet_view_drag_data_delete;
530 widget_class->drag_leave = pspp_sheet_view_drag_leave;
531 widget_class->drag_motion = pspp_sheet_view_drag_motion;
532 widget_class->drag_drop = pspp_sheet_view_drag_drop;
533 widget_class->drag_data_received = pspp_sheet_view_drag_data_received;
534 widget_class->focus = pspp_sheet_view_focus;
535 widget_class->grab_focus = pspp_sheet_view_grab_focus;
536 widget_class->style_updated = pspp_sheet_view_style_updated;
537 widget_class->grab_notify = pspp_sheet_view_grab_notify;
538 widget_class->state_changed = pspp_sheet_view_state_changed;
540 /* GtkContainer signals */
541 container_class->remove = pspp_sheet_view_remove;
542 container_class->forall = pspp_sheet_view_forall;
543 container_class->set_focus_child = pspp_sheet_view_set_focus_child;
545 class->set_scroll_adjustments = pspp_sheet_view_set_adjustments;
546 class->move_cursor = pspp_sheet_view_real_move_cursor;
547 class->select_all = pspp_sheet_view_real_select_all;
548 class->unselect_all = pspp_sheet_view_real_unselect_all;
549 class->select_cursor_row = pspp_sheet_view_real_select_cursor_row;
550 class->toggle_cursor_row = pspp_sheet_view_real_toggle_cursor_row;
551 class->start_interactive_search = pspp_sheet_view_start_interactive_search;
555 g_object_class_install_property (o_class,
557 g_param_spec_object ("model",
558 P_("TreeView Model"),
559 P_("The model for the tree view"),
561 GTK_PARAM_READWRITE));
563 g_object_class_override_property (o_class, PROP_HADJUSTMENT, "hadjustment");
564 g_object_class_override_property (o_class, PROP_VADJUSTMENT, "vadjustment");
565 g_object_class_override_property (o_class, PROP_HSCROLL_POLICY, "hscroll-policy");
566 g_object_class_override_property (o_class, PROP_VSCROLL_POLICY, "vscroll-policy");
568 g_object_class_install_property (o_class,
569 PROP_HEADERS_VISIBLE,
570 g_param_spec_boolean ("headers-visible",
571 P_("Headers Visible"),
572 P_("Show the column header buttons"),
574 GTK_PARAM_READWRITE));
576 g_object_class_install_property (o_class,
577 PROP_HEADERS_CLICKABLE,
578 g_param_spec_boolean ("headers-clickable",
579 P_("Headers Clickable"),
580 P_("Column headers respond to click events"),
582 GTK_PARAM_READWRITE));
584 g_object_class_install_property (o_class,
586 g_param_spec_boolean ("reorderable",
588 P_("View is reorderable"),
590 GTK_PARAM_READWRITE));
592 g_object_class_install_property (o_class,
594 g_param_spec_boolean ("rules-hint",
596 P_("Set a hint to the theme engine to draw rows in alternating colors"),
598 GTK_PARAM_READWRITE));
600 g_object_class_install_property (o_class,
602 g_param_spec_boolean ("enable-search",
604 P_("View allows user to search through columns interactively"),
606 GTK_PARAM_READWRITE));
608 g_object_class_install_property (o_class,
610 g_param_spec_int ("search-column",
612 P_("Model column to search through during interactive search"),
616 GTK_PARAM_READWRITE));
619 * PsppSheetView:hover-selection:
621 * Enables of disables the hover selection mode of @tree_view.
622 * Hover selection makes the selected row follow the pointer.
623 * Currently, this works only for the selection modes
624 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
626 * This mode is primarily intended for treeviews in popups, e.g.
627 * in #GtkComboBox or #GtkEntryCompletion.
631 g_object_class_install_property (o_class,
632 PROP_HOVER_SELECTION,
633 g_param_spec_boolean ("hover-selection",
634 P_("Hover Selection"),
635 P_("Whether the selection should follow the pointer"),
637 GTK_PARAM_READWRITE));
639 g_object_class_install_property (o_class,
641 g_param_spec_boolean ("rubber-banding",
642 P_("Rubber Banding"),
643 P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
645 GTK_PARAM_READWRITE));
647 g_object_class_install_property (o_class,
648 PROP_ENABLE_GRID_LINES,
649 g_param_spec_enum ("enable-grid-lines",
650 P_("Enable Grid Lines"),
651 P_("Whether grid lines should be drawn in the tree view"),
652 PSPP_TYPE_SHEET_VIEW_GRID_LINES,
653 PSPP_SHEET_VIEW_GRID_LINES_NONE,
654 GTK_PARAM_READWRITE));
656 g_object_class_install_property (o_class,
658 g_param_spec_int ("tooltip-column",
659 P_("Tooltip Column"),
660 P_("The column in the model containing the tooltip texts for the rows"),
664 GTK_PARAM_READWRITE));
666 g_object_class_install_property (o_class,
668 g_param_spec_enum ("special-cells",
670 P_("Whether rows have special cells."),
671 PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS,
672 PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT,
673 GTK_PARAM_READWRITE));
675 g_object_class_install_property (o_class,
677 g_param_spec_int ("fixed-height",
679 P_("Height of a single row. Normally the height of a row is determined automatically. Writing this property sets fixed-height-set to true, preventing this property's value from changing."),
683 GTK_PARAM_READWRITE));
685 g_object_class_install_property (o_class,
686 PROP_FIXED_HEIGHT_SET,
687 g_param_spec_boolean ("fixed-height-set",
688 P_("Fixed Height Set"),
689 P_("Whether fixed-height was set externally."),
691 GTK_PARAM_READWRITE));
693 /* Style properties */
694 #define _TREE_VIEW_EXPANDER_SIZE 12
695 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
696 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
698 gtk_widget_class_install_style_property (widget_class,
699 g_param_spec_int ("expander-size",
701 P_("Size of the expander arrow"),
704 _TREE_VIEW_EXPANDER_SIZE,
705 GTK_PARAM_READABLE));
707 gtk_widget_class_install_style_property (widget_class,
708 g_param_spec_int ("vertical-separator",
709 P_("Vertical Separator Width"),
710 P_("Vertical space between cells. Must be an even number"),
713 _TREE_VIEW_VERTICAL_SEPARATOR,
714 GTK_PARAM_READABLE));
716 gtk_widget_class_install_style_property (widget_class,
717 g_param_spec_int ("horizontal-separator",
718 P_("Horizontal Separator Width"),
719 P_("Horizontal space between cells. Must be an even number"),
722 _TREE_VIEW_HORIZONTAL_SEPARATOR,
723 GTK_PARAM_READABLE));
725 gtk_widget_class_install_style_property (widget_class,
726 g_param_spec_boolean ("allow-rules",
728 P_("Allow drawing of alternating color rows"),
730 GTK_PARAM_READABLE));
732 gtk_widget_class_install_style_property (widget_class,
733 g_param_spec_boxed ("even-row-color",
734 P_("Even Row Color"),
735 P_("Color to use for even rows"),
737 GTK_PARAM_READABLE));
739 gtk_widget_class_install_style_property (widget_class,
740 g_param_spec_boxed ("odd-row-color",
742 P_("Color to use for odd rows"),
744 GTK_PARAM_READABLE));
746 gtk_widget_class_install_style_property (widget_class,
747 g_param_spec_boolean ("row-ending-details",
748 P_("Row Ending details"),
749 P_("Enable extended row background theming"),
751 GTK_PARAM_READABLE));
753 gtk_widget_class_install_style_property (widget_class,
754 g_param_spec_int ("grid-line-width",
755 P_("Grid line width"),
756 P_("Width, in pixels, of the tree view grid lines"),
758 GTK_PARAM_READABLE));
760 gtk_widget_class_install_style_property (widget_class,
761 g_param_spec_int ("tree-line-width",
762 P_("Tree line width"),
763 P_("Width, in pixels, of the tree view lines"),
765 GTK_PARAM_READABLE));
767 gtk_widget_class_install_style_property (widget_class,
768 g_param_spec_string ("tree-line-pattern",
769 P_("Tree line pattern"),
770 P_("Dash pattern used to draw the tree view lines"),
772 GTK_PARAM_READABLE));
777 * PsppSheetView::set-scroll-adjustments
778 * @horizontal: the horizontal #GtkAdjustment
779 * @vertical: the vertical #GtkAdjustment
781 * Set the scroll adjustments for the tree view. Usually scrolled containers
782 * like #GtkScrolledWindow will emit this signal to connect two instances
783 * of #GtkScrollbar to the scroll directions of the #PsppSheetView.
785 widget_class->set_scroll_adjustments_signal =
786 g_signal_new ("set-scroll-adjustments",
787 G_TYPE_FROM_CLASS (o_class),
788 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
789 G_STRUCT_OFFSET (PsppSheetViewClass, set_scroll_adjustments),
791 psppire_marshal_VOID__OBJECT_OBJECT,
794 GTK_TYPE_ADJUSTMENT);
798 * PsppSheetView::row-activated:
799 * @tree_view: the object on which the signal is emitted
800 * @path: the #GtkTreePath for the activated row
801 * @column: the #PsppSheetViewColumn in which the activation occurred
803 * The "row-activated" signal is emitted when the method
804 * pspp_sheet_view_row_activated() is called or the user double clicks
805 * a treeview row. It is also emitted when a non-editable row is
806 * selected and one of the keys: Space, Shift+Space, Return or
809 * For selection handling refer to the <link linkend="TreeWidget">tree
810 * widget conceptual overview</link> as well as #PsppSheetSelection.
812 tree_view_signals[ROW_ACTIVATED] =
813 g_signal_new ("row-activated",
814 G_TYPE_FROM_CLASS (o_class),
815 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
816 G_STRUCT_OFFSET (PsppSheetViewClass, row_activated),
818 psppire_marshal_VOID__BOXED_OBJECT,
821 PSPP_TYPE_SHEET_VIEW_COLUMN);
824 * PsppSheetView::columns-changed:
825 * @tree_view: the object on which the signal is emitted
827 * The number of columns of the treeview has changed.
829 tree_view_signals[COLUMNS_CHANGED] =
830 g_signal_new ("columns-changed",
831 G_TYPE_FROM_CLASS (o_class),
833 G_STRUCT_OFFSET (PsppSheetViewClass, columns_changed),
835 g_cclosure_marshal_VOID__VOID,
839 * PsppSheetView::cursor-changed:
840 * @tree_view: the object on which the signal is emitted
842 * The position of the cursor (focused cell) has changed.
844 tree_view_signals[CURSOR_CHANGED] =
845 g_signal_new ("cursor-changed",
846 G_TYPE_FROM_CLASS (o_class),
848 G_STRUCT_OFFSET (PsppSheetViewClass, cursor_changed),
850 g_cclosure_marshal_VOID__VOID,
853 tree_view_signals[MOVE_CURSOR] =
854 g_signal_new ("move-cursor",
855 G_TYPE_FROM_CLASS (o_class),
856 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
857 G_STRUCT_OFFSET (PsppSheetViewClass, move_cursor),
859 psppire_marshal_BOOLEAN__ENUM_INT,
861 GTK_TYPE_MOVEMENT_STEP,
864 tree_view_signals[SELECT_ALL] =
865 g_signal_new ("select-all",
866 G_TYPE_FROM_CLASS (o_class),
867 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
868 G_STRUCT_OFFSET (PsppSheetViewClass, select_all),
870 psppire_marshal_BOOLEAN__VOID,
873 tree_view_signals[UNSELECT_ALL] =
874 g_signal_new ("unselect-all",
875 G_TYPE_FROM_CLASS (o_class),
876 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
877 G_STRUCT_OFFSET (PsppSheetViewClass, unselect_all),
879 psppire_marshal_BOOLEAN__VOID,
882 tree_view_signals[SELECT_CURSOR_ROW] =
883 g_signal_new ("select-cursor-row",
884 G_TYPE_FROM_CLASS (o_class),
885 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
886 G_STRUCT_OFFSET (PsppSheetViewClass, select_cursor_row),
888 psppire_marshal_BOOLEAN__BOOLEAN,
890 G_TYPE_BOOLEAN, G_TYPE_INT);
892 tree_view_signals[TOGGLE_CURSOR_ROW] =
893 g_signal_new ("toggle-cursor-row",
894 G_TYPE_FROM_CLASS (o_class),
895 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
896 G_STRUCT_OFFSET (PsppSheetViewClass, toggle_cursor_row),
898 psppire_marshal_BOOLEAN__VOID,
901 tree_view_signals[START_INTERACTIVE_SEARCH] =
902 g_signal_new ("start-interactive-search",
903 G_TYPE_FROM_CLASS (o_class),
904 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
905 G_STRUCT_OFFSET (PsppSheetViewClass, start_interactive_search),
907 psppire_marshal_BOOLEAN__VOID,
911 for (i = 0; i < 2; i++)
913 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Up, 0, TRUE,
914 GTK_MOVEMENT_DISPLAY_LINES, -1);
915 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Up, 0, TRUE,
916 GTK_MOVEMENT_DISPLAY_LINES, -1);
918 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Down, 0, TRUE,
919 GTK_MOVEMENT_DISPLAY_LINES, 1);
920 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Down, 0, TRUE,
921 GTK_MOVEMENT_DISPLAY_LINES, 1);
923 pspp_sheet_view_add_move_binding (binding_set[i], GDK_p, GDK_CONTROL_MASK, FALSE,
924 GTK_MOVEMENT_DISPLAY_LINES, -1);
926 pspp_sheet_view_add_move_binding (binding_set[i], GDK_n, GDK_CONTROL_MASK, FALSE,
927 GTK_MOVEMENT_DISPLAY_LINES, 1);
929 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Home, 0, TRUE,
930 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
931 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Home, 0, TRUE,
932 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
934 pspp_sheet_view_add_move_binding (binding_set[i], GDK_End, 0, TRUE,
935 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
936 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_End, 0, TRUE,
937 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
939 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Up, 0, TRUE,
940 GTK_MOVEMENT_PAGES, -1);
941 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Up, 0, TRUE,
942 GTK_MOVEMENT_PAGES, -1);
944 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Down, 0, TRUE,
945 GTK_MOVEMENT_PAGES, 1);
946 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Down, 0, TRUE,
947 GTK_MOVEMENT_PAGES, 1);
950 gtk_binding_entry_add_signal (binding_set[i], GDK_Up, GDK_CONTROL_MASK, "move-cursor", 2,
951 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
954 gtk_binding_entry_add_signal (binding_set[i], GDK_Down, GDK_CONTROL_MASK, "move-cursor", 2,
955 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
958 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, 0, "move-cursor", 2,
959 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
962 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, 0, "move-cursor", 2,
963 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
966 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, 0, "move-cursor", 2,
967 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
970 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, GDK_SHIFT_MASK, "move-cursor", 2,
971 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
974 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, 0, "move-cursor", 2,
975 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
978 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, 0, "move-cursor", 2,
979 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
982 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, GDK_CONTROL_MASK,
984 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
987 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, GDK_CONTROL_MASK,
989 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
992 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, GDK_CONTROL_MASK,
994 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
997 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, GDK_CONTROL_MASK,
999 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1002 gtk_binding_entry_add_signal (binding_set[i], GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
1004 gtk_binding_entry_add_signal (binding_set[i], GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
1007 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1008 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1010 gtk_binding_entry_add_signal (binding_set[0], GDK_a, GDK_CONTROL_MASK, "select-all", 0);
1011 gtk_binding_entry_add_signal (binding_set[0], GDK_slash, GDK_CONTROL_MASK, "select-all", 0);
1013 gtk_binding_entry_add_signal (binding_set[0], GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
1014 gtk_binding_entry_add_signal (binding_set[0], GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0);
1016 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1017 G_TYPE_BOOLEAN, TRUE,
1018 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1019 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1020 G_TYPE_BOOLEAN, TRUE,
1021 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1023 gtk_binding_entry_add_signal (binding_set[0], GDK_space, 0, "select-cursor-row", 1,
1024 G_TYPE_BOOLEAN, TRUE,
1026 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, 0, "select-cursor-row", 1,
1027 G_TYPE_BOOLEAN, TRUE,
1029 gtk_binding_entry_add_signal (binding_set[0], GDK_Return, 0, "select-cursor-row", 1,
1030 G_TYPE_BOOLEAN, TRUE,
1032 gtk_binding_entry_add_signal (binding_set[0], GDK_ISO_Enter, 0, "select-cursor-row", 1,
1033 G_TYPE_BOOLEAN, TRUE,
1035 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Enter, 0, "select-cursor-row", 1,
1036 G_TYPE_BOOLEAN, TRUE,
1039 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, 0, "select-cursor-parent", 0);
1040 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0);
1042 g_type_class_add_private (o_class, sizeof (PsppSheetViewPrivate));
1046 pspp_sheet_view_buildable_init (GtkBuildableIface *iface)
1048 iface->add_child = pspp_sheet_view_buildable_add_child;
1052 pspp_sheet_view_init (PsppSheetView *tree_view)
1054 tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, PSPP_TYPE_SHEET_VIEW, PsppSheetViewPrivate);
1056 gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
1057 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE);
1059 tree_view->priv->flags = PSPP_SHEET_VIEW_DRAW_KEYFOCUS
1060 | PSPP_SHEET_VIEW_HEADERS_VISIBLE;
1062 /* We need some padding */
1063 tree_view->priv->selected = range_tower_create ();
1064 tree_view->priv->dy = 0;
1065 tree_view->priv->cursor_offset = 0;
1066 tree_view->priv->n_columns = 0;
1067 tree_view->priv->header_height = 1;
1068 tree_view->priv->x_drag = 0;
1069 tree_view->priv->drag_pos = -1;
1070 tree_view->priv->header_has_focus = FALSE;
1071 tree_view->priv->pressed_button = -1;
1072 tree_view->priv->press_start_x = -1;
1073 tree_view->priv->press_start_y = -1;
1074 tree_view->priv->reorderable = FALSE;
1075 tree_view->priv->presize_handler_timer = 0;
1076 tree_view->priv->scroll_sync_timer = 0;
1077 tree_view->priv->fixed_height = -1;
1078 tree_view->priv->fixed_height_set = FALSE;
1079 pspp_sheet_view_set_adjustments (tree_view, NULL, NULL);
1080 tree_view->priv->selection = _pspp_sheet_selection_new_with_tree_view (tree_view);
1081 tree_view->priv->enable_search = TRUE;
1082 tree_view->priv->search_column = -1;
1083 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
1084 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
1085 tree_view->priv->search_custom_entry_set = FALSE;
1086 tree_view->priv->typeselect_flush_timeout = 0;
1087 tree_view->priv->init_hadjust_value = TRUE;
1088 tree_view->priv->width = 0;
1090 tree_view->priv->hover_selection = FALSE;
1092 tree_view->priv->rubber_banding_enable = FALSE;
1094 tree_view->priv->grid_lines = PSPP_SHEET_VIEW_GRID_LINES_NONE;
1096 tree_view->priv->tooltip_column = -1;
1098 tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT;
1100 tree_view->priv->post_validation_flag = FALSE;
1102 tree_view->priv->last_button_x = -1;
1103 tree_view->priv->last_button_y = -1;
1105 tree_view->priv->event_last_x = -10000;
1106 tree_view->priv->event_last_y = -10000;
1108 tree_view->priv->prelight_node = -1;
1109 tree_view->priv->rubber_band_start_node = -1;
1110 tree_view->priv->rubber_band_end_node = -1;
1112 tree_view->priv->anchor_column = NULL;
1114 tree_view->priv->button_style = NULL;
1116 tree_view->dispose_has_run = FALSE;
1118 pspp_sheet_view_do_set_vadjustment (tree_view, NULL);
1119 pspp_sheet_view_do_set_hadjustment (tree_view, NULL);
1120 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (tree_view)),
1121 GTK_STYLE_CLASS_VIEW);
1130 pspp_sheet_view_set_property (GObject *object,
1132 const GValue *value,
1135 PsppSheetView *tree_view;
1137 tree_view = PSPP_SHEET_VIEW (object);
1142 pspp_sheet_view_set_model (tree_view, g_value_get_object (value));
1144 case PROP_HADJUSTMENT:
1145 pspp_sheet_view_do_set_hadjustment (tree_view, g_value_get_object (value));
1147 case PROP_VADJUSTMENT:
1148 pspp_sheet_view_do_set_vadjustment (tree_view, g_value_get_object (value));
1150 case PROP_HSCROLL_POLICY:
1151 tree_view->priv->hscroll_policy = g_value_get_enum (value);
1152 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1154 case PROP_VSCROLL_POLICY:
1155 tree_view->priv->vscroll_policy = g_value_get_enum (value);
1156 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1158 case PROP_HEADERS_VISIBLE:
1159 pspp_sheet_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1161 case PROP_HEADERS_CLICKABLE:
1162 pspp_sheet_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1164 case PROP_REORDERABLE:
1165 pspp_sheet_view_set_reorderable (tree_view, g_value_get_boolean (value));
1167 case PROP_RULES_HINT:
1168 pspp_sheet_view_set_rules_hint (tree_view, g_value_get_boolean (value));
1170 case PROP_ENABLE_SEARCH:
1171 pspp_sheet_view_set_enable_search (tree_view, g_value_get_boolean (value));
1173 case PROP_SEARCH_COLUMN:
1174 pspp_sheet_view_set_search_column (tree_view, g_value_get_int (value));
1176 case PROP_HOVER_SELECTION:
1177 tree_view->priv->hover_selection = g_value_get_boolean (value);
1179 case PROP_RUBBER_BANDING:
1180 tree_view->priv->rubber_banding_enable = g_value_get_boolean (value);
1182 case PROP_ENABLE_GRID_LINES:
1183 pspp_sheet_view_set_grid_lines (tree_view, g_value_get_enum (value));
1185 case PROP_TOOLTIP_COLUMN:
1186 pspp_sheet_view_set_tooltip_column (tree_view, g_value_get_int (value));
1188 case PROP_SPECIAL_CELLS:
1189 pspp_sheet_view_set_special_cells (tree_view, g_value_get_enum (value));
1191 case PROP_FIXED_HEIGHT:
1192 pspp_sheet_view_set_fixed_height (tree_view, g_value_get_int (value));
1194 case PROP_FIXED_HEIGHT_SET:
1195 if (g_value_get_boolean (value))
1197 if (!tree_view->priv->fixed_height_set
1198 && tree_view->priv->fixed_height >= 0)
1200 tree_view->priv->fixed_height_set = true;
1201 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1206 if (tree_view->priv->fixed_height_set)
1208 tree_view->priv->fixed_height_set = false;
1209 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1210 install_presize_handler (tree_view);
1215 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1221 pspp_sheet_view_get_property (GObject *object,
1226 PsppSheetView *tree_view;
1228 tree_view = PSPP_SHEET_VIEW (object);
1233 g_value_set_object (value, tree_view->priv->model);
1235 case PROP_HADJUSTMENT:
1236 g_value_set_object (value, tree_view->priv->hadjustment);
1238 case PROP_VADJUSTMENT:
1239 g_value_set_object (value, tree_view->priv->vadjustment);
1241 case PROP_HSCROLL_POLICY:
1242 g_value_set_enum (value, tree_view->priv->hscroll_policy);
1244 case PROP_VSCROLL_POLICY:
1245 g_value_set_enum (value, tree_view->priv->vscroll_policy);
1247 case PROP_HEADERS_VISIBLE:
1248 g_value_set_boolean (value, pspp_sheet_view_get_headers_visible (tree_view));
1250 case PROP_HEADERS_CLICKABLE:
1251 g_value_set_boolean (value, pspp_sheet_view_get_headers_clickable (tree_view));
1253 case PROP_REORDERABLE:
1254 g_value_set_boolean (value, tree_view->priv->reorderable);
1256 case PROP_RULES_HINT:
1257 g_value_set_boolean (value, tree_view->priv->has_rules);
1259 case PROP_ENABLE_SEARCH:
1260 g_value_set_boolean (value, tree_view->priv->enable_search);
1262 case PROP_SEARCH_COLUMN:
1263 g_value_set_int (value, tree_view->priv->search_column);
1265 case PROP_HOVER_SELECTION:
1266 g_value_set_boolean (value, tree_view->priv->hover_selection);
1268 case PROP_RUBBER_BANDING:
1269 g_value_set_boolean (value, tree_view->priv->rubber_banding_enable);
1271 case PROP_ENABLE_GRID_LINES:
1272 g_value_set_enum (value, tree_view->priv->grid_lines);
1274 case PROP_TOOLTIP_COLUMN:
1275 g_value_set_int (value, tree_view->priv->tooltip_column);
1277 case PROP_SPECIAL_CELLS:
1278 g_value_set_enum (value, tree_view->priv->special_cells);
1280 case PROP_FIXED_HEIGHT:
1281 g_value_set_int (value, pspp_sheet_view_get_fixed_height (tree_view));
1283 case PROP_FIXED_HEIGHT_SET:
1284 g_value_set_boolean (value, tree_view->priv->fixed_height_set);
1287 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1293 pspp_sheet_view_dispose (GObject *object)
1295 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1297 if (tree_view->dispose_has_run)
1300 tree_view->dispose_has_run = TRUE;
1302 if (tree_view->priv->selection != NULL)
1304 _pspp_sheet_selection_set_tree_view (tree_view->priv->selection, NULL);
1305 g_object_unref (tree_view->priv->selection);
1306 tree_view->priv->selection = NULL;
1309 if (tree_view->priv->hadjustment)
1311 g_object_unref (tree_view->priv->hadjustment);
1312 tree_view->priv->hadjustment = NULL;
1314 if (tree_view->priv->vadjustment)
1316 g_object_unref (tree_view->priv->vadjustment);
1317 tree_view->priv->vadjustment = NULL;
1320 if (tree_view->priv->button_style)
1322 g_object_unref (tree_view->priv->button_style);
1323 tree_view->priv->button_style = NULL;
1327 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->dispose (object);
1333 pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
1334 GtkBuilder *builder,
1338 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), PSPP_SHEET_VIEW_COLUMN (child));
1342 pspp_sheet_view_finalize (GObject *object)
1344 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1346 pspp_sheet_view_stop_editing (tree_view, TRUE);
1348 if (tree_view->priv->selected != NULL)
1350 range_tower_destroy (tree_view->priv->selected);
1351 tree_view->priv->selected = NULL;
1355 tree_view->priv->prelight_node = -1;
1358 if (tree_view->priv->scroll_to_path != NULL)
1360 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
1361 tree_view->priv->scroll_to_path = NULL;
1364 if (tree_view->priv->drag_dest_row != NULL)
1366 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
1367 tree_view->priv->drag_dest_row = NULL;
1370 if (tree_view->priv->top_row != NULL)
1372 gtk_tree_row_reference_free (tree_view->priv->top_row);
1373 tree_view->priv->top_row = NULL;
1376 if (tree_view->priv->column_drop_func_data &&
1377 tree_view->priv->column_drop_func_data_destroy)
1379 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
1380 tree_view->priv->column_drop_func_data = NULL;
1383 if (tree_view->priv->destroy_count_destroy &&
1384 tree_view->priv->destroy_count_data)
1386 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
1387 tree_view->priv->destroy_count_data = NULL;
1390 gtk_tree_row_reference_free (tree_view->priv->cursor);
1391 tree_view->priv->cursor = NULL;
1393 gtk_tree_row_reference_free (tree_view->priv->anchor);
1394 tree_view->priv->anchor = NULL;
1396 /* destroy interactive search dialog */
1397 if (tree_view->priv->search_window)
1399 gtk_widget_destroy (tree_view->priv->search_window);
1400 tree_view->priv->search_window = NULL;
1401 tree_view->priv->search_entry = NULL;
1402 if (tree_view->priv->typeselect_flush_timeout)
1404 g_source_remove (tree_view->priv->typeselect_flush_timeout);
1405 tree_view->priv->typeselect_flush_timeout = 0;
1409 if (tree_view->priv->search_destroy && tree_view->priv->search_user_data)
1411 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
1412 tree_view->priv->search_user_data = NULL;
1415 if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data)
1417 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
1418 tree_view->priv->search_position_user_data = NULL;
1421 pspp_sheet_view_set_model (tree_view, NULL);
1424 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->finalize (object);
1429 /* GtkWidget Methods
1432 /* GtkWidget::map helper */
1434 pspp_sheet_view_map_buttons (PsppSheetView *tree_view)
1438 g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
1440 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
1442 PsppSheetViewColumn *column;
1444 for (list = tree_view->priv->columns; list; list = list->next)
1446 column = list->data;
1447 if (column->button != NULL &&
1448 gtk_widget_get_visible (column->button) &&
1449 !gtk_widget_get_mapped (column->button))
1450 gtk_widget_map (column->button);
1452 for (list = tree_view->priv->columns; list; list = list->next)
1454 column = list->data;
1455 if (column->visible == FALSE || column->window == NULL)
1457 if (column->resizable)
1459 gdk_window_raise (column->window);
1460 gdk_window_show (column->window);
1463 gdk_window_hide (column->window);
1465 gdk_window_show (tree_view->priv->header_window);
1470 pspp_sheet_view_map (GtkWidget *widget)
1472 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1475 gtk_widget_set_mapped (widget, TRUE);
1477 tmp_list = tree_view->priv->children;
1480 PsppSheetViewChild *child = tmp_list->data;
1481 tmp_list = tmp_list->next;
1483 if (gtk_widget_get_visible (child->widget))
1485 if (!gtk_widget_get_mapped (child->widget))
1486 gtk_widget_map (child->widget);
1489 gdk_window_show (tree_view->priv->bin_window);
1491 pspp_sheet_view_map_buttons (tree_view);
1493 gdk_window_show (gtk_widget_get_window (widget));
1497 pspp_sheet_view_realize (GtkWidget *widget)
1499 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1502 GdkWindowAttr attributes;
1503 gint attributes_mask;
1504 GtkAllocation allocation;
1506 gtk_widget_set_realized (widget, TRUE);
1508 gtk_widget_get_allocation (widget, &allocation);
1510 /* Make the main, clipping window */
1511 attributes.window_type = GDK_WINDOW_CHILD;
1512 attributes.x = allocation.x;
1513 attributes.y = allocation.y;
1514 attributes.width = allocation.width;
1515 attributes.height = allocation.height;
1516 attributes.wclass = GDK_INPUT_OUTPUT;
1517 attributes.visual = gtk_widget_get_visual (widget);
1518 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1520 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
1522 window = gdk_window_new (gtk_widget_get_parent_window (widget),
1523 &attributes, attributes_mask);
1524 gtk_widget_set_window (widget, window);
1526 gtk_widget_register_window (widget, window);
1527 gtk_widget_get_allocation (widget, &allocation);
1529 /* Make the window for the tree */
1531 attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
1532 attributes.width = MAX (tree_view->priv->width, allocation.width);
1533 attributes.height = allocation.height;
1534 attributes.event_mask = (GDK_EXPOSURE_MASK |
1536 GDK_POINTER_MOTION_MASK |
1537 GDK_ENTER_NOTIFY_MASK |
1538 GDK_LEAVE_NOTIFY_MASK |
1539 GDK_BUTTON_PRESS_MASK |
1540 GDK_BUTTON_RELEASE_MASK |
1541 gtk_widget_get_events (widget));
1543 tree_view->priv->bin_window = gdk_window_new (window,
1544 &attributes, attributes_mask);
1545 gtk_widget_register_window (widget, tree_view->priv->bin_window);
1546 gtk_widget_get_allocation (widget, &allocation);
1548 /* Make the column header window */
1551 attributes.width = MAX (tree_view->priv->width, allocation.width);
1552 attributes.height = tree_view->priv->header_height;
1553 attributes.event_mask = (GDK_EXPOSURE_MASK |
1555 GDK_BUTTON_PRESS_MASK |
1556 GDK_BUTTON_RELEASE_MASK |
1557 GDK_KEY_PRESS_MASK |
1558 GDK_KEY_RELEASE_MASK |
1559 gtk_widget_get_events (widget));
1561 tree_view->priv->header_window = gdk_window_new (window,
1562 &attributes, attributes_mask);
1563 gtk_widget_register_window (widget, tree_view->priv->header_window);
1565 { /* Ensure Background */
1566 GtkStyleContext *context;
1568 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
1570 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
1571 gtk_style_context_set_background (context, tree_view->priv->header_window);
1574 tmp_list = tree_view->priv->children;
1577 PsppSheetViewChild *child = tmp_list->data;
1578 tmp_list = tmp_list->next;
1580 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
1583 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
1584 _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list->data));
1586 /* Need to call those here, since they create GCs */
1587 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
1589 install_presize_handler (tree_view);
1593 pspp_sheet_view_unrealize (GtkWidget *widget)
1595 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1596 PsppSheetViewPrivate *priv = tree_view->priv;
1599 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget);
1601 if (priv->scroll_timeout != 0)
1603 g_source_remove (priv->scroll_timeout);
1604 priv->scroll_timeout = 0;
1607 if (priv->open_dest_timeout != 0)
1609 g_source_remove (priv->open_dest_timeout);
1610 priv->open_dest_timeout = 0;
1613 if (priv->presize_handler_timer != 0)
1615 g_source_remove (priv->presize_handler_timer);
1616 priv->presize_handler_timer = 0;
1619 if (priv->validate_rows_timer != 0)
1621 g_source_remove (priv->validate_rows_timer);
1622 priv->validate_rows_timer = 0;
1625 if (priv->scroll_sync_timer != 0)
1627 g_source_remove (priv->scroll_sync_timer);
1628 priv->scroll_sync_timer = 0;
1631 if (priv->typeselect_flush_timeout)
1633 g_source_remove (priv->typeselect_flush_timeout);
1634 priv->typeselect_flush_timeout = 0;
1637 for (list = priv->columns; list; list = list->next)
1638 _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list->data));
1640 gdk_window_set_user_data (priv->bin_window, NULL);
1641 gdk_window_destroy (priv->bin_window);
1642 priv->bin_window = NULL;
1644 gdk_window_set_user_data (priv->header_window, NULL);
1645 gdk_window_destroy (priv->header_window);
1646 priv->header_window = NULL;
1648 if (priv->drag_window)
1650 gdk_window_set_user_data (priv->drag_window, NULL);
1651 gdk_window_destroy (priv->drag_window);
1652 priv->drag_window = NULL;
1655 if (priv->drag_highlight_window)
1657 gdk_window_set_user_data (priv->drag_highlight_window, NULL);
1658 gdk_window_destroy (priv->drag_highlight_window);
1659 priv->drag_highlight_window = NULL;
1662 if (tree_view->priv->columns != NULL)
1664 list = tree_view->priv->columns;
1667 PsppSheetViewColumn *column;
1668 column = PSPP_SHEET_VIEW_COLUMN (list->data);
1670 pspp_sheet_view_remove_column (tree_view, column);
1672 tree_view->priv->columns = NULL;
1676 /* GtkWidget::size_request helper */
1678 pspp_sheet_view_size_request_columns (PsppSheetView *tree_view)
1682 tree_view->priv->header_height = 0;
1684 if (tree_view->priv->model)
1686 for (list = tree_view->priv->columns; list; list = list->next)
1688 GtkRequisition requisition;
1689 PsppSheetViewColumn *column = list->data;
1691 pspp_sheet_view_column_size_request (column, &requisition);
1692 column->button_request = requisition.width;
1693 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
1699 /* Called only by ::size_request */
1701 pspp_sheet_view_update_size (PsppSheetView *tree_view)
1704 PsppSheetViewColumn *column;
1707 if (tree_view->priv->model == NULL)
1709 tree_view->priv->width = 0;
1710 tree_view->priv->prev_width = 0;
1711 tree_view->priv->height = 0;
1715 tree_view->priv->prev_width = tree_view->priv->width;
1716 tree_view->priv->width = 0;
1718 /* keep this in sync with size_allocate below */
1719 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
1721 gint real_requested_width = 0;
1722 column = list->data;
1723 if (!column->visible)
1726 if (column->use_resized_width)
1728 real_requested_width = column->resized_width;
1732 real_requested_width = column->fixed_width;
1735 if (column->min_width != -1)
1736 real_requested_width = MAX (real_requested_width, column->min_width);
1737 if (column->max_width != -1)
1738 real_requested_width = MIN (real_requested_width, column->max_width);
1740 tree_view->priv->width += real_requested_width;
1743 tree_view->priv->height = tree_view->priv->fixed_height * tree_view->priv->row_count;
1747 pspp_sheet_view_size_request (GtkWidget *widget,
1748 GtkRequisition *requisition)
1750 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1753 /* we validate some rows initially just to make sure we have some size.
1754 * In practice, with a lot of static lists, this should get a good width.
1756 initialize_fixed_height_mode (tree_view);
1757 pspp_sheet_view_size_request_columns (tree_view);
1758 pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget));
1760 requisition->width = tree_view->priv->width;
1761 requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
1763 tmp_list = tree_view->priv->children;
1767 PsppSheetViewChild *child = tmp_list->data;
1768 GtkRequisition child_requisition;
1770 tmp_list = tmp_list->next;
1772 if (gtk_widget_get_visible (child->widget))
1774 gtk_widget_get_preferred_size (child->widget, NULL, &child_requisition);
1780 invalidate_column (PsppSheetView *tree_view,
1781 PsppSheetViewColumn *column)
1783 gint column_offset = 0;
1785 GtkWidget *widget = GTK_WIDGET (tree_view);
1788 if (!gtk_widget_get_realized (widget))
1791 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1792 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
1794 list = (rtl ? list->prev : list->next))
1796 PsppSheetViewColumn *tmpcolumn = list->data;
1797 if (tmpcolumn == column)
1799 GdkRectangle invalid_rect;
1800 GtkAllocation allocation;
1802 gtk_widget_get_allocation (widget, &allocation);
1803 invalid_rect.x = column_offset;
1805 invalid_rect.width = column->width;
1806 invalid_rect.height = allocation.height;
1808 gdk_window_invalidate_rect (gtk_widget_get_window (widget), &invalid_rect, TRUE);
1812 column_offset += tmpcolumn->width;
1817 invalidate_last_column (PsppSheetView *tree_view)
1822 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1824 for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
1826 last_column = (rtl ? last_column->next : last_column->prev))
1828 if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible)
1830 invalidate_column (tree_view, last_column->data);
1837 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_view,
1838 PsppSheetViewColumn *column)
1840 gint real_requested_width;
1842 if (column->use_resized_width)
1844 real_requested_width = column->resized_width;
1848 real_requested_width = column->fixed_width;
1851 if (column->min_width != -1)
1852 real_requested_width = MAX (real_requested_width, column->min_width);
1853 if (column->max_width != -1)
1854 real_requested_width = MIN (real_requested_width, column->max_width);
1856 return real_requested_width;
1860 span_intersects (int a0, int a_width,
1861 int b0, int b_width)
1863 int a1 = a0 + a_width;
1864 int b1 = b0 + b_width;
1865 return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1);
1868 /* GtkWidget::size_allocate helper */
1870 pspp_sheet_view_size_allocate_columns (GtkWidget *widget,
1871 gboolean *width_changed)
1873 PsppSheetView *tree_view;
1874 GList *list, *first_column, *last_column;
1875 PsppSheetViewColumn *column;
1876 GtkAllocation col_allocation;
1877 GtkAllocation allocation;
1879 gint extra, extra_per_column;
1880 gint full_requested_width = 0;
1881 gint number_of_expand_columns = 0;
1882 gboolean column_changed = FALSE;
1885 tree_view = PSPP_SHEET_VIEW (widget);
1887 for (last_column = g_list_last (tree_view->priv->columns);
1888 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
1889 last_column = last_column->prev)
1892 if (last_column == NULL)
1895 for (first_column = g_list_first (tree_view->priv->columns);
1896 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
1897 first_column = first_column->next)
1900 col_allocation.y = 0;
1901 col_allocation.height = tree_view->priv->header_height;
1903 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1905 /* find out how many extra space and expandable columns we have */
1906 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
1908 column = (PsppSheetViewColumn *)list->data;
1910 if (!column->visible)
1913 full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1916 number_of_expand_columns++;
1919 gtk_widget_get_allocation (widget, &allocation);
1920 extra = MAX (allocation.width - full_requested_width, 0);
1921 if (number_of_expand_columns > 0)
1922 extra_per_column = extra/number_of_expand_columns;
1924 extra_per_column = 0;
1926 for (list = (rtl ? last_column : first_column);
1927 list != (rtl ? first_column->prev : last_column->next);
1928 list = (rtl ? list->prev : list->next))
1930 gint real_requested_width = 0;
1933 column = list->data;
1934 old_width = column->width;
1936 if (!column->visible)
1939 /* We need to handle the dragged button specially.
1941 if (column == tree_view->priv->drag_column)
1943 GtkAllocation drag_allocation;
1944 drag_allocation.width = gdk_window_get_width (tree_view->priv->drag_window);
1945 drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window);
1946 drag_allocation.x = 0;
1947 drag_allocation.y = 0;
1948 pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column,
1950 width += drag_allocation.width;
1954 real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1956 col_allocation.x = width;
1957 column->width = real_requested_width;
1961 if (number_of_expand_columns == 1)
1963 /* We add the remander to the last column as
1965 column->width += extra;
1969 column->width += extra_per_column;
1970 extra -= extra_per_column;
1971 number_of_expand_columns --;
1975 if (column->width != old_width)
1976 g_object_notify (G_OBJECT (column), "width");
1978 col_allocation.width = column->width;
1979 width += column->width;
1981 if (column->width > old_width)
1982 column_changed = TRUE;
1984 pspp_sheet_view_column_size_allocate (column, &col_allocation);
1987 gdk_window_move_resize (column->window,
1988 col_allocation.x + (rtl ? 0 : col_allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
1990 TREE_VIEW_DRAG_WIDTH, col_allocation.height);
1993 /* We change the width here. The user might have been resizing columns,
1994 * so the total width of the tree view changes.
1996 tree_view->priv->width = width;
1998 *width_changed = TRUE;
2001 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2005 update_childrens_allocation (PsppSheetView *tree_view)
2008 for (tmp_list = tree_view->priv->children; tmp_list; tmp_list = tmp_list->next)
2010 PsppSheetViewChild *child = tmp_list->data;
2011 GtkAllocation allocation;
2014 /* totally ignore our child's requisition */
2015 path = _pspp_sheet_view_find_path (tree_view, child->node);
2016 pspp_sheet_view_get_cell_area (tree_view, path, child->column, &allocation);
2017 gtk_tree_path_free (path);
2018 gtk_widget_size_allocate (child->widget, &allocation);
2023 pspp_sheet_view_size_allocate (GtkWidget *widget,
2024 GtkAllocation *allocation)
2026 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2028 gboolean width_changed = FALSE;
2029 GtkAllocation old_allocation;
2030 gtk_widget_get_allocation (widget, &old_allocation);
2032 if (allocation->width != old_allocation.width)
2033 width_changed = TRUE;
2035 gtk_widget_set_allocation (widget, allocation);
2037 /* We size-allocate the columns first because the width of the
2038 * tree view (used in updating the adjustments below) might change.
2040 pspp_sheet_view_size_allocate_columns (widget, &width_changed);
2042 gtk_adjustment_set_page_size (tree_view->priv->hadjustment, allocation->width);
2043 gtk_adjustment_set_page_increment (tree_view->priv->hadjustment, allocation->width * 0.9);
2044 gtk_adjustment_set_step_increment (tree_view->priv->hadjustment, allocation->width * 0.1);
2045 gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0);
2046 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->hadjustment), tree_view->priv->width));
2048 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2050 if (allocation->width < tree_view->priv->width)
2052 if (tree_view->priv->init_hadjust_value)
2054 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2055 tree_view->priv->init_hadjust_value = FALSE;
2057 else if (allocation->width != old_allocation.width)
2059 gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) - allocation->width + old_allocation.width, 0, tree_view->priv->width - allocation->width));
2062 gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (tree_view->priv->width - (tree_view->priv->prev_width - gtk_adjustment_get_value (tree_view->priv->hadjustment)), 0, tree_view->priv->width - allocation->width));
2066 gtk_adjustment_set_value (tree_view->priv->hadjustment, 0);
2067 tree_view->priv->init_hadjust_value = TRUE;
2071 if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + allocation->width > tree_view->priv->width)
2072 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2074 gtk_adjustment_changed (tree_view->priv->hadjustment);
2076 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2077 gtk_adjustment_set_step_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.1);
2078 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.9);
2079 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
2080 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->vadjustment), tree_view->priv->height));
2082 gtk_adjustment_changed (tree_view->priv->vadjustment);
2084 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2085 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
2086 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2087 else if (gtk_adjustment_get_value (tree_view->priv->vadjustment) + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
2088 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2089 tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
2090 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2091 pspp_sheet_view_top_row_to_dy (tree_view);
2093 pspp_sheet_view_dy_to_top_row (tree_view);
2095 if (gtk_widget_get_realized (widget))
2097 gdk_window_move_resize (gtk_widget_get_window (widget),
2098 allocation->x, allocation->y,
2099 allocation->width, allocation->height);
2100 gdk_window_move_resize (tree_view->priv->header_window,
2101 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2103 MAX (tree_view->priv->width, allocation->width),
2104 tree_view->priv->header_height);
2105 gdk_window_move_resize (tree_view->priv->bin_window,
2106 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2107 TREE_VIEW_HEADER_HEIGHT (tree_view),
2108 MAX (tree_view->priv->width, allocation->width),
2109 allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2112 if (tree_view->priv->row_count == 0)
2113 invalidate_empty_focus (tree_view);
2115 if (gtk_widget_get_realized (widget))
2117 gboolean has_expand_column = FALSE;
2118 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2120 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2122 has_expand_column = TRUE;
2127 /* This little hack only works if we have an LTR locale, and no column has the */
2130 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2131 ! has_expand_column)
2132 invalidate_last_column (tree_view);
2134 gtk_widget_queue_draw (widget);
2136 update_childrens_allocation(tree_view);
2140 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2142 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2144 GtkWidget *widget = GTK_WIDGET (tree_view);
2146 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2147 gtk_widget_grab_focus (widget);
2148 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2152 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2155 return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2159 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2162 range_tower_set1 (tree_view->priv->selected, node, 1);
2166 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2169 range_tower_set0 (tree_view->priv->selected, node, 1);
2173 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2176 return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2180 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2183 return node > 0 ? node - 1 : -1;
2187 all_columns_selected (PsppSheetView *tree_view)
2191 for (list = tree_view->priv->columns; list; list = list->next)
2193 PsppSheetViewColumn *column = list->data;
2194 if (column->selectable && !column->selected)
2202 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2204 PsppSheetViewColumn *column,
2205 GdkEventButton *event)
2207 PsppSheetSelection *selection;
2208 PsppSheetSelectionMode mode;
2210 gboolean update_anchor;
2214 g_return_val_if_fail (tree_view != NULL, FALSE);
2215 g_return_val_if_fail (column != NULL, FALSE);
2217 selection = tree_view->priv->selection;
2218 mode = pspp_sheet_selection_get_mode (selection);
2219 if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2222 if (!column->row_head)
2227 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2228 if (event->type != GDK_BUTTON_PRESS
2229 || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2235 path = gtk_tree_path_new_from_indices (node, -1);
2238 pspp_sheet_selection_unselect_all (selection);
2239 pspp_sheet_selection_select_path (selection, path);
2240 pspp_sheet_selection_select_all_columns (selection);
2241 update_anchor = TRUE;
2244 else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2246 if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2247 || !all_columns_selected (tree_view))
2249 pspp_sheet_selection_unselect_all (selection);
2250 pspp_sheet_selection_select_path (selection, path);
2251 pspp_sheet_selection_select_all_columns (selection);
2252 update_anchor = TRUE;
2256 update_anchor = handled = FALSE;
2258 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2259 && modifiers == GDK_CONTROL_MASK)
2261 if (!all_columns_selected (tree_view))
2263 pspp_sheet_selection_unselect_all (selection);
2264 pspp_sheet_selection_select_all_columns (selection);
2267 if (pspp_sheet_selection_path_is_selected (selection, path))
2268 pspp_sheet_selection_unselect_path (selection, path);
2270 pspp_sheet_selection_select_path (selection, path);
2271 update_anchor = TRUE;
2274 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2275 && modifiers == GDK_SHIFT_MASK)
2277 GtkTreeRowReference *anchor = tree_view->priv->anchor;
2278 GtkTreePath *anchor_path;
2280 if (all_columns_selected (tree_view)
2281 && gtk_tree_row_reference_valid (anchor))
2283 update_anchor = FALSE;
2284 anchor_path = gtk_tree_row_reference_get_path (anchor);
2288 update_anchor = TRUE;
2289 anchor_path = gtk_tree_path_copy (path);
2292 pspp_sheet_selection_unselect_all (selection);
2293 pspp_sheet_selection_select_range (selection, anchor_path, path);
2294 pspp_sheet_selection_select_all_columns (selection);
2296 gtk_tree_path_free (anchor_path);
2301 update_anchor = handled = FALSE;
2305 if (tree_view->priv->anchor)
2306 gtk_tree_row_reference_free (tree_view->priv->anchor);
2307 tree_view->priv->anchor =
2308 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2309 tree_view->priv->model,
2313 gtk_tree_path_free (path);
2318 find_click (PsppSheetView *tree_view,
2321 PsppSheetViewColumn **column,
2322 GdkRectangle *background_area,
2323 GdkRectangle *cell_area)
2330 /* find the node that was clicked */
2331 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2334 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2339 background_area->y = y_offset + y;
2340 background_area->height = ROW_HEIGHT (tree_view);
2341 background_area->x = 0;
2343 /* Let the column have a chance at selecting it. */
2344 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2345 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2346 list; list = (rtl ? list->prev : list->next))
2348 PsppSheetViewColumn *candidate = list->data;
2350 if (!candidate->visible)
2353 background_area->width = candidate->width;
2354 if ((background_area->x > x) ||
2355 (background_area->x + background_area->width <= x))
2357 background_area->x += background_area->width;
2361 /* we found the focus column */
2363 pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2365 *column = candidate;
2373 pspp_sheet_view_button_press (GtkWidget *widget,
2374 GdkEventButton *event)
2376 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2378 PsppSheetViewColumn *column = NULL;
2380 GdkRectangle background_area;
2381 GdkRectangle cell_area;
2384 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2385 pspp_sheet_view_stop_editing (tree_view, FALSE);
2388 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2389 * we're done handling the button press.
2392 if (event->window == tree_view->priv->bin_window)
2397 gint pre_val, aft_val;
2398 PsppSheetViewColumn *column = NULL;
2399 GtkCellRenderer *focus_cell = NULL;
2400 gboolean row_double_click = FALSE;
2403 if (tree_view->priv->row_count == 0)
2405 grab_focus_and_unset_draw_keyfocus (tree_view);
2409 if (!find_click (tree_view, event->x, event->y, &node, &column,
2410 &background_area, &cell_area))
2412 grab_focus_and_unset_draw_keyfocus (tree_view);
2416 tree_view->priv->focus_column = column;
2418 if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2422 pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2424 path = _pspp_sheet_view_find_path (tree_view, node);
2426 /* we only handle selection modifications on the first button press
2428 if (event->type == GDK_BUTTON_PRESS)
2430 PsppSheetSelectionMode mode = 0;
2432 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2433 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
2434 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2435 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
2437 focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2439 pspp_sheet_view_column_focus_cell (column, focus_cell);
2441 if (event->state & GDK_CONTROL_MASK)
2443 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, mode);
2444 pspp_sheet_view_real_toggle_cursor_row (tree_view);
2446 else if (event->state & GDK_SHIFT_MASK)
2448 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
2449 pspp_sheet_view_real_select_cursor_row (tree_view, FALSE, mode);
2453 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
2456 if (tree_view->priv->anchor_column == NULL ||
2457 !(event->state & GDK_SHIFT_MASK))
2458 tree_view->priv->anchor_column = column;
2459 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2460 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2461 tree_view->priv->anchor_column,
2465 /* the treeview may have been scrolled because of _set_cursor,
2469 aft_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2470 dval = pre_val - aft_val;
2472 cell_area.y += dval;
2473 background_area.y += dval;
2475 /* Save press to possibly begin a drag
2477 if (!tree_view->priv->in_grab &&
2478 tree_view->priv->pressed_button < 0)
2480 tree_view->priv->pressed_button = event->button;
2481 tree_view->priv->press_start_x = event->x;
2482 tree_view->priv->press_start_y = event->y;
2483 tree_view->priv->press_start_node = node;
2485 if (tree_view->priv->rubber_banding_enable
2486 && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2487 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2489 tree_view->priv->press_start_y += tree_view->priv->dy;
2490 tree_view->priv->rubber_band_x = event->x;
2491 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2492 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2494 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2495 tree_view->priv->rubber_band_ctrl = TRUE;
2496 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2497 tree_view->priv->rubber_band_shift = TRUE;
2502 /* Test if a double click happened on the same row. */
2503 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2505 int double_click_time, double_click_distance;
2507 g_object_get (gtk_settings_get_for_screen (
2508 gtk_widget_get_screen (widget)),
2509 "gtk-double-click-time", &double_click_time,
2510 "gtk-double-click-distance", &double_click_distance,
2513 /* Same conditions as _gdk_event_button_generate */
2514 if (tree_view->priv->last_button_x != -1 &&
2515 (event->time < tree_view->priv->last_button_time + double_click_time) &&
2516 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2517 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2519 /* We do no longer compare paths of this row and the
2520 * row clicked previously. We use the double click
2521 * distance to decide whether this is a valid click,
2522 * allowing the mouse to slightly move over another row.
2524 row_double_click = TRUE;
2526 tree_view->priv->last_button_time = 0;
2527 tree_view->priv->last_button_x = -1;
2528 tree_view->priv->last_button_y = -1;
2532 tree_view->priv->last_button_time = event->time;
2533 tree_view->priv->last_button_x = event->x;
2534 tree_view->priv->last_button_y = event->y;
2538 if (row_double_click)
2540 gtk_grab_remove (widget);
2541 pspp_sheet_view_row_activated (tree_view, path, column);
2543 if (tree_view->priv->pressed_button == event->button)
2544 tree_view->priv->pressed_button = -1;
2547 gtk_tree_path_free (path);
2549 /* If we activated the row through a double click we don't want to grab
2550 * focus back, as moving focus to another widget is pretty common.
2552 if (!row_double_click)
2553 grab_focus_and_unset_draw_keyfocus (tree_view);
2558 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2560 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2562 column = list->data;
2563 if (event->window == column->window &&
2564 column->resizable &&
2569 if (GDK_GRAB_SUCCESS != gdk_device_grab (event->device,
2573 GDK_POINTER_MOTION_HINT_MASK |
2574 GDK_BUTTON1_MOTION_MASK |
2575 GDK_BUTTON_RELEASE_MASK,
2579 gtk_grab_add (widget);
2580 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2581 column->resized_width = column->width;
2583 /* block attached dnd signal handler */
2584 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2586 g_signal_handlers_block_matched (widget,
2587 G_SIGNAL_MATCH_DATA,
2591 tree_view->priv->drag_pos = i;
2592 tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2594 if (!gtk_widget_has_focus (widget))
2595 gtk_widget_grab_focus (widget);
2603 /* GtkWidget::button_release_event helper */
2605 pspp_sheet_view_button_release_drag_column (GtkWidget *widget,
2606 GdkEventButton *event)
2608 PsppSheetView *tree_view;
2612 tree_view = PSPP_SHEET_VIEW (widget);
2614 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2615 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2616 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2618 /* Move the button back */
2619 g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2621 g_object_ref (tree_view->priv->drag_column->button);
2622 gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2623 gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2624 gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2625 g_object_unref (tree_view->priv->drag_column->button);
2626 gtk_widget_queue_resize (widget);
2627 if (tree_view->priv->drag_column->resizable)
2629 gdk_window_raise (tree_view->priv->drag_column->window);
2630 gdk_window_show (tree_view->priv->drag_column->window);
2633 gdk_window_hide (tree_view->priv->drag_column->window);
2635 gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2639 if (tree_view->priv->cur_reorder &&
2640 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2641 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2642 tree_view->priv->cur_reorder->right_column);
2646 if (tree_view->priv->cur_reorder &&
2647 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2648 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2649 tree_view->priv->cur_reorder->left_column);
2651 tree_view->priv->drag_column = NULL;
2652 gdk_window_hide (tree_view->priv->drag_window);
2654 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2655 g_slice_free (PsppSheetViewColumnReorder, l->data);
2656 g_list_free (tree_view->priv->column_drag_info);
2657 tree_view->priv->column_drag_info = NULL;
2658 tree_view->priv->cur_reorder = NULL;
2660 if (tree_view->priv->drag_highlight_window)
2661 gdk_window_hide (tree_view->priv->drag_highlight_window);
2663 /* Reset our flags */
2664 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2665 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2670 /* GtkWidget::button_release_event helper */
2672 pspp_sheet_view_button_release_column_resize (GtkWidget *widget,
2673 GdkEventButton *event)
2675 PsppSheetView *tree_view;
2678 tree_view = PSPP_SHEET_VIEW (widget);
2680 tree_view->priv->drag_pos = -1;
2682 /* unblock attached dnd signal handler */
2683 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2685 g_signal_handlers_unblock_matched (widget,
2686 G_SIGNAL_MATCH_DATA,
2690 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2691 gtk_grab_remove (widget);
2692 gdk_device_ungrab (event->device, event->time);
2698 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2699 GdkEventButton *event)
2701 GtkCellEditable *cell_editable;
2706 PsppSheetViewColumn *column;
2707 GdkRectangle background_area;
2708 GdkRectangle cell_area;
2714 if (event->window != tree_view->priv->bin_window)
2717 /* Ignore a released button, if that button wasn't depressed */
2718 if (tree_view->priv->pressed_button != event->button)
2721 if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2725 /* decide if we edit */
2726 path = _pspp_sheet_view_find_path (tree_view, node);
2727 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2728 if (event->button != 1 || modifiers)
2731 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2732 pspp_sheet_view_column_cell_set_cell_data (column,
2733 tree_view->priv->model,
2736 if (!pspp_sheet_view_column_get_quick_edit (column)
2737 && _pspp_sheet_view_column_has_editable_cell (column))
2740 flags = 0; /* FIXME: get the right flags */
2741 path_string = gtk_tree_path_to_string (path);
2743 if (!_pspp_sheet_view_column_cell_event (column,
2751 if (cell_editable == NULL)
2754 pspp_sheet_view_real_set_cursor (tree_view, path,
2755 TRUE, TRUE, 0); /* XXX mode? */
2756 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2759 _pspp_sheet_view_column_get_neighbor_sizes (
2760 column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2763 area.width -= right + left;
2765 pspp_sheet_view_real_start_editing (tree_view,
2772 g_free (path_string);
2773 gtk_tree_path_free (path);
2778 pspp_sheet_view_button_release (GtkWidget *widget,
2779 GdkEventButton *event)
2781 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2783 pspp_sheet_view_stop_editing (tree_view, FALSE);
2784 if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2785 && pspp_sheet_view_button_release_edit (tree_view, event))
2787 if (tree_view->priv->pressed_button == event->button)
2788 tree_view->priv->pressed_button = -1;
2790 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2794 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2795 return pspp_sheet_view_button_release_drag_column (widget, event);
2797 if (tree_view->priv->rubber_band_status)
2798 pspp_sheet_view_stop_rubber_band (tree_view);
2800 if (tree_view->priv->pressed_button == event->button)
2801 tree_view->priv->pressed_button = -1;
2803 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2804 return pspp_sheet_view_button_release_column_resize (widget, event);
2810 pspp_sheet_view_grab_broken (GtkWidget *widget,
2811 GdkEventGrabBroken *event)
2813 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2815 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2816 pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2818 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2819 pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2824 /* GtkWidget::motion_event function set.
2828 do_prelight (PsppSheetView *tree_view,
2830 /* these are in bin_window coords */
2834 int prev_node = tree_view->priv->prelight_node;
2836 if (prev_node != node)
2838 tree_view->priv->prelight_node = node;
2841 _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2844 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2850 prelight_or_select (PsppSheetView *tree_view,
2852 /* these are in bin_window coords */
2856 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2858 if (tree_view->priv->hover_selection &&
2859 (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2860 !(tree_view->priv->edited_column &&
2861 tree_view->priv->edited_column->editable_widget))
2865 if (!pspp_sheet_view_node_is_selected (tree_view, node))
2869 path = _pspp_sheet_view_find_path (tree_view, node);
2870 pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2871 if (pspp_sheet_view_node_is_selected (tree_view, node))
2873 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2874 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE, 0); /* XXX mode? */
2876 gtk_tree_path_free (path);
2880 else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2881 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2884 do_prelight (tree_view, node, x, y);
2888 ensure_unprelighted (PsppSheetView *tree_view)
2890 do_prelight (tree_view,
2892 -1000, -1000); /* coords not possibly over an arrow */
2894 g_assert (tree_view->priv->prelight_node < 0);
2898 update_prelight (PsppSheetView *tree_view,
2905 if (tree_view->priv->row_count == 0)
2910 ensure_unprelighted (tree_view);
2914 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2918 pspp_sheet_view_find_offset (tree_view, new_y, &node);
2921 prelight_or_select (tree_view, node, x, y);
2927 /* Our motion arrow is either a box (in the case of the original spot)
2928 * or an arrow. It is expander_size wide.
2951 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2954 PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2955 GtkWidget *widget = GTK_WIDGET (tree_view);
2956 GdkBitmap *mask = NULL;
2961 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2962 GdkWindowAttr attributes;
2963 guint attributes_mask;
2966 reorder->left_column == tree_view->priv->drag_column ||
2967 reorder->right_column == tree_view->priv->drag_column)
2968 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2969 else if (reorder->left_column || reorder->right_column)
2971 GdkRectangle visible_rect;
2972 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2973 if (reorder->left_column)
2974 x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2976 x = reorder->right_column->allocation.x;
2978 if (x < visible_rect.x)
2979 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2980 else if (x > visible_rect.x + visible_rect.width)
2981 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2983 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2986 /* We want to draw the rectangle over the initial location. */
2987 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2992 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2994 if (tree_view->priv->drag_highlight_window)
2996 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2998 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3001 attributes.window_type = GDK_WINDOW_CHILD;
3002 attributes.wclass = GDK_INPUT_OUTPUT;
3003 attributes.x = tree_view->priv->drag_column_x;
3005 width = attributes.width = tree_view->priv->drag_column->allocation.width;
3006 height = attributes.height = tree_view->priv->drag_column->allocation.height;
3007 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3008 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3009 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3010 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3011 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
3012 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3014 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3015 gc = gdk_gc_new (mask);
3017 gdk_gc_set_foreground (gc, &col);
3018 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3020 gdk_gc_set_foreground(gc, &col);
3021 gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
3022 g_object_unref (gc);
3024 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3026 if (mask) g_object_unref (mask);
3027 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3030 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
3036 width = tree_view->priv->expander_size;
3038 /* Get x, y, width, height of arrow */
3039 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
3040 if (reorder->left_column)
3042 x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
3043 height = reorder->left_column->allocation.height;
3047 x += reorder->right_column->allocation.x - width/2;
3048 height = reorder->right_column->allocation.height;
3050 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3051 height += tree_view->priv->expander_size;
3053 /* Create the new window */
3054 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3056 if (tree_view->priv->drag_highlight_window)
3058 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3060 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3063 attributes.window_type = GDK_WINDOW_TEMP;
3064 attributes.wclass = GDK_INPUT_OUTPUT;
3065 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3066 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3067 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3068 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3071 attributes.width = width;
3072 attributes.height = height;
3073 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3074 &attributes, attributes_mask);
3075 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3077 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3078 gc = gdk_gc_new (mask);
3080 gdk_gc_set_foreground (gc, &col);
3081 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3083 /* Draw the 2 arrows as per above */
3085 gdk_gc_set_foreground (gc, &col);
3086 for (i = 0; i < width; i ++)
3088 if (i == (width/2 - 1))
3090 gdk_draw_line (mask, gc, i, j, i, height - j);
3091 if (i < (width/2 - 1))
3096 g_object_unref (gc);
3097 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3099 if (mask) g_object_unref (mask);
3102 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3103 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3105 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3106 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3112 width = tree_view->priv->expander_size;
3114 /* Get x, y, width, height of arrow */
3115 width = width/2; /* remember, the arrow only takes half the available width */
3116 gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
3117 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3118 x += widget->allocation.width - width;
3120 if (reorder->left_column)
3121 height = reorder->left_column->allocation.height;
3123 height = reorder->right_column->allocation.height;
3125 y -= tree_view->priv->expander_size;
3126 height += 2*tree_view->priv->expander_size;
3128 /* Create the new window */
3129 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3130 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3132 if (tree_view->priv->drag_highlight_window)
3134 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3136 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3139 attributes.window_type = GDK_WINDOW_TEMP;
3140 attributes.wclass = GDK_INPUT_OUTPUT;
3141 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3142 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3143 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3144 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3147 attributes.width = width;
3148 attributes.height = height;
3149 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3150 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3152 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3153 gc = gdk_gc_new (mask);
3155 gdk_gc_set_foreground (gc, &col);
3156 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3158 /* Draw the 2 arrows as per above */
3160 gdk_gc_set_foreground (gc, &col);
3161 j = tree_view->priv->expander_size;
3162 for (i = 0; i < width; i ++)
3165 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3169 gdk_draw_line (mask, gc, k, j, k, height - j);
3170 gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3171 gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3174 g_object_unref (gc);
3175 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3177 if (mask) g_object_unref (mask);
3180 tree_view->priv->drag_column_window_state = arrow_type;
3181 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3185 g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3186 gdk_window_hide (tree_view->priv->drag_highlight_window);
3190 gdk_window_show (tree_view->priv->drag_highlight_window);
3191 gdk_window_raise (tree_view->priv->drag_highlight_window);
3196 pspp_sheet_view_motion_resize_column (GtkWidget *widget,
3197 GdkEventMotion *event)
3201 PsppSheetViewColumn *column;
3202 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3204 column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3206 if (event->is_hint || event->window != gtk_widget_get_window (widget))
3207 gtk_widget_get_pointer (widget, &x, NULL);
3211 if (tree_view->priv->hadjustment)
3212 x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
3214 new_width = pspp_sheet_view_new_column_width (tree_view,
3215 tree_view->priv->drag_pos, &x);
3216 if (x != tree_view->priv->x_drag &&
3217 (new_width != column->fixed_width))
3219 column->use_resized_width = TRUE;
3220 column->resized_width = new_width;
3223 column->resized_width -= tree_view->priv->last_extra_space_per_column;
3225 gtk_widget_queue_resize (widget);
3233 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3235 PsppSheetViewColumnReorder *reorder = NULL;
3239 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3240 for (list = tree_view->priv->column_drag_info; list; list = list->next)
3242 reorder = (PsppSheetViewColumnReorder *) list->data;
3243 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3248 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3251 tree_view->priv->cur_reorder = reorder;
3252 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3256 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3258 GdkRectangle visible_rect;
3263 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3264 y += tree_view->priv->dy;
3266 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3268 /* see if we are near the edge. */
3269 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3272 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3277 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->vadjustment) + offset, 0.0,
3278 gtk_adjustment_get_upper (tree_view->priv->vadjustment) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
3279 gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3283 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3285 GdkRectangle visible_rect;
3290 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3292 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3294 /* See if we are near the edge. */
3295 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3298 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3304 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) + offset,
3305 0.0, gtk_adjustment_get_upper (tree_view->priv->hadjustment) - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
3306 gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3313 pspp_sheet_view_motion_drag_column (GtkWidget *widget,
3314 GdkEventMotion *event)
3316 PsppSheetView *tree_view = (PsppSheetView *) widget;
3317 PsppSheetViewColumn *column = tree_view->priv->drag_column;
3319 GtkAllocation allocation;
3322 if ((column == NULL) ||
3323 (event->window != tree_view->priv->drag_window))
3326 /* Handle moving the header */
3327 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3328 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
3329 x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3330 MAX (tree_view->priv->width, allocation.width) - column->allocation.width);
3331 gdk_window_move (tree_view->priv->drag_window, x, y);
3333 /* autoscroll, if needed */
3334 pspp_sheet_view_horizontal_autoscroll (tree_view);
3335 /* Update the current reorder position and arrow; */
3336 pspp_sheet_view_update_current_reorder (tree_view);
3342 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3344 remove_scroll_timeout (tree_view);
3345 gtk_grab_remove (GTK_WIDGET (tree_view));
3347 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3349 GtkTreePath *tmp_path;
3351 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3353 /* The anchor path should be set to the start path */
3354 tmp_path = _pspp_sheet_view_find_path (tree_view,
3355 tree_view->priv->rubber_band_start_node);
3357 if (tree_view->priv->anchor)
3358 gtk_tree_row_reference_free (tree_view->priv->anchor);
3360 tree_view->priv->anchor =
3361 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3362 tree_view->priv->model,
3365 gtk_tree_path_free (tmp_path);
3367 /* ... and the cursor to the end path */
3368 tmp_path = _pspp_sheet_view_find_path (tree_view,
3369 tree_view->priv->rubber_band_end_node);
3370 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE, 0); /* XXX mode? */
3371 gtk_tree_path_free (tmp_path);
3373 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3376 /* Clear status variables */
3377 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3378 tree_view->priv->rubber_band_shift = 0;
3379 tree_view->priv->rubber_band_ctrl = 0;
3381 tree_view->priv->rubber_band_start_node = -1;
3382 tree_view->priv->rubber_band_end_node = -1;
3386 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3390 gboolean skip_start,
3393 if (start_node == end_node)
3396 /* We skip the first node and jump inside the loop */
3402 /* Small optimization by assuming insensitive nodes are never
3407 if (tree_view->priv->rubber_band_shift)
3408 pspp_sheet_view_node_select (tree_view, start_node);
3409 else if (tree_view->priv->rubber_band_ctrl)
3411 /* Toggle the selection state */
3412 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3413 pspp_sheet_view_node_unselect (tree_view, start_node);
3415 pspp_sheet_view_node_select (tree_view, start_node);
3418 pspp_sheet_view_node_select (tree_view, start_node);
3422 /* Mirror the above */
3423 if (tree_view->priv->rubber_band_shift)
3424 pspp_sheet_view_node_unselect (tree_view, start_node);
3425 else if (tree_view->priv->rubber_band_ctrl)
3427 /* Toggle the selection state */
3428 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3429 pspp_sheet_view_node_unselect (tree_view, start_node);
3431 pspp_sheet_view_node_select (tree_view, start_node);
3434 pspp_sheet_view_node_unselect (tree_view, start_node);
3437 _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3439 if (start_node == end_node)
3444 start_node = pspp_sheet_view_node_next (tree_view, start_node);
3447 /* Ran out of tree */
3450 if (skip_end && start_node == end_node)
3457 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3460 return node * tree_view->priv->fixed_height;
3464 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3468 int fixed_height = tree_view->priv->fixed_height;
3469 if (fixed_height <= 0
3471 || height >= tree_view->priv->row_count * fixed_height)
3478 *new_node = height / fixed_height;
3479 return height % fixed_height;
3484 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3489 pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3490 pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3492 /* Handle the start area first */
3493 if (tree_view->priv->rubber_band_start_node < 0)
3495 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3502 else if (start_node < tree_view->priv->rubber_band_start_node)
3504 /* New node is above the old one; selection became bigger */
3505 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3507 tree_view->priv->rubber_band_start_node,
3512 else if (start_node > tree_view->priv->rubber_band_start_node)
3514 /* New node is below the old one; selection became smaller */
3515 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3516 tree_view->priv->rubber_band_start_node,
3523 tree_view->priv->rubber_band_start_node = start_node;
3525 /* Next, handle the end area */
3526 if (tree_view->priv->rubber_band_end_node < 0)
3528 /* In the event this happens, start_node was also -1; this case is
3532 else if (end_node < 0)
3534 /* Find the last node in the tree */
3535 pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3538 /* Selection reached end of the tree */
3539 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3540 tree_view->priv->rubber_band_end_node,
3546 else if (end_node > tree_view->priv->rubber_band_end_node)
3548 /* New node is below the old one; selection became bigger */
3549 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3550 tree_view->priv->rubber_band_end_node,
3556 else if (end_node < tree_view->priv->rubber_band_end_node)
3558 /* New node is above the old one; selection became smaller */
3559 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3561 tree_view->priv->rubber_band_end_node,
3567 tree_view->priv->rubber_band_end_node = end_node;
3570 #define GDK_RECTANGLE_PTR(X) ((GdkRectangle *)(X))
3573 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3576 cairo_rectangle_int_t old_area;
3577 cairo_rectangle_int_t new_area;
3578 cairo_rectangle_int_t common;
3579 cairo_region_t *invalid_region;
3580 PsppSheetViewColumn *column;
3582 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3583 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3584 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3585 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3587 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3590 y = MAX (y, 0) + tree_view->priv->dy;
3592 new_area.x = MIN (tree_view->priv->press_start_x, x);
3593 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3594 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3595 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3597 invalid_region = cairo_region_create_rectangle (&old_area);
3598 cairo_region_union_rectangle (invalid_region, &new_area);
3600 gdk_rectangle_intersect (GDK_RECTANGLE_PTR (&old_area),
3601 GDK_RECTANGLE_PTR (&new_area), GDK_RECTANGLE_PTR (&common));
3602 if (common.width > 2 && common.height > 2)
3604 cairo_region_t *common_region;
3606 /* make sure the border is invalidated */
3612 common_region = cairo_region_create_rectangle (&common);
3614 cairo_region_subtract (invalid_region, common_region);
3615 cairo_region_destroy (common_region);
3618 #if GTK_MAJOR_VERSION == 3
3619 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
3622 cairo_rectangle_int_t extents;
3624 cairo_region_get_extents (invalid_region, &extents);
3625 ereg = gdk_region_rectangle (GDK_RECTANGLE_PTR (&extents));
3626 gdk_window_invalidate_region (tree_view->priv->bin_window, ereg, TRUE);
3627 gdk_region_destroy (ereg);
3631 cairo_region_destroy (invalid_region);
3633 tree_view->priv->rubber_band_x = x;
3634 tree_view->priv->rubber_band_y = y;
3635 pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3637 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3638 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3639 tree_view->priv->anchor_column,
3642 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3644 pspp_sheet_view_update_rubber_band_selection (tree_view);
3649 pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view,
3654 GdkRectangle rubber_rect;
3658 rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3659 rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3660 rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3661 rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3663 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3666 cr = gdk_cairo_create (tree_view->priv->bin_window);
3667 cairo_set_line_width (cr, 1.0);
3669 style = gtk_widget_get_style (GTK_WIDGET (tree_view));
3670 cairo_set_source_rgba (cr,
3671 style->fg[GTK_STATE_NORMAL].red / 65535.,
3672 style->fg[GTK_STATE_NORMAL].green / 65535.,
3673 style->fg[GTK_STATE_NORMAL].blue / 65535.,
3676 gdk_cairo_rectangle (cr, &rect);
3680 cairo_set_source_rgb (cr,
3681 style->fg[GTK_STATE_NORMAL].red / 65535.,
3682 style->fg[GTK_STATE_NORMAL].green / 65535.,
3683 style->fg[GTK_STATE_NORMAL].blue / 65535.);
3685 cairo_rectangle (cr,
3686 rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3687 rubber_rect.width - 1, rubber_rect.height - 1);
3696 pspp_sheet_view_motion_bin_window (GtkWidget *widget,
3697 GdkEventMotion *event)
3699 PsppSheetView *tree_view;
3703 tree_view = (PsppSheetView *) widget;
3705 if (tree_view->priv->row_count == 0)
3708 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3710 GdkRectangle background_area, cell_area;
3711 PsppSheetViewColumn *column;
3713 if (find_click (tree_view, event->x, event->y, &node, &column,
3714 &background_area, &cell_area)
3715 && tree_view->priv->focus_column == column
3716 && tree_view->priv->press_start_node == node)
3719 gtk_grab_add (GTK_WIDGET (tree_view));
3720 pspp_sheet_view_update_rubber_band (tree_view);
3722 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3724 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3726 pspp_sheet_view_update_rubber_band (tree_view);
3728 add_scroll_timeout (tree_view);
3731 /* only check for an initiated drag when a button is pressed */
3732 if (tree_view->priv->pressed_button >= 0
3733 && !tree_view->priv->rubber_band_status)
3734 pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3736 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3740 pspp_sheet_view_find_offset (tree_view, new_y, &node);
3742 tree_view->priv->event_last_x = event->x;
3743 tree_view->priv->event_last_y = event->y;
3745 prelight_or_select (tree_view, node, event->x, event->y);
3751 pspp_sheet_view_motion (GtkWidget *widget,
3752 GdkEventMotion *event)
3754 PsppSheetView *tree_view;
3756 tree_view = (PsppSheetView *) widget;
3758 /* Resizing a column */
3759 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3760 return pspp_sheet_view_motion_resize_column (widget, event);
3763 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3764 return pspp_sheet_view_motion_drag_column (widget, event);
3766 /* Sanity check it */
3767 if (event->window == tree_view->priv->bin_window)
3768 return pspp_sheet_view_motion_bin_window (widget, event);
3773 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3774 * the tree is empty.
3777 invalidate_empty_focus (PsppSheetView *tree_view)
3781 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3786 area.width = gdk_window_get_width (tree_view->priv->bin_window);
3787 area.height = gdk_window_get_height (tree_view->priv->bin_window);
3788 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3791 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3795 draw_empty_focus (PsppSheetView *tree_view)
3797 GtkWidget *widget = GTK_WIDGET (tree_view);
3799 cairo_t *cr = gdk_cairo_create (tree_view->priv->bin_window);
3801 if (!gtk_widget_has_focus (widget))
3804 w = gdk_window_get_width (tree_view->priv->bin_window);
3805 h = gdk_window_get_height (tree_view->priv->bin_window);
3811 gtk_paint_focus (gtk_widget_get_style (widget),
3813 gtk_widget_get_state (widget),
3821 pspp_sheet_view_draw_vertical_grid_lines (PsppSheetView *tree_view,
3823 gint n_visible_columns,
3827 GList *list = tree_view->priv->columns;
3831 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3832 && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3835 /* Only draw the lines for visible rows and columns */
3836 for (list = tree_view->priv->columns; list; list = list->next, i++)
3838 PsppSheetViewColumn *column = list->data;
3841 if (! column->visible)
3844 current_x += column->width;
3846 /* Generally the grid lines should fit within the column, but for the
3847 last visible column we put it just past the end of the column.
3848 (Otherwise horizontal grid lines sometimes stick out by one pixel.) */
3850 if (i != n_visible_columns - 1)
3853 cairo_set_line_width (cr, 1.0);
3854 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
3855 cairo_move_to (cr, x + 0.5, min_y);
3856 cairo_line_to (cr, x + 0.5, max_y - min_y);
3861 /* Warning: Very scary function.
3862 * Modify at your own risk
3864 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3865 * FIXME: It's not...
3868 pspp_sheet_view_draw_bin (GtkWidget *widget,
3871 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3876 int drag_highlight = -1;
3879 gint y_offset, cell_offset;
3881 GdkRectangle background_area;
3882 GdkRectangle cell_area;
3884 gint bin_window_width;
3885 gint bin_window_height;
3886 GtkTreePath *cursor_path;
3887 GtkTreePath *drag_dest_path;
3888 GList *first_column, *last_column;
3889 gint vertical_separator;
3890 gint horizontal_separator;
3891 gint focus_line_width;
3892 gboolean allow_rules;
3894 gint n_visible_columns;
3895 gint grid_line_width;
3896 gboolean row_ending_details;
3897 gboolean draw_vgrid_lines, draw_hgrid_lines;
3899 GtkStyleContext *context;
3900 context = gtk_widget_get_style_context (widget);
3903 GtkAllocation allocation;
3904 gtk_widget_get_allocation (widget, &allocation);
3906 GdkRectangle exposed_rect;
3907 gdk_cairo_get_clip_rectangle (cr, &exposed_rect);
3911 Zarea.height = allocation.height;
3913 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3915 gtk_widget_style_get (widget,
3916 "horizontal-separator", &horizontal_separator,
3917 "vertical-separator", &vertical_separator,
3918 "allow-rules", &allow_rules,
3919 "focus-line-width", &focus_line_width,
3920 "row-ending-details", &row_ending_details,
3923 if (tree_view->priv->row_count == 0)
3925 draw_empty_focus (tree_view);
3930 /* clip event->area to the visible area */
3931 if (Zarea.height < 0.5)
3935 validate_visible_area (tree_view);
3937 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, Zarea.y);
3941 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3943 gdk_window_get_width (tree_view->priv->bin_window);
3946 gdk_window_get_height (tree_view->priv->bin_window);
3949 if (tree_view->priv->height < bin_window_height)
3951 gtk_paint_flat_box (gtk_widget_get_style (widget),
3953 gtk_widget_get_state (widget),
3957 0, tree_view->priv->height,
3959 bin_window_height - tree_view->priv->height);
3965 /* find the path for the node */
3966 path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3967 gtk_tree_model_get_iter (tree_view->priv->model,
3970 gtk_tree_path_free (path);
3973 drag_dest_path = NULL;
3975 if (tree_view->priv->cursor)
3976 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3979 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3981 if (tree_view->priv->drag_dest_row)
3982 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3985 _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3989 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3990 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3992 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
3993 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3995 if (draw_vgrid_lines || draw_hgrid_lines)
3996 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
3998 n_visible_columns = 0;
3999 for (list = tree_view->priv->columns; list; list = list->next)
4001 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4003 n_visible_columns ++;
4006 /* Find the last column */
4007 for (last_column = g_list_last (tree_view->priv->columns);
4008 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
4009 last_column = last_column->prev)
4013 for (first_column = g_list_first (tree_view->priv->columns);
4014 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
4015 first_column = first_column->next)
4018 /* Actually process the expose event. To do this, we want to
4019 * start at the first node of the event, and walk the tree in
4020 * order, drawing each successive node.
4027 gboolean is_first = FALSE;
4028 gboolean is_last = FALSE;
4029 gboolean done = FALSE;
4032 max_height = ROW_HEIGHT (tree_view);
4036 background_area.y = y_offset + Zarea.y;
4037 background_area.height = max_height;
4038 max_y = background_area.y + max_height;
4042 if (node == tree_view->priv->prelight_node)
4043 flags |= GTK_CELL_RENDERER_PRELIT;
4045 selected = pspp_sheet_view_node_is_selected (tree_view, node);
4049 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4051 list = (rtl ? list->prev : list->next))
4053 PsppSheetViewColumn *column = list->data;
4054 const gchar *detail = NULL;
4055 gboolean selected_column;
4058 if (!column->visible)
4061 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
4062 selected_column = column->selected && column->selectable;
4064 selected_column = TRUE;
4067 if (cell_offset > Zarea.x + Zarea.width ||
4068 cell_offset + column->width < Zarea.x)
4070 cell_offset += column->width;
4075 if (selected && selected_column)
4076 flags |= GTK_CELL_RENDERER_SELECTED;
4078 flags &= ~GTK_CELL_RENDERER_SELECTED;
4080 if (column->show_sort_indicator)
4081 flags |= GTK_CELL_RENDERER_SORTED;
4083 flags &= ~GTK_CELL_RENDERER_SORTED;
4086 flags |= GTK_CELL_RENDERER_FOCUSED;
4088 flags &= ~GTK_CELL_RENDERER_FOCUSED;
4090 background_area.x = cell_offset;
4091 background_area.width = column->width;
4093 cell_area = background_area;
4094 cell_area.y += vertical_separator / 2;
4095 cell_area.x += horizontal_separator / 2;
4096 cell_area.height -= vertical_separator;
4097 cell_area.width -= horizontal_separator;
4099 if (draw_vgrid_lines)
4101 if (list == first_column)
4103 cell_area.width -= grid_line_width / 2;
4105 else if (list == last_column)
4107 cell_area.x += grid_line_width / 2;
4108 cell_area.width -= grid_line_width / 2;
4112 cell_area.x += grid_line_width / 2;
4113 cell_area.width -= grid_line_width;
4117 if (draw_hgrid_lines)
4119 cell_area.y += grid_line_width / 2;
4120 cell_area.height -= grid_line_width;
4124 if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4126 if (!gdk_rectangle_intersect (&background_area, &exposed_rect, NULL))
4129 cell_offset += column->width;
4134 pspp_sheet_view_column_cell_set_cell_data (column,
4135 tree_view->priv->model,
4138 /* Select the detail for drawing the cell. relevant
4139 * factors are parity, sortedness, and whether to
4142 if (allow_rules && tree_view->priv->has_rules)
4144 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4145 n_visible_columns >= 3)
4148 detail = "cell_odd_ruled_sorted";
4150 detail = "cell_even_ruled_sorted";
4155 detail = "cell_odd_ruled";
4157 detail = "cell_even_ruled";
4162 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4163 n_visible_columns >= 3)
4166 detail = "cell_odd_sorted";
4168 detail = "cell_even_sorted";
4173 detail = "cell_odd";
4175 detail = "cell_even";
4181 gtk_style_context_save (context);
4182 state = gtk_cell_renderer_get_state (NULL, widget, flags);
4183 gtk_style_context_set_state (context, state);
4184 gtk_style_context_add_class (context, GTK_STYLE_CLASS_CELL);
4186 /* Draw background */
4187 gtk_render_background (context, cr,
4190 background_area.width,
4191 background_area.height);
4194 gtk_render_frame (context, cr,
4197 background_area.width,
4198 background_area.height);
4200 if (draw_hgrid_lines)
4202 cairo_set_line_width (cr, 1.0);
4203 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
4205 if (background_area.y >= 0)
4208 gdk_draw_line (event->window,
4209 tree_view->priv->grid_line_gc[widget->state],
4210 background_area.x, background_area.y,
4211 background_area.x + background_area.width,
4214 cairo_move_to (cr, background_area.x, background_area.y - 0.5);
4215 cairo_line_to (cr, background_area.x + background_area.width,
4216 background_area.y - 0.5);
4220 if (y_offset + max_height <= Zarea.height - 0.5)
4223 gdk_draw_line (event->window,
4224 tree_view->priv->grid_line_gc[widget->state],
4225 background_area.x, background_area.y + max_height,
4226 background_area.x + background_area.width,
4227 background_area.y + max_height);
4230 cairo_move_to (cr, background_area.x, background_area.y + max_height - 0.5);
4231 cairo_line_to (cr, background_area.x + background_area.width,
4232 background_area.y + max_height - 0.5);
4238 _pspp_sheet_view_column_cell_render (column,
4245 cell_offset += column->width;
4246 gtk_style_context_restore (context);
4249 if (node == drag_highlight)
4251 /* Draw indicator for the drop
4253 gint highlight_y = -1;
4257 switch (tree_view->priv->drag_dest_pos)
4259 case PSPP_SHEET_VIEW_DROP_BEFORE:
4260 highlight_y = background_area.y - 1;
4261 if (highlight_y < 0)
4265 case PSPP_SHEET_VIEW_DROP_AFTER:
4266 highlight_y = background_area.y + background_area.height - 1;
4269 case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE:
4270 case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER:
4271 _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node);
4275 width = gdk_window_get_width (tree_view->priv->bin_window);
4277 if (row_ending_details)
4278 gtk_paint_focus (gtk_widget_get_style (widget),
4280 gtk_widget_get_state (widget),
4283 ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4284 : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4285 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4286 - focus_line_width / 2,
4287 width, ROW_HEIGHT (tree_view)
4288 - focus_line_width + 1);
4290 gtk_paint_focus (gtk_widget_get_style (widget),
4292 gtk_widget_get_state (widget),
4294 "treeview-drop-indicator",
4295 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4296 - focus_line_width / 2,
4297 width, ROW_HEIGHT (tree_view)
4298 - focus_line_width + 1);
4303 if (highlight_y >= 0)
4305 gdk_draw_line (event->window,
4306 widget->style->fg_gc[gtk_widget_get_state (widget)],
4309 rtl ? 0 : bin_window_width,
4315 y_offset += max_height;
4319 node = pspp_sheet_view_node_next (tree_view, node);
4322 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
4326 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
4333 while (y_offset < Zarea.height);
4336 pspp_sheet_view_draw_vertical_grid_lines (tree_view, cr, n_visible_columns,
4340 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4342 GdkRectangle *rectangles;
4345 gdk_region_get_rectangles (event->region,
4349 while (n_rectangles--)
4350 pspp_sheet_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
4352 g_free (rectangles);
4357 gtk_tree_path_free (cursor_path);
4360 gtk_tree_path_free (drag_dest_path);
4367 pspp_sheet_view_draw (GtkWidget *widget,
4370 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4371 GtkStyleContext *context;
4373 context = gtk_widget_get_style_context (widget);
4375 if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window))
4380 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->bin_window);
4381 pspp_sheet_view_draw_bin (widget, cr);
4384 /* We can't just chain up to Container::expose as it will try to send the
4385 * event to the headers, so we handle propagating it to our children
4386 * (eg. widgets being edited) ourselves.
4388 tmp_list = tree_view->priv->children;
4391 PsppSheetViewChild *child = tmp_list->data;
4392 tmp_list = tmp_list->next;
4394 gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
4399 gtk_render_background (context, cr,
4401 gtk_widget_get_allocated_width (widget),
4402 gtk_widget_get_allocated_height (widget));
4405 gtk_style_context_save (context);
4406 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_VIEW);
4408 if (gtk_cairo_should_draw_window (cr, tree_view->priv->header_window))
4410 gint n_visible_columns;
4413 for (list = tree_view->priv->columns; list != NULL; list = list->next)
4415 PsppSheetViewColumn *column = list->data;
4417 if (column == tree_view->priv->drag_column || !column->visible)
4420 if (span_intersects (column->allocation.x, column->allocation.width,
4421 (int) gtk_adjustment_get_value (tree_view->priv->hadjustment),
4422 (int) gtk_widget_get_allocated_width (widget))
4423 && column->button != NULL)
4424 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4425 column->button, cr);
4428 n_visible_columns = 0;
4429 for (list = tree_view->priv->columns; list; list = list->next)
4431 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4433 n_visible_columns ++;
4436 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->header_window);
4437 pspp_sheet_view_draw_vertical_grid_lines (tree_view,
4441 TREE_VIEW_HEADER_HEIGHT (tree_view));
4444 if (tree_view->priv->drag_window &&
4445 gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window))
4447 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4448 tree_view->priv->drag_column->button,
4452 gtk_style_context_restore (context);
4464 /* returns 0x1 when no column has been found -- yes it's hackish */
4465 static PsppSheetViewColumn *
4466 pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
4467 PsppSheetViewColumn *column,
4470 PsppSheetViewColumn *left_column = NULL;
4471 PsppSheetViewColumn *cur_column = NULL;
4474 if (!column->reorderable)
4475 return (PsppSheetViewColumn *)0x1;
4477 switch (drop_position)
4480 /* find first column where we can drop */
4481 tmp_list = tree_view->priv->columns;
4482 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4483 return (PsppSheetViewColumn *)0x1;
4487 g_assert (tmp_list);
4489 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4490 tmp_list = tmp_list->next;
4492 if (left_column && left_column->visible == FALSE)
4495 if (!tree_view->priv->column_drop_func)
4498 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4500 left_column = cur_column;
4507 if (!tree_view->priv->column_drop_func)
4510 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4513 return (PsppSheetViewColumn *)0x1;
4517 /* find first column after column where we can drop */
4518 tmp_list = tree_view->priv->columns;
4520 for (; tmp_list; tmp_list = tmp_list->next)
4521 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4524 if (!tmp_list || !tmp_list->next)
4525 return (PsppSheetViewColumn *)0x1;
4527 tmp_list = tmp_list->next;
4528 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4529 tmp_list = tmp_list->next;
4533 g_assert (tmp_list);
4535 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4536 tmp_list = tmp_list->next;
4538 if (left_column && left_column->visible == FALSE)
4540 left_column = cur_column;
4542 tmp_list = tmp_list->next;
4546 if (!tree_view->priv->column_drop_func)
4549 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4551 left_column = cur_column;
4558 if (!tree_view->priv->column_drop_func)
4561 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4564 return (PsppSheetViewColumn *)0x1;
4568 /* find first column before column where we can drop */
4569 tmp_list = tree_view->priv->columns;
4571 for (; tmp_list; tmp_list = tmp_list->next)
4572 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4575 if (!tmp_list || !tmp_list->prev)
4576 return (PsppSheetViewColumn *)0x1;
4578 tmp_list = tmp_list->prev;
4579 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4580 tmp_list = tmp_list->prev;
4584 g_assert (tmp_list);
4586 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4588 if (left_column && !left_column->visible)
4590 /*if (!tmp_list->prev)
4591 return (PsppSheetViewColumn *)0x1;
4594 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4595 tmp_list = tmp_list->prev->prev;
4598 cur_column = left_column;
4600 tmp_list = tmp_list->prev;
4604 if (!tree_view->priv->column_drop_func)
4607 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4610 cur_column = left_column;
4611 tmp_list = tmp_list->prev;
4614 if (!tree_view->priv->column_drop_func)
4617 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4620 return (PsppSheetViewColumn *)0x1;
4624 /* same as DROP_HOME case, but doing it backwards */
4625 tmp_list = g_list_last (tree_view->priv->columns);
4628 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4629 return (PsppSheetViewColumn *)0x1;
4633 g_assert (tmp_list);
4635 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4637 if (left_column && !left_column->visible)
4639 cur_column = left_column;
4640 tmp_list = tmp_list->prev;
4643 if (!tree_view->priv->column_drop_func)
4646 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4649 cur_column = left_column;
4650 tmp_list = tmp_list->prev;
4653 if (!tree_view->priv->column_drop_func)
4656 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4659 return (PsppSheetViewColumn *)0x1;
4663 return (PsppSheetViewColumn *)0x1;
4667 pspp_sheet_view_key_press (GtkWidget *widget,
4670 PsppSheetView *tree_view = (PsppSheetView *) widget;
4672 if (tree_view->priv->rubber_band_status)
4674 if (event->keyval == GDK_Escape)
4675 pspp_sheet_view_stop_rubber_band (tree_view);
4680 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4682 if (event->keyval == GDK_Escape)
4684 tree_view->priv->cur_reorder = NULL;
4685 pspp_sheet_view_button_release_drag_column (widget, NULL);
4690 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4692 GList *focus_column;
4695 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4697 for (focus_column = tree_view->priv->columns;
4699 focus_column = focus_column->next)
4701 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4703 if (column->button && gtk_widget_has_focus (column->button))
4708 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4709 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4710 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4712 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4714 if (!column->resizable)
4716 gtk_widget_error_bell (widget);
4720 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4721 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4723 gint old_width = column->resized_width;
4725 column->resized_width = MAX (column->resized_width,
4727 column->resized_width -= 2;
4728 if (column->resized_width < 0)
4729 column->resized_width = 0;
4731 if (column->min_width == -1)
4732 column->resized_width = MAX (column->button_request,
4733 column->resized_width);
4735 column->resized_width = MAX (column->min_width,
4736 column->resized_width);
4738 if (column->max_width != -1)
4739 column->resized_width = MIN (column->resized_width,
4742 column->use_resized_width = TRUE;
4744 if (column->resized_width != old_width)
4745 gtk_widget_queue_resize (widget);
4747 gtk_widget_error_bell (widget);
4749 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4750 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4752 gint old_width = column->resized_width;
4754 column->resized_width = MAX (column->resized_width,
4756 column->resized_width += 2;
4758 if (column->max_width != -1)
4759 column->resized_width = MIN (column->resized_width,
4762 column->use_resized_width = TRUE;
4764 if (column->resized_width != old_width)
4765 gtk_widget_queue_resize (widget);
4767 gtk_widget_error_bell (widget);
4774 (event->state & GDK_MOD1_MASK) &&
4775 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4776 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4777 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4778 || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4780 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4782 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4783 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4785 PsppSheetViewColumn *col;
4786 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
4787 if (col != (PsppSheetViewColumn *)0x1)
4788 pspp_sheet_view_move_column_after (tree_view, column, col);
4790 gtk_widget_error_bell (widget);
4792 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4793 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4795 PsppSheetViewColumn *col;
4796 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4797 if (col != (PsppSheetViewColumn *)0x1)
4798 pspp_sheet_view_move_column_after (tree_view, column, col);
4800 gtk_widget_error_bell (widget);
4802 else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4804 PsppSheetViewColumn *col;
4805 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4806 if (col != (PsppSheetViewColumn *)0x1)
4807 pspp_sheet_view_move_column_after (tree_view, column, col);
4809 gtk_widget_error_bell (widget);
4811 else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4813 PsppSheetViewColumn *col;
4814 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4815 if (col != (PsppSheetViewColumn *)0x1)
4816 pspp_sheet_view_move_column_after (tree_view, column, col);
4818 gtk_widget_error_bell (widget);
4825 /* Chain up to the parent class. It handles the keybindings. */
4826 if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4829 if (tree_view->priv->search_entry_avoid_unhandled_binding)
4831 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4835 /* We pass the event to the search_entry. If its text changes, then we start
4836 * the typeahead find capabilities. */
4837 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4838 && tree_view->priv->enable_search
4839 && !tree_view->priv->search_custom_entry_set)
4841 GdkEvent *new_event;
4843 const char *new_text;
4846 gboolean text_modified;
4847 gulong popup_menu_id;
4849 pspp_sheet_view_ensure_interactive_directory (tree_view);
4851 /* Make a copy of the current text */
4852 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4853 new_event = gdk_event_copy ((GdkEvent *) event);
4854 g_object_unref (((GdkEventKey *) new_event)->window);
4855 ((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window));
4856 gtk_widget_realize (tree_view->priv->search_window);
4858 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
4859 "popup-menu", G_CALLBACK (gtk_true),
4862 /* Move the entry off screen */
4863 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4864 gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4865 gdk_screen_get_width (screen) + 1,
4866 gdk_screen_get_height (screen) + 1);
4867 gtk_widget_show (tree_view->priv->search_window);
4869 /* Send the event to the window. If the preedit_changed signal is emitted
4870 * during this event, we will set priv->imcontext_changed */
4871 tree_view->priv->imcontext_changed = FALSE;
4872 retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4873 gdk_event_free (new_event);
4874 gtk_widget_hide (tree_view->priv->search_window);
4876 g_signal_handler_disconnect (tree_view->priv->search_entry,
4879 /* We check to make sure that the entry tried to handle the text, and that
4880 * the text has changed.
4882 new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
4883 text_modified = strcmp (old_text, new_text) != 0;
4885 if (tree_view->priv->imcontext_changed || /* we're in a preedit */
4886 (retval && text_modified)) /* ...or the text was modified */
4888 if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
4890 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
4895 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
4905 pspp_sheet_view_key_release (GtkWidget *widget,
4908 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4910 if (tree_view->priv->rubber_band_status)
4913 return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
4916 /* FIXME Is this function necessary? Can I get an enter_notify event
4917 * w/o either an expose event or a mouse motion event?
4920 pspp_sheet_view_enter_notify (GtkWidget *widget,
4921 GdkEventCrossing *event)
4923 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4927 /* Sanity check it */
4928 if (event->window != tree_view->priv->bin_window)
4931 if (tree_view->priv->row_count == 0)
4934 if (event->mode == GDK_CROSSING_GRAB ||
4935 event->mode == GDK_CROSSING_GTK_GRAB ||
4936 event->mode == GDK_CROSSING_GTK_UNGRAB ||
4937 event->mode == GDK_CROSSING_STATE_CHANGED)
4940 /* find the node internally */
4941 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
4944 pspp_sheet_view_find_offset (tree_view, new_y, &node);
4946 tree_view->priv->event_last_x = event->x;
4947 tree_view->priv->event_last_y = event->y;
4949 prelight_or_select (tree_view, node, event->x, event->y);
4955 pspp_sheet_view_leave_notify (GtkWidget *widget,
4956 GdkEventCrossing *event)
4958 PsppSheetView *tree_view;
4960 if (event->mode == GDK_CROSSING_GRAB)
4963 tree_view = PSPP_SHEET_VIEW (widget);
4965 if (tree_view->priv->prelight_node >= 0)
4966 _pspp_sheet_view_queue_draw_node (tree_view,
4967 tree_view->priv->prelight_node,
4970 tree_view->priv->event_last_x = -10000;
4971 tree_view->priv->event_last_y = -10000;
4973 prelight_or_select (tree_view,
4975 -1000, -1000); /* coords not possibly over an arrow */
4982 pspp_sheet_view_focus_out (GtkWidget *widget,
4983 GdkEventFocus *event)
4985 PsppSheetView *tree_view;
4987 tree_view = PSPP_SHEET_VIEW (widget);
4989 gtk_widget_queue_draw (widget);
4991 /* destroy interactive search dialog */
4992 if (tree_view->priv->search_window)
4993 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
4999 /* Incremental Reflow
5003 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
5006 GtkAllocation allocation;
5007 gint y = pspp_sheet_view_node_find_offset (tree_view, node)
5008 - gtk_adjustment_get_value (tree_view->priv->vadjustment)
5009 + TREE_VIEW_HEADER_HEIGHT (tree_view);
5011 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5013 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
5016 tree_view->priv->fixed_height);
5020 node_is_visible (PsppSheetView *tree_view,
5026 y = pspp_sheet_view_node_find_offset (tree_view, node);
5027 height = ROW_HEIGHT (tree_view);
5029 if (y >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5030 y + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5031 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5037 /* Returns the row height. */
5039 validate_row (PsppSheetView *tree_view,
5044 PsppSheetViewColumn *column;
5045 GList *list, *first_column, *last_column;
5047 gint horizontal_separator;
5048 gint vertical_separator;
5049 gint focus_line_width;
5050 gboolean draw_vgrid_lines, draw_hgrid_lines;
5052 gint grid_line_width;
5053 gboolean wide_separators;
5054 gint separator_height;
5056 gtk_widget_style_get (GTK_WIDGET (tree_view),
5057 "focus-padding", &focus_pad,
5058 "focus-line-width", &focus_line_width,
5059 "horizontal-separator", &horizontal_separator,
5060 "vertical-separator", &vertical_separator,
5061 "grid-line-width", &grid_line_width,
5062 "wide-separators", &wide_separators,
5063 "separator-height", &separator_height,
5067 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5068 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5070 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5071 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5073 for (last_column = g_list_last (tree_view->priv->columns);
5074 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5075 last_column = last_column->prev)
5078 for (first_column = g_list_first (tree_view->priv->columns);
5079 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5080 first_column = first_column->next)
5083 for (list = tree_view->priv->columns; list; list = list->next)
5088 column = list->data;
5090 if (! column->visible)
5093 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5094 pspp_sheet_view_column_cell_get_size (column,
5096 &tmp_width, &tmp_height);
5098 tmp_height += vertical_separator;
5099 height = MAX (height, tmp_height);
5101 tmp_width = tmp_width + horizontal_separator;
5103 if (draw_vgrid_lines)
5105 if (list->data == first_column || list->data == last_column)
5106 tmp_width += grid_line_width / 2.0;
5108 tmp_width += grid_line_width;
5111 if (tmp_width > column->requested_width)
5112 column->requested_width = tmp_width;
5115 if (draw_hgrid_lines)
5116 height += grid_line_width;
5118 tree_view->priv->post_validation_flag = TRUE;
5124 validate_visible_area (PsppSheetView *tree_view)
5126 GtkTreePath *path = NULL;
5127 GtkTreePath *above_path = NULL;
5131 gint area_above = 0;
5132 gint area_below = 0;
5133 GtkAllocation allocation;
5135 if (tree_view->priv->row_count == 0)
5138 if (tree_view->priv->scroll_to_path == NULL)
5141 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5143 total_height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5145 if (total_height == 0)
5148 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5151 /* we are going to scroll, and will update dy */
5152 _pspp_sheet_view_find_node (tree_view, path, &node);
5153 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5155 if (tree_view->priv->scroll_to_use_align)
5157 gint height = ROW_HEIGHT (tree_view);
5158 area_above = (total_height - height) *
5159 tree_view->priv->scroll_to_row_align;
5160 area_below = total_height - area_above - height;
5161 area_above = MAX (area_above, 0);
5162 area_below = MAX (area_below, 0);
5167 * 1) row not visible
5171 gint height = ROW_HEIGHT (tree_view);
5173 dy = pspp_sheet_view_node_find_offset (tree_view, node);
5175 if (dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5176 dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5177 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5179 /* row visible: keep the row at the same position */
5180 area_above = dy - gtk_adjustment_get_value (tree_view->priv->vadjustment);
5181 area_below = (gtk_adjustment_get_value (tree_view->priv->vadjustment) +
5182 gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5187 /* row not visible */
5189 && dy + height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5191 /* row at the beginning -- fixed */
5193 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment)
5194 - area_above - height;
5196 else if (dy >= (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5197 gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5199 /* row at the end -- fixed */
5200 area_above = dy - (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5201 gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
5202 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) -
5203 area_above - height;
5207 area_above = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - height;
5213 /* row somewhere in the middle, bring it to the top
5217 area_below = total_height - height;
5223 /* the scroll to isn't valid; ignore it.
5226 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5227 tree_view->priv->scroll_to_path = NULL;
5231 above_path = gtk_tree_path_copy (path);
5233 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5234 * backwards is much slower then forward, as there is no iter_prev function.
5235 * We go forwards first in case we run out of tree. Then we go backwards to
5238 while (node >= 0 && area_below > 0)
5240 gboolean done = FALSE;
5243 node = pspp_sheet_view_node_next (tree_view, node);
5246 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5248 gtk_tree_path_next (path);
5251 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5261 area_below -= ROW_HEIGHT (tree_view);
5263 gtk_tree_path_free (path);
5265 /* If we ran out of tree, and have extra area_below left, we need to add it
5268 area_above += area_below;
5270 _pspp_sheet_view_find_node (tree_view, above_path, &node);
5272 /* We walk backwards */
5273 while (area_above > 0)
5275 node = pspp_sheet_view_node_prev (tree_view, node);
5277 /* Always find the new path in the tree. We cannot just assume
5278 * a gtk_tree_path_prev() is enough here, as there might be children
5279 * in between this node and the previous sibling node. If this
5280 * appears to be a performance hotspot in profiles, we can look into
5281 * intrigate logic for keeping path, node and iter in sync like
5282 * we do for forward walks. (Which will be hard because of the lacking
5289 gtk_tree_path_free (above_path);
5290 above_path = _pspp_sheet_view_find_path (tree_view, node);
5292 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5294 area_above -= ROW_HEIGHT (tree_view);
5297 /* set the dy here to scroll to the path,
5298 * and sync the top row accordingly
5300 pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5301 pspp_sheet_view_top_row_to_dy (tree_view);
5303 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5304 tree_view->priv->scroll_to_path = NULL;
5307 gtk_tree_path_free (above_path);
5309 if (tree_view->priv->scroll_to_column)
5311 tree_view->priv->scroll_to_column = NULL;
5313 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5317 initialize_fixed_height_mode (PsppSheetView *tree_view)
5319 if (!tree_view->priv->row_count)
5322 if (tree_view->priv->fixed_height_set)
5325 if (tree_view->priv->fixed_height < 0)
5332 path = _pspp_sheet_view_find_path (tree_view, node);
5333 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5335 tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5337 gtk_tree_path_free (path);
5339 g_object_notify (G_OBJECT (tree_view), "fixed-height");
5343 /* Our strategy for finding nodes to validate is a little convoluted. We find
5344 * the left-most uninvalidated node. We then try walking right, validating
5345 * nodes. Once we find a valid node, we repeat the previous process of finding
5346 * the first invalid node.
5350 validate_rows_handler (PsppSheetView *tree_view)
5352 initialize_fixed_height_mode (tree_view);
5353 if (tree_view->priv->validate_rows_timer)
5355 g_source_remove (tree_view->priv->validate_rows_timer);
5356 tree_view->priv->validate_rows_timer = 0;
5363 do_presize_handler (PsppSheetView *tree_view)
5365 GtkRequisition requisition;
5367 validate_visible_area (tree_view);
5368 tree_view->priv->presize_handler_timer = 0;
5370 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5373 gtk_widget_get_preferred_size (GTK_WIDGET (tree_view), NULL, &requisition);
5375 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5376 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5377 gtk_adjustment_changed (tree_view->priv->hadjustment);
5378 gtk_adjustment_changed (tree_view->priv->vadjustment);
5379 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5385 presize_handler_callback (gpointer data)
5387 do_presize_handler (PSPP_SHEET_VIEW (data));
5393 install_presize_handler (PsppSheetView *tree_view)
5395 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5398 if (! tree_view->priv->presize_handler_timer)
5400 tree_view->priv->presize_handler_timer =
5401 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5403 if (! tree_view->priv->validate_rows_timer)
5405 tree_view->priv->validate_rows_timer =
5406 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5411 scroll_sync_handler (PsppSheetView *tree_view)
5413 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5414 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5415 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5416 pspp_sheet_view_top_row_to_dy (tree_view);
5418 pspp_sheet_view_dy_to_top_row (tree_view);
5420 tree_view->priv->scroll_sync_timer = 0;
5426 install_scroll_sync_handler (PsppSheetView *tree_view)
5428 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5431 if (!tree_view->priv->scroll_sync_timer)
5433 tree_view->priv->scroll_sync_timer =
5434 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5439 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5443 gtk_tree_row_reference_free (tree_view->priv->top_row);
5447 tree_view->priv->top_row = NULL;
5448 tree_view->priv->top_row_dy = 0;
5452 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5453 tree_view->priv->top_row_dy = offset;
5457 /* Always call this iff dy is in the visible range. If the tree is empty, then
5458 * it's set to be NULL, and top_row_dy is 0;
5461 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5467 if (tree_view->priv->row_count == 0)
5469 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5473 offset = pspp_sheet_view_find_offset (tree_view,
5474 tree_view->priv->dy,
5479 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5483 path = _pspp_sheet_view_find_path (tree_view, node);
5484 pspp_sheet_view_set_top_row (tree_view, path, offset);
5485 gtk_tree_path_free (path);
5491 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5497 /* Avoid recursive calls */
5498 if (tree_view->priv->in_top_row_to_dy)
5501 if (tree_view->priv->top_row)
5502 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5509 _pspp_sheet_view_find_node (tree_view, path, &node);
5512 gtk_tree_path_free (path);
5516 /* keep dy and set new toprow */
5517 gtk_tree_row_reference_free (tree_view->priv->top_row);
5518 tree_view->priv->top_row = NULL;
5519 tree_view->priv->top_row_dy = 0;
5520 /* DO NOT install the idle handler */
5521 pspp_sheet_view_dy_to_top_row (tree_view);
5525 if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5527 /* new top row -- do NOT install the idle handler */
5528 pspp_sheet_view_dy_to_top_row (tree_view);
5532 new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5533 new_dy += tree_view->priv->top_row_dy;
5535 if (new_dy + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
5536 new_dy = tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
5538 new_dy = MAX (0, new_dy);
5540 tree_view->priv->in_top_row_to_dy = TRUE;
5541 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5542 tree_view->priv->in_top_row_to_dy = FALSE;
5547 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5549 install_presize_handler (tree_view);
5555 set_source_row (GdkDragContext *context,
5556 GtkTreeModel *model,
5557 GtkTreePath *source_row)
5559 g_object_set_data_full (G_OBJECT (context),
5560 "gtk-tree-view-source-row",
5561 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5562 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5566 get_source_row (GdkDragContext *context)
5568 GtkTreeRowReference *ref =
5569 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5572 return gtk_tree_row_reference_get_path (ref);
5579 GtkTreeRowReference *dest_row;
5580 guint path_down_mode : 1;
5581 guint empty_view_drop : 1;
5582 guint drop_append_mode : 1;
5587 dest_row_free (gpointer data)
5589 DestRow *dr = (DestRow *)data;
5591 gtk_tree_row_reference_free (dr->dest_row);
5592 g_slice_free (DestRow, dr);
5596 set_dest_row (GdkDragContext *context,
5597 GtkTreeModel *model,
5598 GtkTreePath *dest_row,
5599 gboolean path_down_mode,
5600 gboolean empty_view_drop,
5601 gboolean drop_append_mode)
5607 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5612 dr = g_slice_new (DestRow);
5614 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5615 dr->path_down_mode = path_down_mode != FALSE;
5616 dr->empty_view_drop = empty_view_drop != FALSE;
5617 dr->drop_append_mode = drop_append_mode != FALSE;
5619 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5620 dr, (GDestroyNotify) dest_row_free);
5624 get_dest_row (GdkDragContext *context,
5625 gboolean *path_down_mode)
5628 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5632 GtkTreePath *path = NULL;
5635 *path_down_mode = dr->path_down_mode;
5638 path = gtk_tree_row_reference_get_path (dr->dest_row);
5639 else if (dr->empty_view_drop)
5640 path = gtk_tree_path_new_from_indices (0, -1);
5644 if (path && dr->drop_append_mode)
5645 gtk_tree_path_next (path);
5653 /* Get/set whether drag_motion requested the drag data and
5654 * drag_data_received should thus not actually insert the data,
5655 * since the data doesn't result from a drop.
5658 set_status_pending (GdkDragContext *context,
5659 GdkDragAction suggested_action)
5661 g_object_set_data (G_OBJECT (context),
5662 "gtk-tree-view-status-pending",
5663 GINT_TO_POINTER (suggested_action));
5666 static GdkDragAction
5667 get_status_pending (GdkDragContext *context)
5669 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5670 "gtk-tree-view-status-pending"));
5673 static TreeViewDragInfo*
5674 get_info (PsppSheetView *tree_view)
5676 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5680 destroy_info (TreeViewDragInfo *di)
5682 g_slice_free (TreeViewDragInfo, di);
5685 static TreeViewDragInfo*
5686 ensure_info (PsppSheetView *tree_view)
5688 TreeViewDragInfo *di;
5690 di = get_info (tree_view);
5694 di = g_slice_new0 (TreeViewDragInfo);
5696 g_object_set_data_full (G_OBJECT (tree_view),
5697 "gtk-tree-view-drag-info",
5699 (GDestroyNotify) destroy_info);
5706 remove_info (PsppSheetView *tree_view)
5708 g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5713 drag_scan_timeout (gpointer data)
5715 PsppSheetView *tree_view;
5717 GdkModifierType state;
5718 GtkTreePath *path = NULL;
5719 PsppSheetViewColumn *column = NULL;
5720 GdkRectangle visible_rect;
5722 GDK_THREADS_ENTER ();
5724 tree_view = PSPP_SHEET_VIEW (data);
5726 gdk_window_get_pointer (tree_view->priv->bin_window,
5729 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5731 /* See if we are near the edge. */
5732 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5733 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5734 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5735 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5737 pspp_sheet_view_get_path_at_pos (tree_view,
5738 tree_view->priv->bin_window,
5747 pspp_sheet_view_scroll_to_cell (tree_view,
5753 gtk_tree_path_free (path);
5757 GDK_THREADS_LEAVE ();
5764 add_scroll_timeout (PsppSheetView *tree_view)
5766 if (tree_view->priv->scroll_timeout == 0)
5768 tree_view->priv->scroll_timeout =
5769 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5774 remove_scroll_timeout (PsppSheetView *tree_view)
5776 if (tree_view->priv->scroll_timeout != 0)
5778 g_source_remove (tree_view->priv->scroll_timeout);
5779 tree_view->priv->scroll_timeout = 0;
5784 check_model_dnd (GtkTreeModel *model,
5785 GType required_iface,
5786 const gchar *signal)
5788 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5790 g_warning ("You must override the default '%s' handler "
5791 "on PsppSheetView when using models that don't support "
5792 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5793 "is to connect to '%s' and call "
5794 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5795 "the default handler from running. Look at the source code "
5796 "for the default handler in gtktreeview.c to get an idea what "
5797 "your handler should do. (gtktreeview.c is in the GTK source "
5798 "code.) If you're using GTK from a language other than C, "
5799 "there may be a more natural way to override default handlers, e.g. via derivation.",
5800 signal, g_type_name (required_iface), signal);
5808 scroll_row_timeout (gpointer data)
5810 PsppSheetView *tree_view = data;
5812 pspp_sheet_view_horizontal_autoscroll (tree_view);
5813 pspp_sheet_view_vertical_autoscroll (tree_view);
5815 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5816 pspp_sheet_view_update_rubber_band (tree_view);
5821 /* Returns TRUE if event should not be propagated to parent widgets */
5823 set_destination_row (PsppSheetView *tree_view,
5824 GdkDragContext *context,
5825 /* coordinates relative to the widget */
5828 GdkDragAction *suggested_action,
5831 GtkTreePath *path = NULL;
5832 PsppSheetViewDropPosition pos;
5833 PsppSheetViewDropPosition old_pos;
5834 TreeViewDragInfo *di;
5836 GtkTreePath *old_dest_path = NULL;
5837 gboolean can_drop = FALSE;
5839 *suggested_action = 0;
5842 widget = GTK_WIDGET (tree_view);
5844 di = get_info (tree_view);
5846 if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5848 /* someone unset us as a drag dest, note that if
5849 * we return FALSE drag_leave isn't called
5852 pspp_sheet_view_set_drag_dest_row (tree_view,
5854 PSPP_SHEET_VIEW_DROP_BEFORE);
5856 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5858 return FALSE; /* no longer a drop site */
5861 *target = gtk_drag_dest_find_target (widget, context,
5862 gtk_drag_dest_get_target_list (widget));
5863 if (*target == GDK_NONE)
5868 if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
5874 GtkTreeModel *model;
5876 /* the row got dropped on empty space, let's setup a special case
5880 gtk_tree_path_free (path);
5882 model = pspp_sheet_view_get_model (tree_view);
5884 n_children = gtk_tree_model_iter_n_children (model, NULL);
5887 pos = PSPP_SHEET_VIEW_DROP_AFTER;
5888 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
5892 pos = PSPP_SHEET_VIEW_DROP_BEFORE;
5893 path = gtk_tree_path_new_from_indices (0, -1);
5903 /* If we left the current row's "open" zone, unset the timeout for
5906 pspp_sheet_view_get_drag_dest_row (tree_view,
5911 gtk_tree_path_free (old_dest_path);
5913 if (TRUE /* FIXME if the location droppable predicate */)
5921 GtkWidget *source_widget;
5923 *suggested_action = gdk_drag_context_get_suggested_action (context);
5924 source_widget = gtk_drag_get_source_widget (context);
5926 if (source_widget == widget)
5928 /* Default to MOVE, unless the user has
5929 * pressed ctrl or shift to affect available actions
5931 if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
5932 *suggested_action = GDK_ACTION_MOVE;
5935 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5940 /* can't drop here */
5941 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5943 PSPP_SHEET_VIEW_DROP_BEFORE);
5947 gtk_tree_path_free (path);
5953 get_logical_dest_row (PsppSheetView *tree_view,
5954 gboolean *path_down_mode,
5955 gboolean *drop_append_mode)
5957 /* adjust path to point to the row the drop goes in front of */
5958 GtkTreePath *path = NULL;
5959 PsppSheetViewDropPosition pos;
5961 g_return_val_if_fail (path_down_mode != NULL, NULL);
5962 g_return_val_if_fail (drop_append_mode != NULL, NULL);
5964 *path_down_mode = FALSE;
5965 *drop_append_mode = 0;
5967 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
5972 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
5974 else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
5975 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
5976 *path_down_mode = TRUE;
5980 GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
5982 g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
5984 if (!gtk_tree_model_get_iter (model, &iter, path) ||
5985 !gtk_tree_model_iter_next (model, &iter))
5986 *drop_append_mode = 1;
5989 *drop_append_mode = 0;
5990 gtk_tree_path_next (path);
5998 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
5999 GdkEventMotion *event)
6001 GtkWidget *widget = GTK_WIDGET (tree_view);
6002 GdkDragContext *context;
6003 TreeViewDragInfo *di;
6004 GtkTreePath *path = NULL;
6006 gint cell_x, cell_y;
6007 GtkTreeModel *model;
6008 gboolean retval = FALSE;
6010 di = get_info (tree_view);
6012 if (di == NULL || !di->source_set)
6015 if (tree_view->priv->pressed_button < 0)
6018 if (!gtk_drag_check_threshold (widget,
6019 tree_view->priv->press_start_x,
6020 tree_view->priv->press_start_y,
6021 event->x, event->y))
6024 model = pspp_sheet_view_get_model (tree_view);
6029 button = tree_view->priv->pressed_button;
6030 tree_view->priv->pressed_button = -1;
6032 pspp_sheet_view_get_path_at_pos (tree_view,
6033 tree_view->priv->press_start_x,
6034 tree_view->priv->press_start_y,
6043 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6044 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6048 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6051 /* Now we can begin the drag */
6055 context = gtk_drag_begin (widget,
6056 gtk_drag_source_get_target_list (widget),
6061 set_source_row (context, model, path);
6065 gtk_tree_path_free (path);
6073 pspp_sheet_view_drag_begin (GtkWidget *widget,
6074 GdkDragContext *context)
6077 PsppSheetView *tree_view;
6078 GtkTreePath *path = NULL;
6079 gint cell_x, cell_y;
6081 TreeViewDragInfo *di;
6083 tree_view = PSPP_SHEET_VIEW (widget);
6085 /* if the user uses a custom DND source impl, we don't set the icon here */
6086 di = get_info (tree_view);
6088 if (di == NULL || !di->source_set)
6091 pspp_sheet_view_get_path_at_pos (tree_view,
6092 tree_view->priv->press_start_x,
6093 tree_view->priv->press_start_y,
6099 g_return_if_fail (path != NULL);
6101 row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6104 gtk_drag_set_icon_pixmap (context,
6105 gdk_drawable_get_colormap (row_pix),
6108 /* the + 1 is for the black border in the icon */
6109 tree_view->priv->press_start_x + 1,
6112 g_object_unref (row_pix);
6113 gtk_tree_path_free (path);
6119 pspp_sheet_view_drag_end (GtkWidget *widget,
6120 GdkDragContext *context)
6125 /* Default signal implementations for the drag signals */
6127 pspp_sheet_view_drag_data_get (GtkWidget *widget,
6128 GdkDragContext *context,
6129 GtkSelectionData *selection_data,
6133 PsppSheetView *tree_view;
6134 GtkTreeModel *model;
6135 TreeViewDragInfo *di;
6136 GtkTreePath *source_row;
6138 tree_view = PSPP_SHEET_VIEW (widget);
6140 model = pspp_sheet_view_get_model (tree_view);
6145 di = get_info (PSPP_SHEET_VIEW (widget));
6150 source_row = get_source_row (context);
6152 if (source_row == NULL)
6155 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6156 * any model; for DragSource models there are some other targets
6160 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6161 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6166 /* If drag_data_get does nothing, try providing row data. */
6167 if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6169 gtk_tree_set_row_drag_data (selection_data,
6175 gtk_tree_path_free (source_row);
6180 pspp_sheet_view_drag_data_delete (GtkWidget *widget,
6181 GdkDragContext *context)
6183 TreeViewDragInfo *di;
6184 GtkTreeModel *model;
6185 PsppSheetView *tree_view;
6186 GtkTreePath *source_row;
6188 tree_view = PSPP_SHEET_VIEW (widget);
6189 model = pspp_sheet_view_get_model (tree_view);
6191 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6194 di = get_info (tree_view);
6199 source_row = get_source_row (context);
6201 if (source_row == NULL)
6204 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6207 gtk_tree_path_free (source_row);
6209 set_source_row (context, NULL, NULL);
6213 pspp_sheet_view_drag_leave (GtkWidget *widget,
6214 GdkDragContext *context,
6217 /* unset any highlight row */
6218 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6220 PSPP_SHEET_VIEW_DROP_BEFORE);
6222 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6227 pspp_sheet_view_drag_motion (GtkWidget *widget,
6228 GdkDragContext *context,
6229 /* coordinates relative to the widget */
6235 GtkTreePath *path = NULL;
6236 PsppSheetViewDropPosition pos;
6237 PsppSheetView *tree_view;
6238 GdkDragAction suggested_action = 0;
6241 tree_view = PSPP_SHEET_VIEW (widget);
6243 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6246 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6248 /* we only know this *after* set_desination_row */
6249 empty = tree_view->priv->empty_view_drop;
6251 if (path == NULL && !empty)
6253 /* Can't drop here. */
6254 gdk_drag_status (context, 0, time);
6258 if (tree_view->priv->open_dest_timeout == 0 &&
6259 (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6260 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6266 add_scroll_timeout (tree_view);
6269 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6271 /* Request data so we can use the source row when
6272 * determining whether to accept the drop
6274 set_status_pending (context, suggested_action);
6275 gtk_drag_get_data (widget, context, target, time);
6279 set_status_pending (context, 0);
6280 gdk_drag_status (context, suggested_action, time);
6285 gtk_tree_path_free (path);
6292 pspp_sheet_view_drag_drop (GtkWidget *widget,
6293 GdkDragContext *context,
6294 /* coordinates relative to the widget */
6299 PsppSheetView *tree_view;
6301 GdkDragAction suggested_action = 0;
6302 GdkAtom target = GDK_NONE;
6303 TreeViewDragInfo *di;
6304 GtkTreeModel *model;
6305 gboolean path_down_mode;
6306 gboolean drop_append_mode;
6308 tree_view = PSPP_SHEET_VIEW (widget);
6310 model = pspp_sheet_view_get_model (tree_view);
6312 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6314 di = get_info (tree_view);
6319 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6322 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6325 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6327 if (target != GDK_NONE && path != NULL)
6329 /* in case a motion had requested drag data, change things so we
6330 * treat drag data receives as a drop.
6332 set_status_pending (context, 0);
6333 set_dest_row (context, model, path,
6334 path_down_mode, tree_view->priv->empty_view_drop,
6339 gtk_tree_path_free (path);
6341 /* Unset this thing */
6342 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6344 PSPP_SHEET_VIEW_DROP_BEFORE);
6346 if (target != GDK_NONE)
6348 gtk_drag_get_data (widget, context, target, time);
6356 pspp_sheet_view_drag_data_received (GtkWidget *widget,
6357 GdkDragContext *context,
6358 /* coordinates relative to the widget */
6361 GtkSelectionData *selection_data,
6366 TreeViewDragInfo *di;
6367 gboolean accepted = FALSE;
6368 GtkTreeModel *model;
6369 PsppSheetView *tree_view;
6370 GtkTreePath *dest_row;
6371 GdkDragAction suggested_action;
6372 gboolean path_down_mode;
6373 gboolean drop_append_mode;
6375 tree_view = PSPP_SHEET_VIEW (widget);
6377 model = pspp_sheet_view_get_model (tree_view);
6379 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6382 di = get_info (tree_view);
6387 suggested_action = get_status_pending (context);
6389 if (suggested_action)
6391 /* We are getting this data due to a request in drag_motion,
6392 * rather than due to a request in drag_drop, so we are just
6393 * supposed to call drag_status, not actually paste in the
6396 path = get_logical_dest_row (tree_view, &path_down_mode,
6400 suggested_action = 0;
6401 else if (path_down_mode)
6402 gtk_tree_path_down (path);
6404 if (suggested_action)
6406 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6412 path_down_mode = FALSE;
6413 gtk_tree_path_up (path);
6415 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6418 suggested_action = 0;
6421 suggested_action = 0;
6425 gdk_drag_status (context, suggested_action, time);
6428 gtk_tree_path_free (path);
6430 /* If you can't drop, remove user drop indicator until the next motion */
6431 if (suggested_action == 0)
6432 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6434 PSPP_SHEET_VIEW_DROP_BEFORE);
6439 dest_row = get_dest_row (context, &path_down_mode);
6441 if (dest_row == NULL)
6444 if (gtk_selection_data_get_length (selection_data) >= 0)
6448 gtk_tree_path_down (dest_row);
6449 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6450 dest_row, selection_data))
6451 gtk_tree_path_up (dest_row);
6455 if (gtk_selection_data_get_length (selection_data) >= 0)
6457 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6463 gtk_drag_finish (context,
6465 (gdk_drag_context_get_actions (context) == GDK_ACTION_MOVE),
6468 if (gtk_tree_path_get_depth (dest_row) == 1
6469 && gtk_tree_path_get_indices (dest_row)[0] == 0)
6471 /* special special case drag to "0", scroll to first item */
6472 if (!tree_view->priv->scroll_to_path)
6473 pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6476 gtk_tree_path_free (dest_row);
6479 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6484 /* GtkContainer Methods
6489 pspp_sheet_view_remove (GtkContainer *container,
6492 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6493 PsppSheetViewChild *child = NULL;
6496 tmp_list = tree_view->priv->children;
6499 child = tmp_list->data;
6500 if (child->widget == widget)
6502 gtk_widget_unparent (widget);
6504 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6505 g_list_free_1 (tmp_list);
6506 g_slice_free (PsppSheetViewChild, child);
6510 tmp_list = tmp_list->next;
6513 tmp_list = tree_view->priv->columns;
6517 PsppSheetViewColumn *column;
6518 column = tmp_list->data;
6519 if (column->button == widget)
6521 gtk_widget_unparent (widget);
6524 tmp_list = tmp_list->next;
6529 pspp_sheet_view_forall (GtkContainer *container,
6530 gboolean include_internals,
6531 GtkCallback callback,
6532 gpointer callback_data)
6534 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6535 PsppSheetViewChild *child = NULL;
6536 PsppSheetViewColumn *column;
6539 tmp_list = tree_view->priv->children;
6542 child = tmp_list->data;
6543 tmp_list = tmp_list->next;
6545 (* callback) (child->widget, callback_data);
6547 if (include_internals == FALSE)
6550 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6552 column = tmp_list->data;
6555 (* callback) (column->button, callback_data);
6559 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6560 * cells. If so we draw one big row-spanning focus rectangle.
6563 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6567 if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6568 return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6570 for (list = tree_view->priv->columns; list; list = list->next)
6572 if (!((PsppSheetViewColumn *)list->data)->visible)
6574 if (_pspp_sheet_view_column_count_special_cells (list->data))
6582 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6583 PsppSheetViewColumn *focus_column,
6584 gboolean clamp_column_visible)
6586 g_return_if_fail (focus_column != NULL);
6588 tree_view->priv->focus_column = focus_column;
6590 if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view)) != focus_column->button)
6591 gtk_widget_grab_focus (focus_column->button);
6593 if (clamp_column_visible)
6594 pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6597 /* Returns TRUE if the focus is within the headers, after the focus operation is
6601 pspp_sheet_view_header_focus (PsppSheetView *tree_view,
6602 GtkDirectionType dir,
6603 gboolean clamp_column_visible)
6605 GtkWidget *focus_child;
6606 PsppSheetViewColumn *focus_column;
6607 GList *last_column, *first_column;
6611 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6614 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
6616 first_column = tree_view->priv->columns;
6617 while (first_column)
6619 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6621 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6623 first_column = first_column->next;
6626 /* No headers are visible, or are focusable. We can't focus in or out.
6628 if (first_column == NULL)
6631 last_column = g_list_last (tree_view->priv->columns);
6634 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6636 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6638 last_column = last_column->prev;
6642 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6646 case GTK_DIR_TAB_BACKWARD:
6647 case GTK_DIR_TAB_FORWARD:
6650 if (focus_child == NULL)
6652 if (tree_view->priv->focus_column != NULL &&
6653 pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6654 focus_column = tree_view->priv->focus_column;
6656 focus_column = first_column->data;
6657 pspp_sheet_view_focus_column (tree_view, focus_column,
6658 clamp_column_visible);
6665 if (focus_child == NULL)
6667 if (tree_view->priv->focus_column != NULL)
6668 focus_column = tree_view->priv->focus_column;
6669 else if (dir == GTK_DIR_LEFT)
6670 focus_column = last_column->data;
6672 focus_column = first_column->data;
6673 pspp_sheet_view_focus_column (tree_view, focus_column,
6674 clamp_column_visible);
6678 if (gtk_widget_child_focus (focus_child, dir))
6680 /* The focus moves inside the button. */
6681 /* This is probably a great example of bad UI */
6682 if (clamp_column_visible)
6683 pspp_sheet_view_clamp_column_visible (tree_view,
6684 tree_view->priv->focus_column,
6689 /* We need to move the focus among the row of buttons. */
6690 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6691 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6694 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6695 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6697 gtk_widget_error_bell (GTK_WIDGET (tree_view));
6703 PsppSheetViewColumn *column;
6705 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6706 tmp_list = tmp_list->next;
6708 tmp_list = tmp_list->prev;
6710 if (tmp_list == NULL)
6712 g_warning ("Internal button not found");
6715 column = tmp_list->data;
6716 if (column->visible &&
6717 pspp_sheet_view_column_can_focus (column))
6721 pspp_sheet_view_focus_column (tree_view, column,
6722 clamp_column_visible);
6730 g_assert_not_reached ();
6737 /* This function returns in 'path' the first focusable path, if the given path
6738 * is already focusable, it's the returned one.
6742 search_first_focusable_path (PsppSheetView *tree_view,
6744 gboolean search_forward,
6747 /* XXX this function is trivial given that the sheetview doesn't support
6751 if (!path || !*path)
6754 _pspp_sheet_view_find_node (tree_view, *path, &node);
6762 return (*path != NULL);
6766 pspp_sheet_view_focus (GtkWidget *widget,
6767 GtkDirectionType direction)
6769 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6770 GtkContainer *container = GTK_CONTAINER (widget);
6771 GtkWidget *focus_child;
6773 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6776 focus_child = gtk_container_get_focus_child (container);
6778 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6779 /* Case 1. Headers currently have focus. */
6786 pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6788 case GTK_DIR_TAB_BACKWARD:
6791 case GTK_DIR_TAB_FORWARD:
6793 gtk_widget_grab_focus (widget);
6796 g_assert_not_reached ();
6801 /* Case 2. We don't have focus at all. */
6802 if (!gtk_widget_has_focus (widget))
6804 if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6805 gtk_widget_grab_focus (widget);
6809 /* Case 3. We have focus already. */
6810 if (direction == GTK_DIR_TAB_BACKWARD)
6811 return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6812 else if (direction == GTK_DIR_TAB_FORWARD)
6815 /* Other directions caught by the keybindings */
6816 gtk_widget_grab_focus (widget);
6821 pspp_sheet_view_grab_focus (GtkWidget *widget)
6823 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6825 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6829 pspp_sheet_view_style_updated (GtkWidget *widget)
6831 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6833 PsppSheetViewColumn *column;
6834 GtkStyleContext *context;
6836 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->style_updated (widget);
6838 if (gtk_widget_get_realized (widget))
6840 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
6841 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
6842 gtk_style_context_set_background (context, tree_view->priv->header_window);
6843 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6846 gtk_widget_style_get (widget,
6847 "expander-size", &tree_view->priv->expander_size,
6849 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6851 for (list = tree_view->priv->columns; list; list = list->next)
6853 column = list->data;
6854 _pspp_sheet_view_column_cell_set_dirty (column);
6857 tree_view->priv->fixed_height = -1;
6859 /* Invalidate cached button style. */
6860 if (tree_view->priv->button_style)
6862 g_object_unref (tree_view->priv->button_style);
6863 tree_view->priv->button_style = NULL;
6866 gtk_widget_queue_resize (widget);
6871 pspp_sheet_view_set_focus_child (GtkContainer *container,
6874 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6877 for (list = tree_view->priv->columns; list; list = list->next)
6879 if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
6881 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
6886 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
6890 pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
6891 GtkAdjustment *hadj,
6892 GtkAdjustment *vadj)
6894 gboolean need_adjust = FALSE;
6896 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
6899 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
6901 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6903 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
6905 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6907 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
6909 g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
6910 pspp_sheet_view_adjustment_changed,
6912 g_object_unref (tree_view->priv->hadjustment);
6915 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
6917 g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
6918 pspp_sheet_view_adjustment_changed,
6920 g_object_unref (tree_view->priv->vadjustment);
6923 if (tree_view->priv->hadjustment != hadj)
6925 tree_view->priv->hadjustment = hadj;
6926 g_object_ref_sink (tree_view->priv->hadjustment);
6928 g_signal_connect (tree_view->priv->hadjustment, "value-changed",
6929 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6934 if (tree_view->priv->vadjustment != vadj)
6936 tree_view->priv->vadjustment = vadj;
6937 g_object_ref_sink (tree_view->priv->vadjustment);
6939 g_signal_connect (tree_view->priv->vadjustment, "value-changed",
6940 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6946 pspp_sheet_view_adjustment_changed (NULL, tree_view);
6951 pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
6952 GtkMovementStep step,
6955 PsppSheetSelectMode mode;
6956 GdkModifierType state;
6958 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
6959 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
6960 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
6961 step == GTK_MOVEMENT_DISPLAY_LINES ||
6962 step == GTK_MOVEMENT_PAGES ||
6963 step == GTK_MOVEMENT_BUFFER_ENDS ||
6964 step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
6966 if (tree_view->priv->row_count == 0)
6968 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
6971 pspp_sheet_view_stop_editing (tree_view, FALSE);
6972 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
6973 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
6976 if (gtk_get_current_event_state (&state))
6978 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
6979 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
6980 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
6981 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
6983 /* else we assume not pressed */
6987 case GTK_MOVEMENT_LOGICAL_POSITIONS:
6988 pspp_sheet_view_move_cursor_tab (tree_view, count);
6990 case GTK_MOVEMENT_VISUAL_POSITIONS:
6991 pspp_sheet_view_move_cursor_left_right (tree_view, count, mode);
6993 case GTK_MOVEMENT_DISPLAY_LINES:
6994 pspp_sheet_view_move_cursor_up_down (tree_view, count, mode);
6996 case GTK_MOVEMENT_PAGES:
6997 pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode);
6999 case GTK_MOVEMENT_BUFFER_ENDS:
7000 pspp_sheet_view_move_cursor_start_end (tree_view, count, mode);
7002 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7003 pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode);
7006 g_assert_not_reached ();
7013 pspp_sheet_view_put (PsppSheetView *tree_view,
7014 GtkWidget *child_widget,
7016 PsppSheetViewColumn *column)
7018 PsppSheetViewChild *child;
7020 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7021 g_return_if_fail (GTK_IS_WIDGET (child_widget));
7023 child = g_slice_new (PsppSheetViewChild);
7025 child->widget = child_widget;
7026 _pspp_sheet_view_find_node (tree_view, path, &child->node);
7027 if (child->node < 0)
7029 g_assert_not_reached ();
7031 child->column = column;
7033 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7035 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7036 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7038 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7041 /* TreeModel Callbacks
7045 pspp_sheet_view_row_changed (GtkTreeModel *model,
7050 PsppSheetView *tree_view = (PsppSheetView *)data;
7052 gboolean free_path = FALSE;
7053 GtkTreePath *cursor_path;
7055 g_return_if_fail (path != NULL || iter != NULL);
7057 if (tree_view->priv->cursor != NULL)
7058 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7062 if (tree_view->priv->edited_column &&
7063 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7064 pspp_sheet_view_stop_editing (tree_view, TRUE);
7066 if (cursor_path != NULL)
7067 gtk_tree_path_free (cursor_path);
7071 path = gtk_tree_model_get_path (model, iter);
7074 else if (iter == NULL)
7075 gtk_tree_model_get_iter (model, iter, path);
7077 _pspp_sheet_view_find_node (tree_view,
7083 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7084 pspp_sheet_view_node_queue_redraw (tree_view, node);
7088 gtk_tree_path_free (path);
7092 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7097 PsppSheetView *tree_view = (PsppSheetView *) data;
7100 gint height = tree_view->priv->fixed_height;
7101 gboolean free_path = FALSE;
7102 gboolean node_visible = TRUE;
7104 g_return_if_fail (path != NULL || iter != NULL);
7108 path = gtk_tree_model_get_path (model, iter);
7111 else if (iter == NULL)
7112 gtk_tree_model_get_iter (model, iter, path);
7114 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7116 /* Update all row-references */
7117 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7118 indices = gtk_tree_path_get_indices (path);
7119 tmpnode = indices[0];
7121 range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7125 if (node_visible && node_is_visible (tree_view, tmpnode))
7126 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7128 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7131 install_presize_handler (tree_view);
7133 gtk_tree_path_free (path);
7137 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7141 PsppSheetView *tree_view = (PsppSheetView *)data;
7144 g_return_if_fail (path != NULL);
7146 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7148 _pspp_sheet_view_find_node (tree_view, path, &node);
7153 range_tower_delete (tree_view->priv->selected, node, 1);
7155 /* Ensure we don't have a dangling pointer to a dead node */
7156 ensure_unprelighted (tree_view);
7158 /* Cancel editting if we've started */
7159 pspp_sheet_view_stop_editing (tree_view, TRUE);
7161 if (tree_view->priv->destroy_count_func)
7163 gint child_count = 0;
7164 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7167 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7169 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7171 gtk_tree_row_reference_free (tree_view->priv->top_row);
7172 tree_view->priv->top_row = NULL;
7175 install_scroll_sync_handler (tree_view);
7177 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7180 if (helper_data.changed)
7181 g_signal_emit_by_name (tree_view->priv->selection, "changed");
7186 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7187 GtkTreePath *parent,
7192 PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7195 /* XXX need to adjust selection */
7196 len = gtk_tree_model_iter_n_children (model, iter);
7201 gtk_tree_row_reference_reordered (G_OBJECT (data),
7206 if (gtk_tree_path_get_depth (parent) != 0)
7209 if (tree_view->priv->edited_column)
7210 pspp_sheet_view_stop_editing (tree_view, TRUE);
7212 /* we need to be unprelighted */
7213 ensure_unprelighted (tree_view);
7215 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7217 pspp_sheet_view_dy_to_top_row (tree_view);
7221 /* Internal tree functions
7226 pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view,
7227 PsppSheetViewColumn *column,
7231 PsppSheetViewColumn *tmp_column = NULL;
7242 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7245 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7247 list = (rtl ? list->prev : list->next))
7249 tmp_column = list->data;
7251 if (tmp_column == column)
7254 if (tmp_column->visible)
7255 total_width += tmp_column->width;
7258 if (tmp_column != column)
7260 g_warning (G_STRLOC": passed-in column isn't in the tree");
7269 if (column->visible)
7270 *x2 = total_width + column->width;
7272 *x2 = total_width; /* width of 0 */
7276 /* Make sure the node is visible vertically */
7278 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7281 gint node_dy, height;
7282 GtkTreePath *path = NULL;
7284 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7287 /* just return if the node is visible, avoiding a costly expose */
7288 node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7289 height = ROW_HEIGHT (tree_view);
7290 if (node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment)
7291 && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
7292 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
7295 path = _pspp_sheet_view_find_path (tree_view, node);
7298 /* We process updates because we want to clear old selected items when we scroll.
7299 * if this is removed, we get a "selection streak" at the bottom. */
7300 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7301 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7302 gtk_tree_path_free (path);
7307 pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
7308 PsppSheetViewColumn *column,
7309 gboolean focus_to_cell)
7316 x = column->allocation.x;
7317 width = column->allocation.width;
7319 if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7321 /* The column is larger than the horizontal page size. If the
7322 * column has cells which can be focussed individually, then we make
7323 * sure the cell which gets focus is fully visible (if even the
7324 * focus cell is bigger than the page size, we make sure the
7325 * left-hand side of the cell is visible).
7327 * If the column does not have those so-called special cells, we
7328 * make sure the left-hand side of the column is visible.
7331 if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7333 GtkTreePath *cursor_path;
7334 GdkRectangle background_area, cell_area, focus_area;
7336 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7338 pspp_sheet_view_get_cell_area (tree_view,
7339 cursor_path, column, &cell_area);
7340 pspp_sheet_view_get_background_area (tree_view,
7341 cursor_path, column,
7344 gtk_tree_path_free (cursor_path);
7346 _pspp_sheet_view_column_get_focus_area (column,
7352 width = focus_area.width;
7354 if (width < gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7356 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7357 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7358 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7359 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7360 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7364 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7366 gtk_adjustment_get_lower (tree_view->priv->hadjustment),
7367 gtk_adjustment_get_upper (tree_view->priv->hadjustment)
7368 - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)));
7372 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7373 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7374 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7375 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7376 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7381 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7386 path = gtk_tree_path_new ();
7388 gtk_tree_path_append_index (path, node);
7393 _pspp_sheet_view_find_node (PsppSheetView *tree_view,
7397 gint *indices = gtk_tree_path_get_indices (path);
7398 gint depth = gtk_tree_path_get_depth (path);
7401 if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7407 pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
7410 gboolean add_shifted_binding,
7411 GtkMovementStep step,
7415 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7420 if (add_shifted_binding)
7421 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7426 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7429 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7434 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7441 pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view,
7442 PsppSheetViewColumn *column)
7444 PsppSheetViewColumn *left_column;
7445 PsppSheetViewColumn *cur_column = NULL;
7446 PsppSheetViewColumnReorder *reorder;
7451 /* We want to precalculate the motion list such that we know what column slots
7455 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7457 /* First, identify all possible drop spots */
7459 tmp_list = g_list_last (tree_view->priv->columns);
7461 tmp_list = g_list_first (tree_view->priv->columns);
7465 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7466 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7468 if (cur_column->visible == FALSE)
7471 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7472 if (left_column != column && cur_column != column &&
7473 tree_view->priv->column_drop_func &&
7474 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7476 left_column = cur_column;
7479 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7480 reorder->left_column = left_column;
7481 left_column = reorder->right_column = cur_column;
7483 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7486 /* Add the last one */
7487 if (tree_view->priv->column_drop_func == NULL ||
7488 ((left_column != column) &&
7489 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7491 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7492 reorder->left_column = left_column;
7493 reorder->right_column = NULL;
7494 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7497 /* We quickly check to see if it even makes sense to reorder columns. */
7498 /* If there is nothing that can be moved, then we return */
7500 if (tree_view->priv->column_drag_info == NULL)
7503 /* We know there are always 2 slots possbile, as you can always return column. */
7504 /* If that's all there is, return */
7505 if (tree_view->priv->column_drag_info->next == NULL ||
7506 (tree_view->priv->column_drag_info->next->next == NULL &&
7507 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7508 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7510 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7511 g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7512 g_list_free (tree_view->priv->column_drag_info);
7513 tree_view->priv->column_drag_info = NULL;
7516 /* We fill in the ranges for the columns, now that we've isolated them */
7517 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7519 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7521 reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7523 reorder->left_align = left;
7524 if (tmp_list->next != NULL)
7526 g_assert (tmp_list->next->data);
7527 left = reorder->right_align = (reorder->right_column->allocation.x +
7528 reorder->right_column->allocation.width +
7529 ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7533 gint width = gdk_window_get_width (tree_view->priv->header_window);
7534 reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7540 _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view,
7541 PsppSheetViewColumn *column)
7543 GdkEvent *send_event;
7544 GtkAllocation allocation;
7546 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7547 GdkDisplay *display = gdk_screen_get_display (screen);
7549 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7550 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7551 g_return_if_fail (column->button);
7553 pspp_sheet_view_set_column_drag_info (tree_view, column);
7555 if (tree_view->priv->column_drag_info == NULL)
7558 if (tree_view->priv->drag_window == NULL)
7560 GdkWindowAttr attributes;
7561 guint attributes_mask;
7563 attributes.window_type = GDK_WINDOW_CHILD;
7564 attributes.wclass = GDK_INPUT_OUTPUT;
7565 attributes.x = column->allocation.x;
7567 attributes.width = column->allocation.width;
7568 attributes.height = column->allocation.height;
7569 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7570 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7571 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL ;
7573 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7576 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7579 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7580 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7582 gtk_grab_remove (column->button);
7584 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7585 send_event->crossing.send_event = TRUE;
7586 send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button)));
7587 send_event->crossing.subwindow = NULL;
7588 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7589 send_event->crossing.time = GDK_CURRENT_TIME;
7591 gtk_propagate_event (column->button, send_event);
7592 gdk_event_free (send_event);
7594 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7595 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7596 send_event->button.send_event = TRUE;
7597 send_event->button.time = GDK_CURRENT_TIME;
7598 send_event->button.x = -1;
7599 send_event->button.y = -1;
7600 send_event->button.axes = NULL;
7601 send_event->button.state = 0;
7602 send_event->button.button = 1;
7603 send_event->button.device =
7604 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
7606 send_event->button.x_root = 0;
7607 send_event->button.y_root = 0;
7609 gtk_propagate_event (column->button, send_event);
7610 gdk_event_free (send_event);
7612 /* Kids, don't try this at home */
7613 g_object_ref (column->button);
7614 gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7615 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7616 gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7617 g_object_unref (column->button);
7619 tree_view->priv->drag_column_x = column->allocation.x;
7620 allocation = column->allocation;
7622 gtk_widget_size_allocate (column->button, &allocation);
7623 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7625 tree_view->priv->drag_column = column;
7626 gdk_window_show (tree_view->priv->drag_window);
7628 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7630 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7631 while (gtk_events_pending ())
7632 gtk_main_iteration ();
7634 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7635 gdk_pointer_grab (tree_view->priv->drag_window,
7637 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7638 NULL, NULL, GDK_CURRENT_TIME);
7639 gdk_keyboard_grab (tree_view->priv->drag_window,
7645 _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view,
7647 const GdkRectangle *clip_rect)
7650 GtkAllocation allocation;
7652 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7655 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
7657 rect.width = MAX (tree_view->priv->width, allocation.width);
7659 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7660 rect.height = ROW_HEIGHT (tree_view);
7664 GdkRectangle new_rect;
7666 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7668 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7672 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7677 pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
7679 const GdkRectangle *clip_rect)
7683 _pspp_sheet_view_find_node (tree_view, path, &node);
7686 _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7690 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7693 GtkTreePath *cursor_path;
7695 if ((tree_view->priv->row_count == 0) ||
7696 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7700 if (tree_view->priv->cursor)
7701 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7703 if (cursor_path == NULL)
7705 /* There's no cursor. Move the cursor to the first selected row, if any
7706 * are selected, otherwise to the first row in the sheetview.
7708 GList *selected_rows;
7709 GtkTreeModel *model;
7710 PsppSheetSelection *selection;
7712 selection = pspp_sheet_view_get_selection (tree_view);
7713 selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7717 /* XXX we could avoid doing O(n) work to get this result */
7718 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7719 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7720 g_list_free (selected_rows);
7724 cursor_path = gtk_tree_path_new_first ();
7725 search_first_focusable_path (tree_view, &cursor_path,
7729 gtk_tree_row_reference_free (tree_view->priv->cursor);
7730 tree_view->priv->cursor = NULL;
7734 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7735 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7736 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0);
7738 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0);
7744 /* Now find a column for the cursor. */
7745 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7747 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7748 gtk_tree_path_free (cursor_path);
7750 if (tree_view->priv->focus_column == NULL)
7753 for (list = tree_view->priv->columns; list; list = list->next)
7755 if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7757 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7758 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7759 pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7769 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7771 PsppSheetSelectMode mode)
7773 gint selection_count;
7774 int cursor_node = -1;
7775 int new_cursor_node = -1;
7776 GtkTreePath *cursor_path = NULL;
7777 gboolean grab_focus = TRUE;
7779 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7783 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7784 /* FIXME: we lost the cursor; should we get the first? */
7787 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7788 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7790 if (cursor_node < 0)
7791 /* FIXME: we lost the cursor; should we get the first? */
7794 selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7796 if (selection_count == 0
7797 && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7798 && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
7800 /* Don't move the cursor, but just select the current node */
7801 new_cursor_node = cursor_node;
7806 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7808 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7811 gtk_tree_path_free (cursor_path);
7813 if (new_cursor_node)
7815 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7817 search_first_focusable_path (tree_view, &cursor_path,
7822 gtk_tree_path_free (cursor_path);
7826 * If the list has only one item and multi-selection is set then select
7827 * the row (if not yet selected).
7829 if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7830 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7831 new_cursor_node < 0)
7834 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7836 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7838 if (new_cursor_node < 0
7839 && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
7841 new_cursor_node = cursor_node;
7845 new_cursor_node = -1;
7849 if (new_cursor_node >= 0)
7851 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7852 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode);
7853 gtk_tree_path_free (cursor_path);
7857 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7859 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
7861 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
7863 GTK_DIR_UP : GTK_DIR_DOWN))
7865 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
7868 gtk_widget_child_focus (toplevel,
7870 GTK_DIR_TAB_BACKWARD :
7871 GTK_DIR_TAB_FORWARD);
7878 gtk_widget_error_bell (GTK_WIDGET (tree_view));
7883 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7885 return new_cursor_node >= 0;
7889 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
7891 PsppSheetSelectMode mode)
7893 int cursor_node = -1;
7894 GtkTreePath *old_cursor_path = NULL;
7895 GtkTreePath *cursor_path = NULL;
7896 int start_cursor_node = -1;
7899 gint vertical_separator;
7901 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7904 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
7905 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7907 /* This is sorta weird. Focus in should give us a cursor */
7910 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
7911 _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
7913 if (cursor_node < 0)
7915 /* FIXME: we lost the cursor. Should we try to get one? */
7916 gtk_tree_path_free (old_cursor_path);
7920 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7921 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
7922 y += tree_view->priv->cursor_offset;
7923 y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment);
7924 y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment), (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator);
7926 if (y >= tree_view->priv->height)
7927 y = tree_view->priv->height - 1;
7929 tree_view->priv->cursor_offset =
7930 pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
7932 if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
7934 cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7935 tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
7938 y -= tree_view->priv->cursor_offset;
7939 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7941 start_cursor_node = cursor_node;
7943 if (! search_first_focusable_path (tree_view, &cursor_path,
7947 /* It looks like we reached the end of the view without finding
7948 * a focusable row. We will step backwards to find the last
7951 cursor_node = start_cursor_node;
7952 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7954 search_first_focusable_path (tree_view, &cursor_path,
7963 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7965 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode);
7968 pspp_sheet_view_scroll_to_point (tree_view, -1, y);
7969 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7970 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
7972 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
7973 gtk_widget_error_bell (GTK_WIDGET (tree_view));
7975 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7978 gtk_tree_path_free (old_cursor_path);
7979 gtk_tree_path_free (cursor_path);
7983 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
7985 PsppSheetSelectMode mode)
7987 int cursor_node = -1;
7988 GtkTreePath *cursor_path = NULL;
7989 PsppSheetViewColumn *column;
7992 gboolean found_column = FALSE;
7995 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7997 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8000 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8001 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8005 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8006 if (cursor_node < 0)
8008 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8010 gtk_tree_path_free (cursor_path);
8013 gtk_tree_path_free (cursor_path);
8015 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8016 if (tree_view->priv->focus_column)
8018 for (; list; list = (rtl ? list->prev : list->next))
8020 if (list->data == tree_view->priv->focus_column)
8027 gboolean left, right;
8029 column = list->data;
8030 if (column->visible == FALSE || column->row_head)
8033 pspp_sheet_view_column_cell_set_cell_data (column,
8034 tree_view->priv->model,
8039 right = list->prev ? TRUE : FALSE;
8040 left = list->next ? TRUE : FALSE;
8044 left = list->prev ? TRUE : FALSE;
8045 right = list->next ? TRUE : FALSE;
8048 if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8050 tree_view->priv->focus_column = column;
8051 found_column = TRUE;
8056 list = rtl ? list->prev : list->next;
8058 list = rtl ? list->next : list->prev;
8063 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8064 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8065 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8069 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8072 pspp_sheet_view_clamp_column_visible (tree_view,
8073 tree_view->priv->focus_column, TRUE);
8077 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8079 PsppSheetSelectMode mode)
8081 int cursor_node = -1;
8082 GtkTreePath *cursor_path = NULL;
8083 PsppSheetViewColumn *column;
8084 PsppSheetViewColumn *found_column;
8089 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8091 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8094 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8095 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8099 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8100 if (cursor_node < 0)
8102 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8104 gtk_tree_path_free (cursor_path);
8107 gtk_tree_path_free (cursor_path);
8109 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8110 if (tree_view->priv->focus_column)
8112 for (; list; list = (rtl ? list->prev : list->next))
8114 if (list->data == tree_view->priv->focus_column)
8119 found_column = NULL;
8122 gboolean left, right;
8124 column = list->data;
8125 if (column->visible == FALSE || column->row_head)
8128 pspp_sheet_view_column_cell_set_cell_data (column,
8129 tree_view->priv->model,
8134 right = list->prev ? TRUE : FALSE;
8135 left = list->next ? TRUE : FALSE;
8139 left = list->prev ? TRUE : FALSE;
8140 right = list->next ? TRUE : FALSE;
8143 if (column->tabbable
8144 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8145 found_column = column;
8149 list = rtl ? list->prev : list->next;
8151 list = rtl ? list->next : list->prev;
8156 tree_view->priv->focus_column = found_column;
8157 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8158 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8159 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8162 pspp_sheet_view_clamp_column_visible (tree_view,
8163 tree_view->priv->focus_column, TRUE);
8167 try_move_cursor_tab (PsppSheetView *tree_view,
8168 gboolean start_at_focus_column,
8171 PsppSheetViewColumn *column;
8173 int cursor_node = -1;
8174 GtkTreePath *cursor_path = NULL;
8178 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8179 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8183 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8184 if (cursor_node < 0)
8186 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8188 gtk_tree_path_free (cursor_path);
8191 gtk_tree_path_free (cursor_path);
8193 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8194 if (start_at_focus_column)
8197 ? g_list_last (tree_view->priv->columns)
8198 : g_list_first (tree_view->priv->columns));
8199 if (tree_view->priv->focus_column)
8201 for (; list; list = (rtl ? list->prev : list->next))
8203 if (list->data == tree_view->priv->focus_column)
8210 list = (rtl ^ (count == 1)
8211 ? g_list_first (tree_view->priv->columns)
8212 : g_list_last (tree_view->priv->columns));
8217 gboolean left, right;
8219 column = list->data;
8220 if (column->visible == FALSE || !column->tabbable)
8223 pspp_sheet_view_column_cell_set_cell_data (column,
8224 tree_view->priv->model,
8229 right = list->prev ? TRUE : FALSE;
8230 left = list->next ? TRUE : FALSE;
8234 left = list->prev ? TRUE : FALSE;
8235 right = list->next ? TRUE : FALSE;
8238 if (column->tabbable
8239 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8241 tree_view->priv->focus_column = column;
8242 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8243 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8244 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8249 list = rtl ? list->prev : list->next;
8251 list = rtl ? list->next : list->prev;
8258 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8261 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8264 if (!try_move_cursor_tab (tree_view, TRUE, count))
8266 if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0)
8267 && !try_move_cursor_tab (tree_view, FALSE, count))
8268 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8271 pspp_sheet_view_clamp_column_visible (tree_view,
8272 tree_view->priv->focus_column, TRUE);
8276 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8278 PsppSheetSelectMode mode)
8282 GtkTreePath *old_path;
8284 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8287 g_return_if_fail (tree_view->priv->row_count > 0);
8289 pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8293 /* Now go forward to find the first focusable row. */
8294 path = _pspp_sheet_view_find_path (tree_view, 0);
8295 search_first_focusable_path (tree_view, &path,
8296 TRUE, &cursor_node);
8300 /* Now go backwards to find last focusable row. */
8301 path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8302 search_first_focusable_path (tree_view, &path,
8303 FALSE, &cursor_node);
8309 if (gtk_tree_path_compare (old_path, path))
8311 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
8312 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8316 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8320 gtk_tree_path_free (old_path);
8321 gtk_tree_path_free (path);
8325 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8327 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8330 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8331 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8334 pspp_sheet_selection_select_all (tree_view->priv->selection);
8340 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8342 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8345 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8346 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8349 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8355 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8356 gboolean start_editing,
8357 PsppSheetSelectMode mode)
8360 int cursor_node = -1;
8361 GtkTreePath *cursor_path = NULL;
8363 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8366 if (tree_view->priv->cursor)
8367 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8369 if (cursor_path == NULL)
8372 _pspp_sheet_view_find_node (tree_view, cursor_path,
8375 if (cursor_node < 0)
8377 gtk_tree_path_free (cursor_path);
8381 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing &&
8382 tree_view->priv->focus_column)
8384 if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8386 gtk_tree_path_free (cursor_path);
8391 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8397 /* We bail out if the original (tree, node) don't exist anymore after
8398 * handling the selection-changed callback. We do return TRUE because
8399 * the key press has been handled at this point.
8401 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8403 if (cursor_node != new_node)
8406 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8408 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8409 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8411 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8412 pspp_sheet_view_row_activated (tree_view, cursor_path,
8413 tree_view->priv->focus_column);
8415 gtk_tree_path_free (cursor_path);
8421 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8424 int cursor_node = -1;
8425 GtkTreePath *cursor_path = NULL;
8427 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8431 if (tree_view->priv->cursor)
8432 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8434 if (cursor_path == NULL)
8437 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8438 if (cursor_node < 0)
8440 gtk_tree_path_free (cursor_path);
8444 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8447 PSPP_SHEET_SELECT_MODE_TOGGLE,
8450 /* We bail out if the original (tree, node) don't exist anymore after
8451 * handling the selection-changed callback. We do return TRUE because
8452 * the key press has been handled at this point.
8454 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8456 if (cursor_node != new_node)
8459 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8461 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8462 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8463 gtk_tree_path_free (cursor_path);
8469 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8471 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8472 tree_view->priv->typeselect_flush_timeout = 0;
8477 /* Cut and paste from gtkwindow.c */
8479 send_focus_change (GtkWidget *widget,
8482 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8484 fevent->focus_change.type = GDK_FOCUS_CHANGE;
8485 fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
8486 fevent->focus_change.in = in;
8488 gtk_widget_send_focus_change (widget, fevent);
8489 gdk_event_free (fevent);
8493 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8495 GtkWidget *frame, *vbox, *toplevel;
8498 if (tree_view->priv->search_custom_entry_set)
8501 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8502 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8504 if (tree_view->priv->search_window != NULL)
8506 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8507 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8508 GTK_WINDOW (tree_view->priv->search_window));
8509 else if (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)))
8510 gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
8511 GTK_WINDOW (tree_view->priv->search_window));
8512 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8516 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8517 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8519 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8520 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8521 GTK_WINDOW (tree_view->priv->search_window));
8523 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8524 GDK_WINDOW_TYPE_HINT_UTILITY);
8525 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8526 g_signal_connect (tree_view->priv->search_window, "delete-event",
8527 G_CALLBACK (pspp_sheet_view_search_delete_event),
8529 g_signal_connect (tree_view->priv->search_window, "key-press-event",
8530 G_CALLBACK (pspp_sheet_view_search_key_press_event),
8532 g_signal_connect (tree_view->priv->search_window, "button-press-event",
8533 G_CALLBACK (pspp_sheet_view_search_button_press_event),
8535 g_signal_connect (tree_view->priv->search_window, "scroll-event",
8536 G_CALLBACK (pspp_sheet_view_search_scroll_event),
8539 frame = gtk_frame_new (NULL);
8540 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8541 gtk_widget_show (frame);
8542 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8544 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
8545 gtk_widget_show (vbox);
8546 gtk_container_add (GTK_CONTAINER (frame), vbox);
8547 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8550 tree_view->priv->search_entry = gtk_entry_new ();
8551 gtk_widget_show (tree_view->priv->search_entry);
8552 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8553 G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8555 g_signal_connect (tree_view->priv->search_entry,
8556 "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8560 g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8562 G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8566 gtk_container_add (GTK_CONTAINER (vbox),
8567 tree_view->priv->search_entry);
8569 gtk_widget_realize (tree_view->priv->search_entry);
8572 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8573 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8576 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8577 gboolean keybinding)
8579 /* We only start interactive search if we have focus or the columns
8580 * have focus. If one of our children have focus, we don't want to
8584 gboolean found_focus = FALSE;
8585 GtkWidgetClass *entry_parent_class;
8587 if (!tree_view->priv->enable_search && !keybinding)
8590 if (tree_view->priv->search_custom_entry_set)
8593 if (tree_view->priv->search_window != NULL &&
8594 gtk_widget_get_visible (tree_view->priv->search_window))
8597 for (list = tree_view->priv->columns; list; list = list->next)
8599 PsppSheetViewColumn *column;
8601 column = list->data;
8602 if (! column->visible)
8605 if (column->button && gtk_widget_has_focus (column->button))
8612 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8618 if (tree_view->priv->search_column < 0)
8621 pspp_sheet_view_ensure_interactive_directory (tree_view);
8624 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8627 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8628 gtk_widget_show (tree_view->priv->search_window);
8629 if (tree_view->priv->search_entry_changed_id == 0)
8631 tree_view->priv->search_entry_changed_id =
8632 g_signal_connect (tree_view->priv->search_entry, "changed",
8633 G_CALLBACK (pspp_sheet_view_search_init),
8637 tree_view->priv->typeselect_flush_timeout =
8638 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8639 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8642 /* Grab focus will select all the text. We don't want that to happen, so we
8643 * call the parent instance and bypass the selection change. This is probably
8644 * really non-kosher. */
8645 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8646 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8648 /* send focus-in event */
8649 send_focus_change (tree_view->priv->search_entry, TRUE);
8651 /* search first matching iter */
8652 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8658 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8660 return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8663 /* this function returns the new width of the column being resized given
8664 * the column and x position of the cursor; the x cursor position is passed
8665 * in as a pointer and automagicly corrected if it's beyond min/max limits
8668 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8672 PsppSheetViewColumn *column;
8676 /* first translate the x position from gtk_widget_get_window (widget)
8677 * to clist->clist_window
8679 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8680 column = g_list_nth (tree_view->priv->columns, i)->data;
8681 width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8683 /* Clamp down the value */
8684 if (column->min_width == -1)
8685 width = MAX (column->button_request, width);
8687 width = MAX (column->min_width, width);
8688 if (column->max_width != -1)
8689 width = MIN (width, column->max_width);
8691 *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8697 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8701 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8702 PsppSheetView *tree_view)
8704 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8708 gdk_window_move (tree_view->priv->bin_window,
8709 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8710 TREE_VIEW_HEADER_HEIGHT (tree_view));
8711 gdk_window_move (tree_view->priv->header_window,
8712 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8714 dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8716 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8720 /* update our dy and top_row */
8721 tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8723 update_prelight (tree_view,
8724 tree_view->priv->event_last_x,
8725 tree_view->priv->event_last_y);
8727 if (!tree_view->priv->in_top_row_to_dy)
8728 pspp_sheet_view_dy_to_top_row (tree_view);
8731 update_childrens_allocation(tree_view);
8739 * pspp_sheet_view_new:
8741 * Creates a new #PsppSheetView widget.
8743 * Return value: A newly created #PsppSheetView widget.
8746 pspp_sheet_view_new (void)
8748 return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
8752 * pspp_sheet_view_new_with_model:
8753 * @model: the model.
8755 * Creates a new #PsppSheetView widget with the model initialized to @model.
8757 * Return value: A newly created #PsppSheetView widget.
8760 pspp_sheet_view_new_with_model (GtkTreeModel *model)
8762 return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
8769 * pspp_sheet_view_get_model:
8770 * @tree_view: a #PsppSheetView
8772 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
8775 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
8778 pspp_sheet_view_get_model (PsppSheetView *tree_view)
8780 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8782 return tree_view->priv->model;
8786 * pspp_sheet_view_set_model:
8787 * @tree_view: A #GtkTreeNode.
8788 * @model: (allow-none): The model.
8790 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
8791 * set, it will remove it before setting the new model. If @model is %NULL,
8792 * then it will unset the old model.
8795 pspp_sheet_view_set_model (PsppSheetView *tree_view,
8796 GtkTreeModel *model)
8798 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8799 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
8801 if (model == tree_view->priv->model)
8804 if (tree_view->priv->scroll_to_path)
8806 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8807 tree_view->priv->scroll_to_path = NULL;
8810 if (tree_view->priv->model)
8812 GList *tmplist = tree_view->priv->columns;
8814 if (tree_view->priv->selected)
8815 range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
8816 pspp_sheet_view_stop_editing (tree_view, TRUE);
8818 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8819 pspp_sheet_view_row_changed,
8821 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8822 pspp_sheet_view_row_inserted,
8824 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8825 pspp_sheet_view_row_deleted,
8827 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8828 pspp_sheet_view_rows_reordered,
8831 for (; tmplist; tmplist = tmplist->next)
8832 _pspp_sheet_view_column_unset_model (tmplist->data,
8833 tree_view->priv->model);
8835 tree_view->priv->prelight_node = -1;
8837 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
8838 tree_view->priv->drag_dest_row = NULL;
8839 gtk_tree_row_reference_free (tree_view->priv->cursor);
8840 tree_view->priv->cursor = NULL;
8841 gtk_tree_row_reference_free (tree_view->priv->anchor);
8842 tree_view->priv->anchor = NULL;
8843 gtk_tree_row_reference_free (tree_view->priv->top_row);
8844 tree_view->priv->top_row = NULL;
8845 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8846 tree_view->priv->scroll_to_path = NULL;
8848 tree_view->priv->scroll_to_column = NULL;
8850 g_object_unref (tree_view->priv->model);
8852 tree_view->priv->search_column = -1;
8853 tree_view->priv->fixed_height = -1;
8854 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
8855 tree_view->priv->last_button_x = -1;
8856 tree_view->priv->last_button_y = -1;
8859 tree_view->priv->model = model;
8861 if (tree_view->priv->model)
8865 if (tree_view->priv->search_column == -1)
8867 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
8869 GType type = gtk_tree_model_get_column_type (model, i);
8871 if (g_value_type_transformable (type, G_TYPE_STRING))
8873 tree_view->priv->search_column = i;
8879 g_object_ref (tree_view->priv->model);
8880 g_signal_connect (tree_view->priv->model,
8882 G_CALLBACK (pspp_sheet_view_row_changed),
8884 g_signal_connect (tree_view->priv->model,
8886 G_CALLBACK (pspp_sheet_view_row_inserted),
8888 g_signal_connect (tree_view->priv->model,
8890 G_CALLBACK (pspp_sheet_view_row_deleted),
8892 g_signal_connect (tree_view->priv->model,
8894 G_CALLBACK (pspp_sheet_view_rows_reordered),
8897 tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
8899 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
8900 install_presize_handler (tree_view);
8903 g_object_notify (G_OBJECT (tree_view), "model");
8905 if (tree_view->priv->selection)
8906 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
8908 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8909 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8913 * pspp_sheet_view_get_selection:
8914 * @tree_view: A #PsppSheetView.
8916 * Gets the #PsppSheetSelection associated with @tree_view.
8918 * Return value: A #PsppSheetSelection object.
8920 PsppSheetSelection *
8921 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
8923 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8925 return tree_view->priv->selection;
8929 * pspp_sheet_view_get_hadjustment:
8930 * @tree_view: A #PsppSheetView
8932 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
8934 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
8938 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
8940 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8942 return pspp_sheet_view_do_get_hadjustment (tree_view);
8945 static GtkAdjustment *
8946 pspp_sheet_view_do_get_hadjustment (PsppSheetView *tree_view)
8948 return tree_view->priv->hadjustment;
8952 * pspp_sheet_view_set_hadjustment:
8953 * @tree_view: A #PsppSheetView
8954 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
8956 * Sets the #GtkAdjustment for the current horizontal aspect.
8959 pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view,
8960 GtkAdjustment *adjustment)
8962 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8964 pspp_sheet_view_set_adjustments (tree_view,
8966 tree_view->priv->vadjustment);
8968 g_object_notify (G_OBJECT (tree_view), "hadjustment");
8972 pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
8973 GtkAdjustment *adjustment)
8975 PsppSheetViewPrivate *priv = tree_view->priv;
8977 if (adjustment && priv->hadjustment == adjustment)
8980 if (priv->hadjustment != NULL)
8982 g_signal_handlers_disconnect_by_func (priv->hadjustment,
8983 pspp_sheet_view_adjustment_changed,
8985 g_object_unref (priv->hadjustment);
8988 if (adjustment == NULL)
8989 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
8992 g_signal_connect (adjustment, "value-changed",
8993 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
8994 priv->hadjustment = g_object_ref_sink (adjustment);
8995 /* FIXME: Adjustment should probably be populated here with fresh values, but
8996 * internal details are too complicated for me to decipher right now.
8998 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9000 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9004 * pspp_sheet_view_get_vadjustment:
9005 * @tree_view: A #PsppSheetView
9007 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9009 * Return value: (transfer none): A #GtkAdjustment object, or %NULL
9010 * if none is currently being used.
9012 * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
9015 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9017 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9019 return pspp_sheet_view_do_get_vadjustment (tree_view);
9022 static GtkAdjustment *
9023 pspp_sheet_view_do_get_vadjustment (PsppSheetView *tree_view)
9025 return tree_view->priv->vadjustment;
9029 * pspp_sheet_view_set_vadjustment:
9030 * @tree_view: A #PsppSheetView
9031 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9033 * Sets the #GtkAdjustment for the current vertical aspect.
9035 * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
9038 pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view,
9039 GtkAdjustment *adjustment)
9041 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9042 g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
9044 pspp_sheet_view_do_set_vadjustment (tree_view, adjustment);
9048 pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
9049 GtkAdjustment *adjustment)
9051 PsppSheetViewPrivate *priv = tree_view->priv;
9053 if (adjustment && priv->vadjustment == adjustment)
9056 if (priv->vadjustment != NULL)
9058 g_signal_handlers_disconnect_by_func (priv->vadjustment,
9059 pspp_sheet_view_adjustment_changed,
9061 g_object_unref (priv->vadjustment);
9064 if (adjustment == NULL)
9065 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9068 g_signal_connect (adjustment, "value-changed",
9069 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9070 priv->vadjustment = g_object_ref_sink (adjustment);
9071 /* FIXME: Adjustment should probably be populated here with fresh values, but
9072 * internal details are too complicated for me to decipher right now.
9074 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9075 g_object_notify (G_OBJECT (tree_view), "vadjustment");
9078 /* Column and header operations */
9081 * pspp_sheet_view_get_headers_visible:
9082 * @tree_view: A #PsppSheetView.
9084 * Returns %TRUE if the headers on the @tree_view are visible.
9086 * Return value: Whether the headers are visible or not.
9089 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9091 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9093 return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9097 * pspp_sheet_view_set_headers_visible:
9098 * @tree_view: A #PsppSheetView.
9099 * @headers_visible: %TRUE if the headers are visible
9101 * Sets the visibility state of the headers.
9104 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9105 gboolean headers_visible)
9109 PsppSheetViewColumn *column;
9110 GtkAllocation allocation;
9112 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9114 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9116 headers_visible = !! headers_visible;
9118 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9121 if (headers_visible)
9122 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9124 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9126 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9128 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9129 if (headers_visible)
9131 gdk_window_move_resize (tree_view->priv->bin_window, x, y + TREE_VIEW_HEADER_HEIGHT (tree_view),
9132 tree_view->priv->width, allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view));
9134 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9135 pspp_sheet_view_map_buttons (tree_view);
9139 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9141 for (list = tree_view->priv->columns; list; list = list->next)
9143 column = list->data;
9145 gtk_widget_unmap (column->button);
9147 gdk_window_hide (tree_view->priv->header_window);
9151 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view));
9152 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2);
9153 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
9154 gtk_adjustment_set_upper (tree_view->priv->vadjustment, tree_view->priv->height);
9155 gtk_adjustment_changed (tree_view->priv->vadjustment);
9157 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9159 g_object_notify (G_OBJECT (tree_view), "headers-visible");
9163 * pspp_sheet_view_columns_autosize:
9164 * @tree_view: A #PsppSheetView.
9166 * Resizes all columns to their optimal width. Only works after the
9167 * treeview has been realized.
9170 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9172 gboolean dirty = FALSE;
9174 PsppSheetViewColumn *column;
9176 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9178 for (list = tree_view->priv->columns; list; list = list->next)
9180 column = list->data;
9181 _pspp_sheet_view_column_cell_set_dirty (column);
9186 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9190 * pspp_sheet_view_set_headers_clickable:
9191 * @tree_view: A #PsppSheetView.
9192 * @setting: %TRUE if the columns are clickable.
9194 * Allow the column title buttons to be clicked.
9197 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9202 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9204 for (list = tree_view->priv->columns; list; list = list->next)
9205 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9207 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9212 * pspp_sheet_view_get_headers_clickable:
9213 * @tree_view: A #PsppSheetView.
9215 * Returns whether all header columns are clickable.
9217 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9222 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9226 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9228 for (list = tree_view->priv->columns; list; list = list->next)
9229 if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9236 * pspp_sheet_view_set_rules_hint
9237 * @tree_view: a #PsppSheetView
9238 * @setting: %TRUE if the tree requires reading across rows
9240 * This function tells GTK+ that the user interface for your
9241 * application requires users to read across tree rows and associate
9242 * cells with one another. By default, GTK+ will then render the tree
9243 * with alternating row colors. Do <emphasis>not</emphasis> use it
9244 * just because you prefer the appearance of the ruled tree; that's a
9245 * question for the theme. Some themes will draw tree rows in
9246 * alternating colors even when rules are turned off, and users who
9247 * prefer that appearance all the time can choose those themes. You
9248 * should call this function only as a <emphasis>semantic</emphasis>
9249 * hint to the theme engine that your tree makes alternating colors
9250 * useful from a functional standpoint (since it has lots of columns,
9255 pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view,
9258 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9260 setting = setting != FALSE;
9262 if (tree_view->priv->has_rules != setting)
9264 tree_view->priv->has_rules = setting;
9265 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9268 g_object_notify (G_OBJECT (tree_view), "rules-hint");
9272 * pspp_sheet_view_get_rules_hint
9273 * @tree_view: a #PsppSheetView
9275 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9277 * Return value: %TRUE if rules are useful for the user of this tree
9280 pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view)
9282 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9284 return tree_view->priv->has_rules;
9287 /* Public Column functions
9291 * pspp_sheet_view_append_column:
9292 * @tree_view: A #PsppSheetView.
9293 * @column: The #PsppSheetViewColumn to add.
9295 * Appends @column to the list of columns.
9297 * Return value: The number of columns in @tree_view after appending.
9300 pspp_sheet_view_append_column (PsppSheetView *tree_view,
9301 PsppSheetViewColumn *column)
9303 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9304 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9305 g_return_val_if_fail (column->tree_view == NULL, -1);
9307 return pspp_sheet_view_insert_column (tree_view, column, -1);
9312 * pspp_sheet_view_remove_column:
9313 * @tree_view: A #PsppSheetView.
9314 * @column: The #PsppSheetViewColumn to remove.
9316 * Removes @column from @tree_view.
9318 * Return value: The number of columns in @tree_view after removing.
9321 pspp_sheet_view_remove_column (PsppSheetView *tree_view,
9322 PsppSheetViewColumn *column)
9324 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9325 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9326 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9328 if (tree_view->priv->focus_column == column)
9329 tree_view->priv->focus_column = NULL;
9331 if (tree_view->priv->edited_column == column)
9333 pspp_sheet_view_stop_editing (tree_view, TRUE);
9335 /* no need to, but just to be sure ... */
9336 tree_view->priv->edited_column = NULL;
9339 _pspp_sheet_view_column_unset_tree_view (column);
9341 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9342 tree_view->priv->n_columns--;
9344 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9348 _pspp_sheet_view_column_unrealize_button (column);
9349 for (list = tree_view->priv->columns; list; list = list->next)
9351 PsppSheetViewColumn *tmp_column;
9353 tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9354 if (tmp_column->visible)
9355 _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9358 if (tree_view->priv->n_columns == 0 &&
9359 pspp_sheet_view_get_headers_visible (tree_view) &&
9360 tree_view->priv->header_window)
9361 gdk_window_hide (tree_view->priv->header_window);
9363 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9366 g_object_unref (column);
9367 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9369 return tree_view->priv->n_columns;
9373 * pspp_sheet_view_insert_column:
9374 * @tree_view: A #PsppSheetView.
9375 * @column: The #PsppSheetViewColumn to be inserted.
9376 * @position: The position to insert @column in.
9378 * This inserts the @column into the @tree_view at @position. If @position is
9379 * -1, then the column is inserted at the end.
9381 * Return value: The number of columns in @tree_view after insertion.
9384 pspp_sheet_view_insert_column (PsppSheetView *tree_view,
9385 PsppSheetViewColumn *column,
9388 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9389 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9390 g_return_val_if_fail (column->tree_view == NULL, -1);
9392 g_object_ref_sink (column);
9394 if (tree_view->priv->n_columns == 0 &&
9395 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9396 pspp_sheet_view_get_headers_visible (tree_view))
9398 gdk_window_show (tree_view->priv->header_window);
9401 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9403 tree_view->priv->n_columns++;
9405 _pspp_sheet_view_column_set_tree_view (column, tree_view);
9407 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9411 _pspp_sheet_view_column_realize_button (column);
9413 for (list = tree_view->priv->columns; list; list = list->next)
9415 column = PSPP_SHEET_VIEW_COLUMN (list->data);
9416 if (column->visible)
9417 _pspp_sheet_view_column_cell_set_dirty (column);
9419 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9422 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9424 return tree_view->priv->n_columns;
9428 * pspp_sheet_view_insert_column_with_attributes:
9429 * @tree_view: A #PsppSheetView
9430 * @position: The position to insert the new column in.
9431 * @title: The title to set the header to.
9432 * @cell: The #GtkCellRenderer.
9433 * @Varargs: A %NULL-terminated list of attributes.
9435 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9436 * @position. If @position is -1, then the newly created column is inserted at
9437 * the end. The column is initialized with the attributes given.
9439 * Return value: The number of columns in @tree_view after insertion.
9442 pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view,
9445 GtkCellRenderer *cell,
9448 PsppSheetViewColumn *column;
9453 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9455 column = pspp_sheet_view_column_new ();
9456 pspp_sheet_view_column_set_title (column, title);
9457 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9459 va_start (args, cell);
9461 attribute = va_arg (args, gchar *);
9463 while (attribute != NULL)
9465 column_id = va_arg (args, gint);
9466 pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9467 attribute = va_arg (args, gchar *);
9472 pspp_sheet_view_insert_column (tree_view, column, position);
9474 return tree_view->priv->n_columns;
9478 * pspp_sheet_view_insert_column_with_data_func:
9479 * @tree_view: a #PsppSheetView
9480 * @position: Position to insert, -1 for append
9481 * @title: column title
9482 * @cell: cell renderer for column
9483 * @func: function to set attributes of cell renderer
9484 * @data: data for @func
9485 * @dnotify: destroy notifier for @data
9487 * Convenience function that inserts a new column into the #PsppSheetView
9488 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9489 * attributes (normally using data from the model). See also
9490 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9492 * Return value: number of columns in the tree view post-insert
9495 pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view,
9498 GtkCellRenderer *cell,
9499 PsppSheetCellDataFunc func,
9501 GDestroyNotify dnotify)
9503 PsppSheetViewColumn *column;
9505 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9507 column = pspp_sheet_view_column_new ();
9508 pspp_sheet_view_column_set_title (column, title);
9509 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9510 pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9512 pspp_sheet_view_insert_column (tree_view, column, position);
9514 return tree_view->priv->n_columns;
9518 * pspp_sheet_view_get_column:
9519 * @tree_view: A #PsppSheetView.
9520 * @n: The position of the column, counting from 0.
9522 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9524 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9527 PsppSheetViewColumn *
9528 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9531 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9533 if (n < 0 || n >= tree_view->priv->n_columns)
9536 if (tree_view->priv->columns == NULL)
9539 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9543 * pspp_sheet_view_get_columns:
9544 * @tree_view: A #PsppSheetView
9546 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9547 * The returned list must be freed with g_list_free ().
9549 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9552 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9554 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9556 return g_list_copy (tree_view->priv->columns);
9560 * pspp_sheet_view_move_column_after:
9561 * @tree_view: A #PsppSheetView
9562 * @column: The #PsppSheetViewColumn to be moved.
9563 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9565 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9566 * @column is placed in the first position.
9569 pspp_sheet_view_move_column_after (PsppSheetView *tree_view,
9570 PsppSheetViewColumn *column,
9571 PsppSheetViewColumn *base_column)
9573 GList *column_list_el, *base_el = NULL;
9575 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9577 column_list_el = g_list_find (tree_view->priv->columns, column);
9578 g_return_if_fail (column_list_el != NULL);
9582 base_el = g_list_find (tree_view->priv->columns, base_column);
9583 g_return_if_fail (base_el != NULL);
9586 if (column_list_el->prev == base_el)
9589 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9590 if (base_el == NULL)
9592 column_list_el->prev = NULL;
9593 column_list_el->next = tree_view->priv->columns;
9594 if (column_list_el->next)
9595 column_list_el->next->prev = column_list_el;
9596 tree_view->priv->columns = column_list_el;
9600 column_list_el->prev = base_el;
9601 column_list_el->next = base_el->next;
9602 if (column_list_el->next)
9603 column_list_el->next->prev = column_list_el;
9604 base_el->next = column_list_el;
9607 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9609 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9610 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9613 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9617 * pspp_sheet_view_set_column_drag_function:
9618 * @tree_view: A #PsppSheetView.
9619 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9620 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9621 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9623 * Sets a user function for determining where a column may be dropped when
9624 * dragged. This function is called on every column pair in turn at the
9625 * beginning of a column drag to determine where a drop can take place. The
9626 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9627 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9628 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9629 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9630 * @tree_view reverts to the default behavior of allowing all columns to be
9631 * dropped everywhere.
9634 pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view,
9635 PsppSheetViewColumnDropFunc func,
9637 GDestroyNotify destroy)
9639 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9641 if (tree_view->priv->column_drop_func_data_destroy)
9642 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9644 tree_view->priv->column_drop_func = func;
9645 tree_view->priv->column_drop_func_data = user_data;
9646 tree_view->priv->column_drop_func_data_destroy = destroy;
9650 * pspp_sheet_view_scroll_to_point:
9651 * @tree_view: a #PsppSheetView
9652 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9653 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9655 * Scrolls the tree view such that the top-left corner of the visible
9656 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9657 * in tree coordinates. The @tree_view must be realized before
9658 * this function is called. If it isn't, you probably want to be
9659 * using pspp_sheet_view_scroll_to_cell().
9661 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9664 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9668 GtkAdjustment *hadj;
9669 GtkAdjustment *vadj;
9671 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9672 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9674 hadj = tree_view->priv->hadjustment;
9675 vadj = tree_view->priv->vadjustment;
9678 gtk_adjustment_set_value (hadj, CLAMP (tree_x, gtk_adjustment_get_lower (hadj), gtk_adjustment_get_upper (hadj) - gtk_adjustment_get_page_size (hadj)));
9680 gtk_adjustment_set_value (vadj, CLAMP (tree_y, gtk_adjustment_get_lower (vadj), gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj)));
9684 * pspp_sheet_view_scroll_to_cell:
9685 * @tree_view: A #PsppSheetView.
9686 * @path: (allow-none): The path of the row to move to, or %NULL.
9687 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9688 * @use_align: whether to use alignment arguments, or %FALSE.
9689 * @row_align: The vertical alignment of the row specified by @path.
9690 * @col_align: The horizontal alignment of the column specified by @column.
9692 * Moves the alignments of @tree_view to the position specified by @column and
9693 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9694 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9695 * or @path need to be non-%NULL. @row_align determines where the row is
9696 * placed, and @col_align determines where @column is placed. Both are expected
9697 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9698 * right/bottom alignment, 0.5 means center.
9700 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9701 * tree does the minimum amount of work to scroll the cell onto the screen.
9702 * This means that the cell will be scrolled to the edge closest to its current
9703 * position. If the cell is currently visible on the screen, nothing is done.
9705 * This function only works if the model is set, and @path is a valid row on the
9706 * model. If the model changes before the @tree_view is realized, the centered
9707 * path will be modified to reflect this change.
9710 pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view,
9712 PsppSheetViewColumn *column,
9717 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9718 g_return_if_fail (tree_view->priv->model != NULL);
9719 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9720 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9721 g_return_if_fail (path != NULL || column != NULL);
9724 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9725 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9727 row_align = CLAMP (row_align, 0.0, 1.0);
9728 col_align = CLAMP (col_align, 0.0, 1.0);
9731 /* Note: Despite the benefits that come from having one code path for the
9732 * scrolling code, we short-circuit validate_visible_area's immplementation as
9733 * it is much slower than just going to the point.
9735 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9736 !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9737 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9739 if (tree_view->priv->scroll_to_path)
9740 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9742 tree_view->priv->scroll_to_path = NULL;
9743 tree_view->priv->scroll_to_column = NULL;
9746 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
9748 tree_view->priv->scroll_to_column = column;
9749 tree_view->priv->scroll_to_use_align = use_align;
9750 tree_view->priv->scroll_to_row_align = row_align;
9751 tree_view->priv->scroll_to_col_align = col_align;
9753 install_presize_handler (tree_view);
9757 GdkRectangle cell_rect;
9758 GdkRectangle vis_rect;
9759 gint dest_x, dest_y;
9761 pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
9762 pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
9764 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
9766 dest_x = vis_rect.x;
9767 dest_y = vis_rect.y;
9773 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
9777 if (cell_rect.x < vis_rect.x)
9778 dest_x = cell_rect.x;
9779 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
9780 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
9788 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
9789 dest_y = MAX (dest_y, 0);
9793 if (cell_rect.y < vis_rect.y)
9794 dest_y = cell_rect.y;
9795 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
9796 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
9800 pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
9805 * pspp_sheet_view_row_activated:
9806 * @tree_view: A #PsppSheetView
9807 * @path: The #GtkTreePath to be activated.
9808 * @column: The #PsppSheetViewColumn to be activated.
9810 * Activates the cell determined by @path and @column.
9813 pspp_sheet_view_row_activated (PsppSheetView *tree_view,
9815 PsppSheetViewColumn *column)
9817 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9819 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
9824 * pspp_sheet_view_get_reorderable:
9825 * @tree_view: a #PsppSheetView
9827 * Retrieves whether the user can reorder the tree via drag-and-drop. See
9828 * pspp_sheet_view_set_reorderable().
9830 * Return value: %TRUE if the tree can be reordered.
9833 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
9835 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9837 return tree_view->priv->reorderable;
9841 * pspp_sheet_view_set_reorderable:
9842 * @tree_view: A #PsppSheetView.
9843 * @reorderable: %TRUE, if the tree can be reordered.
9845 * This function is a convenience function to allow you to reorder
9846 * models that support the #GtkDragSourceIface and the
9847 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
9848 * these. If @reorderable is %TRUE, then the user can reorder the
9849 * model by dragging and dropping rows. The developer can listen to
9850 * these changes by connecting to the model's row_inserted and
9851 * row_deleted signals. The reordering is implemented by setting up
9852 * the tree view as a drag source and destination. Therefore, drag and
9853 * drop can not be used in a reorderable view for any other purpose.
9855 * This function does not give you any degree of control over the order -- any
9856 * reordering is allowed. If more control is needed, you should probably
9857 * handle drag and drop manually.
9860 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
9861 gboolean reorderable)
9863 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9865 reorderable = reorderable != FALSE;
9867 if (tree_view->priv->reorderable == reorderable)
9872 const GtkTargetEntry row_targets[] = {
9873 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
9876 pspp_sheet_view_enable_model_drag_source (tree_view,
9879 G_N_ELEMENTS (row_targets),
9881 pspp_sheet_view_enable_model_drag_dest (tree_view,
9883 G_N_ELEMENTS (row_targets),
9888 pspp_sheet_view_unset_rows_drag_source (tree_view);
9889 pspp_sheet_view_unset_rows_drag_dest (tree_view);
9892 tree_view->priv->reorderable = reorderable;
9894 g_object_notify (G_OBJECT (tree_view), "reorderable");
9897 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
9898 is pressed, other rows will be unselected.
9900 If CLAMP_NODE is true, then the sheetview will scroll to make the row
9903 pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
9905 gboolean clear_and_select,
9906 gboolean clamp_node,
9907 PsppSheetSelectMode mode)
9911 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9913 GtkTreePath *cursor_path;
9914 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9915 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
9916 gtk_tree_path_free (cursor_path);
9919 gtk_tree_row_reference_free (tree_view->priv->cursor);
9920 tree_view->priv->cursor = NULL;
9922 _pspp_sheet_view_find_node (tree_view, path, &node);
9923 tree_view->priv->cursor =
9924 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
9925 tree_view->priv->model,
9928 if (tree_view->priv->row_count > 0)
9932 if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
9933 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
9937 /* We have to re-find tree and node here again, somebody might have
9938 * cleared the node or the whole tree in the PsppSheetSelection::changed
9939 * callback. If the nodes differ we bail out here.
9941 _pspp_sheet_view_find_node (tree_view, path, &new_node);
9943 if (node != new_node)
9948 pspp_sheet_view_clamp_node_visible (tree_view, node);
9949 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
9953 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
9957 * pspp_sheet_view_get_cursor:
9958 * @tree_view: A #PsppSheetView
9959 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
9960 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
9962 * Fills in @path and @focus_column with the current path and focus column. If
9963 * the cursor isn't currently set, then *@path will be %NULL. If no column
9964 * currently has focus, then *@focus_column will be %NULL.
9966 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
9967 * you are done with it.
9970 pspp_sheet_view_get_cursor (PsppSheetView *tree_view,
9972 PsppSheetViewColumn **focus_column)
9974 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9978 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9979 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9986 *focus_column = tree_view->priv->focus_column;
9991 * pspp_sheet_view_set_cursor:
9992 * @tree_view: A #PsppSheetView
9993 * @path: A #GtkTreePath
9994 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
9995 * @start_editing: %TRUE if the specified cell should start being edited.
9997 * Sets the current keyboard focus to be at @path, and selects it. This is
9998 * useful when you want to focus the user's attention on a particular row. If
9999 * @focus_column is not %NULL, then focus is given to the column specified by
10000 * it. Additionally, if @focus_column is specified, and @start_editing is
10001 * %TRUE, then editing should be started in the specified cell.
10002 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10003 * in order to give keyboard focus to the widget. Please note that editing
10004 * can only happen when the widget is realized.
10006 * If @path is invalid for @model, the current cursor (if any) will be unset
10007 * and the function will return without failing.
10010 pspp_sheet_view_set_cursor (PsppSheetView *tree_view,
10012 PsppSheetViewColumn *focus_column,
10013 gboolean start_editing)
10015 pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10016 NULL, start_editing);
10020 * pspp_sheet_view_set_cursor_on_cell:
10021 * @tree_view: A #PsppSheetView
10022 * @path: A #GtkTreePath
10023 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10024 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10025 * @start_editing: %TRUE if the specified cell should start being edited.
10027 * Sets the current keyboard focus to be at @path, and selects it. This is
10028 * useful when you want to focus the user's attention on a particular row. If
10029 * @focus_column is not %NULL, then focus is given to the column specified by
10030 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10031 * contains 2 or more editable or activatable cells, then focus is given to
10032 * the cell specified by @focus_cell. Additionally, if @focus_column is
10033 * specified, and @start_editing is %TRUE, then editing should be started in
10034 * the specified cell. This function is often followed by
10035 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10036 * widget. Please note that editing can only happen when the widget is
10039 * If @path is invalid for @model, the current cursor (if any) will be unset
10040 * and the function will return without failing.
10045 pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view,
10047 PsppSheetViewColumn *focus_column,
10048 GtkCellRenderer *focus_cell,
10049 gboolean start_editing)
10051 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10052 g_return_if_fail (path != NULL);
10053 g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10055 if (!tree_view->priv->model)
10060 g_return_if_fail (focus_column);
10061 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10064 /* cancel the current editing, if it exists */
10065 if (tree_view->priv->edited_column &&
10066 tree_view->priv->edited_column->editable_widget)
10067 pspp_sheet_view_stop_editing (tree_view, TRUE);
10069 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
10071 if (focus_column && focus_column->visible)
10074 gboolean column_in_tree = FALSE;
10076 for (list = tree_view->priv->columns; list; list = list->next)
10077 if (list->data == focus_column)
10079 column_in_tree = TRUE;
10082 g_return_if_fail (column_in_tree);
10083 tree_view->priv->focus_column = focus_column;
10085 pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10087 pspp_sheet_view_start_editing (tree_view, path);
10089 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10090 pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10096 * pspp_sheet_view_get_bin_window:
10097 * @tree_view: A #PsppSheetView
10099 * Returns the window that @tree_view renders to. This is used primarily to
10100 * compare to <literal>event->window</literal> to confirm that the event on
10101 * @tree_view is on the right window.
10103 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10106 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10108 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10110 return tree_view->priv->bin_window;
10114 * pspp_sheet_view_get_path_at_pos:
10115 * @tree_view: A #PsppSheetView.
10116 * @x: The x position to be identified (relative to bin_window).
10117 * @y: The y position to be identified (relative to bin_window).
10118 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10119 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10120 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10121 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10123 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10124 * (please see pspp_sheet_view_get_bin_window()).
10125 * That is, @x and @y are relative to an events coordinates. @x and @y must
10126 * come from an event on the @tree_view only where <literal>event->window ==
10127 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10128 * things like popup menus. If @path is non-%NULL, then it will be filled
10129 * with the #GtkTreePath at that point. This path should be freed with
10130 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10131 * with the column at that point. @cell_x and @cell_y return the coordinates
10132 * relative to the cell background (i.e. the @background_area passed to
10133 * gtk_cell_renderer_render()). This function is only meaningful if
10134 * @tree_view is realized. Therefore this function will always return %FALSE
10135 * if @tree_view is not realized or does not have a model.
10137 * For converting widget coordinates (eg. the ones you get from
10138 * GtkWidget::query-tooltip), please see
10139 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10141 * Return value: %TRUE if a row exists at that coordinate.
10144 pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view,
10147 GtkTreePath **path,
10148 PsppSheetViewColumn **column,
10155 g_return_val_if_fail (tree_view != NULL, FALSE);
10162 if (tree_view->priv->bin_window == NULL)
10165 if (tree_view->priv->row_count == 0)
10168 if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment))
10171 if (x < 0 || y < 0)
10174 if (column || cell_x)
10176 PsppSheetViewColumn *tmp_column;
10177 PsppSheetViewColumn *last_column = NULL;
10179 gint remaining_x = x;
10180 gboolean found = FALSE;
10183 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10184 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10186 list = (rtl ? list->prev : list->next))
10188 tmp_column = list->data;
10190 if (tmp_column->visible == FALSE)
10193 last_column = tmp_column;
10194 if (remaining_x <= tmp_column->width)
10199 *column = tmp_column;
10202 *cell_x = remaining_x;
10206 remaining_x -= tmp_column->width;
10209 /* If found is FALSE and there is a last_column, then it the remainder
10210 * space is in that area
10217 *column = last_column;
10220 *cell_x = last_column->width + remaining_x;
10229 y_offset = pspp_sheet_view_find_offset (tree_view,
10230 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10237 *cell_y = y_offset;
10240 *path = _pspp_sheet_view_find_path (tree_view, node);
10245 /* Computes 'cell_area' from 'background_area', which must be the background
10246 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10247 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10248 the cell area as passed to _pspp_sheet_view_column_cell_render().
10250 'column' is required to properly adjust 'cell_area->x' and
10251 'cell_area->width'. It may be set to NULL if these values are not of
10252 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10255 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
10256 PsppSheetViewColumn *column,
10257 const GdkRectangle *background_area,
10258 gboolean subtract_focus_rect,
10259 GdkRectangle *cell_area)
10261 gint vertical_separator;
10262 gint horizontal_separator;
10264 *cell_area = *background_area;
10266 gtk_widget_style_get (GTK_WIDGET (tree_view),
10267 "vertical-separator", &vertical_separator,
10268 "horizontal-separator", &horizontal_separator,
10270 cell_area->x += horizontal_separator / 2;
10271 cell_area->y += vertical_separator / 2;
10272 cell_area->width -= horizontal_separator;
10273 cell_area->height -= vertical_separator;
10275 if (subtract_focus_rect)
10277 int focus_line_width;
10279 gtk_widget_style_get (GTK_WIDGET (tree_view),
10280 "focus-line-width", &focus_line_width,
10282 cell_area->x += focus_line_width;
10283 cell_area->y += focus_line_width;
10284 cell_area->width -= 2 * focus_line_width;
10285 cell_area->height -= 2 * focus_line_width;
10288 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10290 gint grid_line_width;
10291 gtk_widget_style_get (GTK_WIDGET (tree_view),
10292 "grid-line-width", &grid_line_width,
10295 if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10296 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10299 PsppSheetViewColumn *first_column, *last_column;
10302 /* Find the last visible column. */
10303 last_column = NULL;
10304 for (list = g_list_last (tree_view->priv->columns);
10308 PsppSheetViewColumn *c = list->data;
10316 /* Find the first visible column. */
10317 first_column = NULL;
10318 for (list = g_list_first (tree_view->priv->columns);
10322 PsppSheetViewColumn *c = list->data;
10330 if (column == first_column)
10332 cell_area->width -= grid_line_width / 2;
10334 else if (column == last_column)
10336 cell_area->x += grid_line_width / 2;
10337 cell_area->width -= grid_line_width / 2;
10341 cell_area->x += grid_line_width / 2;
10342 cell_area->width -= grid_line_width;
10346 if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10347 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10349 cell_area->y += grid_line_width / 2;
10350 cell_area->height -= grid_line_width;
10354 if (column == NULL)
10357 cell_area->width = 0;
10362 * pspp_sheet_view_get_cell_area:
10363 * @tree_view: a #PsppSheetView
10364 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10365 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10366 * @rect: rectangle to fill with cell rect
10368 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10369 * row specified by @path and the column specified by @column. If @path is
10370 * %NULL, or points to a path not currently displayed, the @y and @height fields
10371 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10372 * fields will be filled with 0. The sum of all cell rects does not cover the
10373 * entire tree; there are extra pixels in between rows, for example. The
10374 * returned rectangle is equivalent to the @cell_area passed to
10375 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10379 pspp_sheet_view_get_cell_area (PsppSheetView *tree_view,
10381 PsppSheetViewColumn *column,
10382 GdkRectangle *rect)
10384 GdkRectangle background_area;
10386 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10387 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10388 g_return_if_fail (rect != NULL);
10389 g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10390 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10392 pspp_sheet_view_get_background_area (tree_view, path, column,
10394 pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10399 * pspp_sheet_view_get_background_area:
10400 * @tree_view: a #PsppSheetView
10401 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10402 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10403 * @rect: rectangle to fill with cell background rect
10405 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10406 * row specified by @path and the column specified by @column. If @path is
10407 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10408 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10409 * fields will be filled with 0. The returned rectangle is equivalent to the
10410 * @background_area passed to gtk_cell_renderer_render(). These background
10411 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10412 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10413 * itself, excluding surrounding borders.
10417 pspp_sheet_view_get_background_area (PsppSheetView *tree_view,
10419 PsppSheetViewColumn *column,
10420 GdkRectangle *rect)
10424 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10425 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10426 g_return_if_fail (rect != NULL);
10435 /* Get vertical coords */
10437 _pspp_sheet_view_find_node (tree_view, path, &node);
10441 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10443 rect->height = ROW_HEIGHT (tree_view);
10450 pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10451 rect->width = x2 - rect->x;
10456 * pspp_sheet_view_get_visible_rect:
10457 * @tree_view: a #PsppSheetView
10458 * @visible_rect: rectangle to fill
10460 * Fills @visible_rect with the currently-visible region of the
10461 * buffer, in tree coordinates. Convert to bin_window coordinates with
10462 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10463 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10464 * scrollable area of the tree.
10467 pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view,
10468 GdkRectangle *visible_rect)
10472 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10474 widget = GTK_WIDGET (tree_view);
10478 GtkAllocation allocation;
10479 gtk_widget_get_allocation (widget, &allocation);
10480 visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
10481 visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
10482 visible_rect->width = allocation.width;
10483 visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10488 * pspp_sheet_view_widget_to_tree_coords:
10489 * @tree_view: a #PsppSheetView
10490 * @wx: X coordinate relative to bin_window
10491 * @wy: Y coordinate relative to bin_window
10492 * @tx: return location for tree X coordinate
10493 * @ty: return location for tree Y coordinate
10495 * Converts bin_window coordinates to coordinates for the
10496 * tree (the full scrollable area of the tree).
10498 * Deprecated: 2.12: Due to historial reasons the name of this function is
10499 * incorrect. For converting coordinates relative to the widget to
10500 * bin_window coordinates, please see
10501 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10505 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10511 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10514 *tx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10516 *ty = wy + tree_view->priv->dy;
10520 * pspp_sheet_view_tree_to_widget_coords:
10521 * @tree_view: a #PsppSheetView
10522 * @tx: tree X coordinate
10523 * @ty: tree Y coordinate
10524 * @wx: return location for X coordinate relative to bin_window
10525 * @wy: return location for Y coordinate relative to bin_window
10527 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10528 * to bin_window coordinates.
10530 * Deprecated: 2.12: Due to historial reasons the name of this function is
10531 * incorrect. For converting bin_window coordinates to coordinates relative
10532 * to bin_window, please see
10533 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10537 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10543 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10546 *wx = tx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10548 *wy = ty - tree_view->priv->dy;
10553 * pspp_sheet_view_convert_widget_to_tree_coords:
10554 * @tree_view: a #PsppSheetView
10555 * @wx: X coordinate relative to the widget
10556 * @wy: Y coordinate relative to the widget
10557 * @tx: return location for tree X coordinate
10558 * @ty: return location for tree Y coordinate
10560 * Converts widget coordinates to coordinates for the
10561 * tree (the full scrollable area of the tree).
10566 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10574 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10576 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10579 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10585 * pspp_sheet_view_convert_tree_to_widget_coords:
10586 * @tree_view: a #PsppSheetView
10587 * @tx: X coordinate relative to the tree
10588 * @ty: Y coordinate relative to the tree
10589 * @wx: return location for widget X coordinate
10590 * @wy: return location for widget Y coordinate
10592 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10593 * to widget coordinates.
10598 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10606 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10608 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10611 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10617 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10618 * @tree_view: a #PsppSheetView
10619 * @wx: X coordinate relative to the widget
10620 * @wy: Y coordinate relative to the widget
10621 * @bx: return location for bin_window X coordinate
10622 * @by: return location for bin_window Y coordinate
10624 * Converts widget coordinates to coordinates for the bin_window
10625 * (see pspp_sheet_view_get_bin_window()).
10630 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10636 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10639 *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10641 *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10645 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10646 * @tree_view: a #PsppSheetView
10647 * @bx: bin_window X coordinate
10648 * @by: bin_window Y coordinate
10649 * @wx: return location for widget X coordinate
10650 * @wy: return location for widget Y coordinate
10652 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10653 * to widget relative coordinates.
10658 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10664 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10667 *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10669 *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10673 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10674 * @tree_view: a #PsppSheetView
10675 * @tx: tree X coordinate
10676 * @ty: tree Y coordinate
10677 * @bx: return location for X coordinate relative to bin_window
10678 * @by: return location for Y coordinate relative to bin_window
10680 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10681 * to bin_window coordinates.
10686 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10692 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10697 *by = ty - tree_view->priv->dy;
10701 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10702 * @tree_view: a #PsppSheetView
10703 * @bx: X coordinate relative to bin_window
10704 * @by: Y coordinate relative to bin_window
10705 * @tx: return location for tree X coordinate
10706 * @ty: return location for tree Y coordinate
10708 * Converts bin_window coordinates to coordinates for the
10709 * tree (the full scrollable area of the tree).
10714 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10720 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10725 *ty = by + tree_view->priv->dy;
10731 * pspp_sheet_view_get_visible_range:
10732 * @tree_view: A #PsppSheetView
10733 * @start_path: (allow-none): Return location for start of region, or %NULL.
10734 * @end_path: (allow-none): Return location for end of region, or %NULL.
10736 * Sets @start_path and @end_path to be the first and last visible path.
10737 * Note that there may be invisible paths in between.
10739 * The paths should be freed with gtk_tree_path_free() after use.
10741 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
10746 pspp_sheet_view_get_visible_range (PsppSheetView *tree_view,
10747 GtkTreePath **start_path,
10748 GtkTreePath **end_path)
10753 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10755 if (!tree_view->priv->row_count)
10762 pspp_sheet_view_find_offset (tree_view,
10763 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
10766 *start_path = _pspp_sheet_view_find_path (tree_view, node);
10775 if (tree_view->priv->height < gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
10776 y = tree_view->priv->height - 1;
10778 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1;
10780 pspp_sheet_view_find_offset (tree_view, y, &node);
10782 *end_path = _pspp_sheet_view_find_path (tree_view, node);
10791 unset_reorderable (PsppSheetView *tree_view)
10793 if (tree_view->priv->reorderable)
10795 tree_view->priv->reorderable = FALSE;
10796 g_object_notify (G_OBJECT (tree_view), "reorderable");
10801 * pspp_sheet_view_enable_model_drag_source:
10802 * @tree_view: a #PsppSheetView
10803 * @start_button_mask: Mask of allowed buttons to start drag
10804 * @targets: the table of targets that the drag will support
10805 * @n_targets: the number of items in @targets
10806 * @actions: the bitmask of possible actions for a drag from this
10809 * Turns @tree_view into a drag source for automatic DND. Calling this
10810 * method sets #PsppSheetView:reorderable to %FALSE.
10813 pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view,
10814 GdkModifierType start_button_mask,
10815 const GtkTargetEntry *targets,
10817 GdkDragAction actions)
10819 TreeViewDragInfo *di;
10821 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10823 gtk_drag_source_set (GTK_WIDGET (tree_view),
10829 di = ensure_info (tree_view);
10831 di->start_button_mask = start_button_mask;
10832 di->source_actions = actions;
10833 di->source_set = TRUE;
10835 unset_reorderable (tree_view);
10839 * pspp_sheet_view_enable_model_drag_dest:
10840 * @tree_view: a #PsppSheetView
10841 * @targets: the table of targets that the drag will support
10842 * @n_targets: the number of items in @targets
10843 * @actions: the bitmask of possible actions for a drag from this
10846 * Turns @tree_view into a drop destination for automatic DND. Calling
10847 * this method sets #PsppSheetView:reorderable to %FALSE.
10850 pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view,
10851 const GtkTargetEntry *targets,
10853 GdkDragAction actions)
10855 TreeViewDragInfo *di;
10857 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10859 gtk_drag_dest_set (GTK_WIDGET (tree_view),
10865 di = ensure_info (tree_view);
10866 di->dest_set = TRUE;
10868 unset_reorderable (tree_view);
10872 * pspp_sheet_view_unset_rows_drag_source:
10873 * @tree_view: a #PsppSheetView
10875 * Undoes the effect of
10876 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
10877 * #PsppSheetView:reorderable to %FALSE.
10880 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
10882 TreeViewDragInfo *di;
10884 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10886 di = get_info (tree_view);
10890 if (di->source_set)
10892 gtk_drag_source_unset (GTK_WIDGET (tree_view));
10893 di->source_set = FALSE;
10896 if (!di->dest_set && !di->source_set)
10897 remove_info (tree_view);
10900 unset_reorderable (tree_view);
10904 * pspp_sheet_view_unset_rows_drag_dest:
10905 * @tree_view: a #PsppSheetView
10907 * Undoes the effect of
10908 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
10909 * #PsppSheetView:reorderable to %FALSE.
10912 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
10914 TreeViewDragInfo *di;
10916 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10918 di = get_info (tree_view);
10924 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
10925 di->dest_set = FALSE;
10928 if (!di->dest_set && !di->source_set)
10929 remove_info (tree_view);
10932 unset_reorderable (tree_view);
10936 * pspp_sheet_view_set_drag_dest_row:
10937 * @tree_view: a #PsppSheetView
10938 * @path: (allow-none): The path of the row to highlight, or %NULL.
10939 * @pos: Specifies whether to drop before, after or into the row
10941 * Sets the row that is highlighted for feedback.
10944 pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view,
10946 PsppSheetViewDropPosition pos)
10948 GtkTreePath *current_dest;
10950 /* Note; this function is exported to allow a custom DND
10951 * implementation, so it can't touch TreeViewDragInfo
10954 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10956 current_dest = NULL;
10958 if (tree_view->priv->drag_dest_row)
10960 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
10961 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
10964 /* special case a drop on an empty model */
10965 tree_view->priv->empty_view_drop = 0;
10967 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
10968 && gtk_tree_path_get_depth (path) == 1
10969 && gtk_tree_path_get_indices (path)[0] == 0)
10973 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
10977 tree_view->priv->empty_view_drop = 1;
10980 tree_view->priv->drag_dest_pos = pos;
10984 tree_view->priv->drag_dest_row =
10985 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
10986 pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
10989 tree_view->priv->drag_dest_row = NULL;
10993 int node, new_node;
10995 _pspp_sheet_view_find_node (tree_view, current_dest, &node);
10996 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11000 new_node = pspp_sheet_view_node_next (tree_view, node);
11002 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11004 new_node = pspp_sheet_view_node_prev (tree_view, node);
11006 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11008 gtk_tree_path_free (current_dest);
11013 * pspp_sheet_view_get_drag_dest_row:
11014 * @tree_view: a #PsppSheetView
11015 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11016 * @pos: (allow-none): Return location for the drop position, or %NULL
11018 * Gets information about the row that is highlighted for feedback.
11021 pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view,
11022 GtkTreePath **path,
11023 PsppSheetViewDropPosition *pos)
11025 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11029 if (tree_view->priv->drag_dest_row)
11030 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11033 if (tree_view->priv->empty_view_drop)
11034 *path = gtk_tree_path_new_from_indices (0, -1);
11041 *pos = tree_view->priv->drag_dest_pos;
11045 * pspp_sheet_view_get_dest_row_at_pos:
11046 * @tree_view: a #PsppSheetView
11047 * @drag_x: the position to determine the destination row for
11048 * @drag_y: the position to determine the destination row for
11049 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11050 * @pos: (allow-none): Return location for the drop position, or %NULL
11052 * Determines the destination row for a given position. @drag_x and
11053 * @drag_y are expected to be in widget coordinates. This function is only
11054 * meaningful if @tree_view is realized. Therefore this function will always
11055 * return %FALSE if @tree_view is not realized or does not have a model.
11057 * Return value: whether there is a row at the given position, %TRUE if this
11058 * is indeed the case.
11061 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view,
11064 GtkTreePath **path,
11065 PsppSheetViewDropPosition *pos)
11069 gdouble offset_into_row;
11072 PsppSheetViewColumn *column = NULL;
11073 GtkTreePath *tmp_path = NULL;
11075 /* Note; this function is exported to allow a custom DND
11076 * implementation, so it can't touch TreeViewDragInfo
11079 g_return_val_if_fail (tree_view != NULL, FALSE);
11080 g_return_val_if_fail (drag_x >= 0, FALSE);
11081 g_return_val_if_fail (drag_y >= 0, FALSE);
11086 if (tree_view->priv->bin_window == NULL)
11089 if (tree_view->priv->row_count == 0)
11092 /* If in the top third of a row, we drop before that row; if
11093 * in the bottom third, drop after that row; if in the middle,
11094 * and the row has children, drop into the row.
11096 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11099 if (!pspp_sheet_view_get_path_at_pos (tree_view,
11108 pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11111 offset_into_row = cell_y;
11116 gtk_tree_path_free (tmp_path);
11120 third = cell.height / 3.0;
11124 if (offset_into_row < third)
11126 *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11128 else if (offset_into_row < (cell.height / 2.0))
11130 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11132 else if (offset_into_row < third * 2.0)
11134 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11138 *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11146 #if GTK3_TRANSITION
11147 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11149 * pspp_sheet_view_create_row_drag_icon:
11150 * @tree_view: a #PsppSheetView
11151 * @path: a #GtkTreePath in @tree_view
11153 * Creates a #GdkPixmap representation of the row at @path.
11154 * This image is used for a drag icon.
11156 * Return value: a newly-allocated pixmap of the drag icon.
11159 pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view,
11166 GdkRectangle background_area;
11167 GdkRectangle expose_area;
11169 /* start drawing inside the black outline */
11171 GdkDrawable *drawable;
11172 gint bin_window_width;
11175 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11176 g_return_val_if_fail (path != NULL, NULL);
11178 widget = GTK_WIDGET (tree_view);
11180 if (!gtk_widget_get_realized (widget))
11183 _pspp_sheet_view_find_node (tree_view,
11190 if (!gtk_tree_model_get_iter (tree_view->priv->model,
11197 background_area.y = y;
11198 background_area.height = ROW_HEIGHT (tree_view);
11200 bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
11202 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11203 bin_window_width + 2,
11204 background_area.height + 2,
11209 expose_area.width = bin_window_width + 2;
11210 expose_area.height = background_area.height + 2;
11212 #if GTK3_TRANSITION
11213 gdk_draw_rectangle (drawable,
11214 widget->style->base_gc [gtk_widget_get_state (widget)],
11217 bin_window_width + 2,
11218 background_area.height + 2);
11221 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11223 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11225 list = (rtl ? list->prev : list->next))
11227 PsppSheetViewColumn *column = list->data;
11228 GdkRectangle cell_area;
11229 gint vertical_separator;
11231 if (!column->visible)
11234 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11236 background_area.x = cell_offset;
11237 background_area.width = column->width;
11239 gtk_widget_style_get (widget,
11240 "vertical-separator", &vertical_separator,
11243 cell_area = background_area;
11245 cell_area.y += vertical_separator / 2;
11246 cell_area.height -= vertical_separator;
11248 if (pspp_sheet_view_column_cell_is_visible (column))
11249 _pspp_sheet_view_column_cell_render (column,
11255 cell_offset += column->width;
11258 #if GTK3_TRANSITION
11259 gdk_draw_rectangle (drawable,
11260 widget->style->black_gc,
11263 bin_window_width + 1,
11264 background_area.height + 1);
11272 * pspp_sheet_view_set_destroy_count_func:
11273 * @tree_view: A #PsppSheetView
11274 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11275 * @data: (allow-none): User data to be passed to @func, or %NULL
11276 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11278 * This function should almost never be used. It is meant for private use by
11279 * ATK for determining the number of visible children that are removed when a row is deleted.
11282 pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view,
11283 PsppSheetDestroyCountFunc func,
11285 GDestroyNotify destroy)
11287 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11289 if (tree_view->priv->destroy_count_destroy)
11290 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11292 tree_view->priv->destroy_count_func = func;
11293 tree_view->priv->destroy_count_data = data;
11294 tree_view->priv->destroy_count_destroy = destroy;
11299 * Interactive search
11303 * pspp_sheet_view_set_enable_search:
11304 * @tree_view: A #PsppSheetView
11305 * @enable_search: %TRUE, if the user can search interactively
11307 * If @enable_search is set, then the user can type in text to search through
11308 * the tree interactively (this is sometimes called "typeahead find").
11310 * Note that even if this is %FALSE, the user can still initiate a search
11311 * using the "start-interactive-search" key binding.
11314 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11315 gboolean enable_search)
11317 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11319 enable_search = !!enable_search;
11321 if (tree_view->priv->enable_search != enable_search)
11323 tree_view->priv->enable_search = enable_search;
11324 g_object_notify (G_OBJECT (tree_view), "enable-search");
11329 * pspp_sheet_view_get_enable_search:
11330 * @tree_view: A #PsppSheetView
11332 * Returns whether or not the tree allows to start interactive searching
11333 * by typing in text.
11335 * Return value: whether or not to let the user search interactively
11338 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11340 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11342 return tree_view->priv->enable_search;
11347 * pspp_sheet_view_get_search_column:
11348 * @tree_view: A #PsppSheetView
11350 * Gets the column searched on by the interactive search code.
11352 * Return value: the column the interactive search code searches in.
11355 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11357 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11359 return (tree_view->priv->search_column);
11363 * pspp_sheet_view_set_search_column:
11364 * @tree_view: A #PsppSheetView
11365 * @column: the column of the model to search in, or -1 to disable searching
11367 * Sets @column as the column where the interactive search code should
11368 * search in for the current model.
11370 * If the search column is set, users can use the "start-interactive-search"
11371 * key binding to bring up search popup. The enable-search property controls
11372 * whether simply typing text will also start an interactive search.
11374 * Note that @column refers to a column of the current model. The search
11375 * column is reset to -1 when the model is changed.
11378 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11381 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11382 g_return_if_fail (column >= -1);
11384 if (tree_view->priv->search_column == column)
11387 tree_view->priv->search_column = column;
11388 g_object_notify (G_OBJECT (tree_view), "search-column");
11392 * pspp_sheet_view_get_search_equal_func:
11393 * @tree_view: A #PsppSheetView
11395 * Returns the compare function currently in use.
11397 * Return value: the currently used compare function for the search code.
11400 PsppSheetViewSearchEqualFunc
11401 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11403 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11405 return tree_view->priv->search_equal_func;
11409 * pspp_sheet_view_set_search_equal_func:
11410 * @tree_view: A #PsppSheetView
11411 * @search_equal_func: the compare function to use during the search
11412 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11413 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11415 * Sets the compare function for the interactive search capabilities; note
11416 * that somewhat like strcmp() returning 0 for equality
11417 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11420 pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view,
11421 PsppSheetViewSearchEqualFunc search_equal_func,
11422 gpointer search_user_data,
11423 GDestroyNotify search_destroy)
11425 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11426 g_return_if_fail (search_equal_func != NULL);
11428 if (tree_view->priv->search_destroy)
11429 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11431 tree_view->priv->search_equal_func = search_equal_func;
11432 tree_view->priv->search_user_data = search_user_data;
11433 tree_view->priv->search_destroy = search_destroy;
11434 if (tree_view->priv->search_equal_func == NULL)
11435 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11439 * pspp_sheet_view_get_search_entry:
11440 * @tree_view: A #PsppSheetView
11442 * Returns the #GtkEntry which is currently in use as interactive search
11443 * entry for @tree_view. In case the built-in entry is being used, %NULL
11444 * will be returned.
11446 * Return value: the entry currently in use as search entry.
11451 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11453 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11455 if (tree_view->priv->search_custom_entry_set)
11456 return GTK_ENTRY (tree_view->priv->search_entry);
11462 * pspp_sheet_view_set_search_entry:
11463 * @tree_view: A #PsppSheetView
11464 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11466 * Sets the entry which the interactive search code will use for this
11467 * @tree_view. This is useful when you want to provide a search entry
11468 * in our interface at all time at a fixed position. Passing %NULL for
11469 * @entry will make the interactive search code use the built-in popup
11475 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11478 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11479 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11481 if (tree_view->priv->search_custom_entry_set)
11483 if (tree_view->priv->search_entry_changed_id)
11485 g_signal_handler_disconnect (tree_view->priv->search_entry,
11486 tree_view->priv->search_entry_changed_id);
11487 tree_view->priv->search_entry_changed_id = 0;
11489 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11490 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11493 g_object_unref (tree_view->priv->search_entry);
11495 else if (tree_view->priv->search_window)
11497 gtk_widget_destroy (tree_view->priv->search_window);
11499 tree_view->priv->search_window = NULL;
11504 tree_view->priv->search_entry = g_object_ref (entry);
11505 tree_view->priv->search_custom_entry_set = TRUE;
11507 if (tree_view->priv->search_entry_changed_id == 0)
11509 tree_view->priv->search_entry_changed_id =
11510 g_signal_connect (tree_view->priv->search_entry, "changed",
11511 G_CALLBACK (pspp_sheet_view_search_init),
11515 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11516 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11519 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11523 tree_view->priv->search_entry = NULL;
11524 tree_view->priv->search_custom_entry_set = FALSE;
11529 * pspp_sheet_view_set_search_position_func:
11530 * @tree_view: A #PsppSheetView
11531 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11532 * to use the default search position function
11533 * @data: (allow-none): user data to pass to @func, or %NULL
11534 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11536 * Sets the function to use when positioning the search dialog.
11541 pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view,
11542 PsppSheetViewSearchPositionFunc func,
11543 gpointer user_data,
11544 GDestroyNotify destroy)
11546 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11548 if (tree_view->priv->search_position_destroy)
11549 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11551 tree_view->priv->search_position_func = func;
11552 tree_view->priv->search_position_user_data = user_data;
11553 tree_view->priv->search_position_destroy = destroy;
11554 if (tree_view->priv->search_position_func == NULL)
11555 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11559 * pspp_sheet_view_get_search_position_func:
11560 * @tree_view: A #PsppSheetView
11562 * Returns the positioning function currently in use.
11564 * Return value: the currently used function for positioning the search dialog.
11568 PsppSheetViewSearchPositionFunc
11569 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11571 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11573 return tree_view->priv->search_position_func;
11578 pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
11579 PsppSheetView *tree_view)
11581 if (tree_view->priv->disable_popdown)
11584 if (tree_view->priv->search_entry_changed_id)
11586 g_signal_handler_disconnect (tree_view->priv->search_entry,
11587 tree_view->priv->search_entry_changed_id);
11588 tree_view->priv->search_entry_changed_id = 0;
11590 if (tree_view->priv->typeselect_flush_timeout)
11592 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11593 tree_view->priv->typeselect_flush_timeout = 0;
11596 if (gtk_widget_get_visible (search_dialog))
11598 /* send focus-in event */
11599 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11600 gtk_widget_hide (search_dialog);
11601 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11602 send_focus_change (GTK_WIDGET (tree_view), TRUE);
11607 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11608 GtkWidget *search_dialog,
11609 gpointer user_data)
11612 gint tree_x, tree_y;
11613 gint tree_width, tree_height;
11614 GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
11615 GdkScreen *screen = gdk_window_get_screen (tree_window);
11616 GtkRequisition requisition;
11618 GdkRectangle monitor;
11620 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11621 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11623 gtk_widget_realize (search_dialog);
11625 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11626 tree_width = gdk_window_get_width (tree_window);
11627 tree_height = gdk_window_get_height (tree_window);
11629 gtk_widget_size_request (search_dialog, &requisition);
11631 if (tree_x + tree_width > gdk_screen_get_width (screen))
11632 x = gdk_screen_get_width (screen) - requisition.width;
11633 else if (tree_x + tree_width - requisition.width < 0)
11636 x = tree_x + tree_width - requisition.width;
11638 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11639 y = gdk_screen_get_height (screen) - requisition.height;
11640 else if (tree_y + tree_height < 0) /* isn't really possible ... */
11643 y = tree_y + tree_height;
11645 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11649 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11653 PsppSheetView *tree_view = (PsppSheetView *)data;
11655 tree_view->priv->disable_popdown = 1;
11656 g_signal_connect (menu, "hide",
11657 G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11660 #if GTK3_TRANSITION
11661 /* Because we're visible but offscreen, we just set a flag in the preedit
11665 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11666 PsppSheetView *tree_view)
11668 tree_view->priv->imcontext_changed = 1;
11669 if (tree_view->priv->typeselect_flush_timeout)
11671 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11672 tree_view->priv->typeselect_flush_timeout =
11673 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11674 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11682 pspp_sheet_view_search_activate (GtkEntry *entry,
11683 PsppSheetView *tree_view)
11688 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11691 /* If we have a row selected and it's the cursor row, we activate
11693 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11695 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11697 _pspp_sheet_view_find_node (tree_view, path, &node);
11699 if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11700 pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11702 gtk_tree_path_free (path);
11707 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11709 PsppSheetView *tree_view = (PsppSheetView *)data;
11711 tree_view->priv->disable_popdown = 0;
11717 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11720 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11724 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11725 GdkEventAny *event,
11726 PsppSheetView *tree_view)
11728 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11730 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11736 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11737 GdkEventButton *event,
11738 PsppSheetView *tree_view)
11740 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11742 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11744 if (event->window == tree_view->priv->bin_window)
11745 pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
11751 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
11752 GdkEventScroll *event,
11753 PsppSheetView *tree_view)
11755 gboolean retval = FALSE;
11757 if (event->direction == GDK_SCROLL_UP)
11759 pspp_sheet_view_search_move (widget, tree_view, TRUE);
11762 else if (event->direction == GDK_SCROLL_DOWN)
11764 pspp_sheet_view_search_move (widget, tree_view, FALSE);
11768 /* renew the flush timeout */
11769 if (retval && tree_view->priv->typeselect_flush_timeout
11770 && !tree_view->priv->search_custom_entry_set)
11772 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11773 tree_view->priv->typeselect_flush_timeout =
11774 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11775 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11783 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
11784 GdkEventKey *event,
11785 PsppSheetView *tree_view)
11787 gboolean retval = FALSE;
11789 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11790 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11792 /* close window and cancel the search */
11793 if (!tree_view->priv->search_custom_entry_set
11794 && (event->keyval == GDK_Escape ||
11795 event->keyval == GDK_Tab ||
11796 event->keyval == GDK_KP_Tab ||
11797 event->keyval == GDK_ISO_Left_Tab))
11799 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11803 /* select previous matching iter */
11804 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
11806 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11807 gtk_widget_error_bell (widget);
11812 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
11813 && (event->keyval == GDK_g || event->keyval == GDK_G))
11815 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11816 gtk_widget_error_bell (widget);
11821 /* select next matching iter */
11822 if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
11824 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11825 gtk_widget_error_bell (widget);
11830 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
11831 && (event->keyval == GDK_g || event->keyval == GDK_G))
11833 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11834 gtk_widget_error_bell (widget);
11839 /* renew the flush timeout */
11840 if (retval && tree_view->priv->typeselect_flush_timeout
11841 && !tree_view->priv->search_custom_entry_set)
11843 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11844 tree_view->priv->typeselect_flush_timeout =
11845 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11846 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11853 /* this function returns FALSE if there is a search string but
11854 * nothing was found, and TRUE otherwise.
11857 pspp_sheet_view_search_move (GtkWidget *window,
11858 PsppSheetView *tree_view,
11866 GtkTreeModel *model;
11867 PsppSheetSelection *selection;
11869 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
11871 g_return_val_if_fail (text != NULL, FALSE);
11873 len = strlen (text);
11875 if (up && tree_view->priv->selected_iter == 1)
11876 return strlen (text) < 1;
11878 len = strlen (text);
11883 model = pspp_sheet_view_get_model (tree_view);
11884 selection = pspp_sheet_view_get_selection (tree_view);
11887 pspp_sheet_selection_unselect_all (selection);
11888 if (!gtk_tree_model_get_iter_first (model, &iter))
11891 ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
11892 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
11897 tree_view->priv->selected_iter += up?(-1):(1);
11902 /* return to old iter */
11904 gtk_tree_model_get_iter_first (model, &iter);
11905 pspp_sheet_view_search_iter (model, selection,
11907 &count, tree_view->priv->selected_iter);
11913 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
11917 gpointer search_data)
11919 gboolean retval = TRUE;
11921 gchar *normalized_string;
11922 gchar *normalized_key;
11923 gchar *case_normalized_string = NULL;
11924 gchar *case_normalized_key = NULL;
11925 GValue value = {0,};
11926 GValue transformed = {0,};
11928 gtk_tree_model_get_value (model, iter, column, &value);
11930 g_value_init (&transformed, G_TYPE_STRING);
11932 if (!g_value_transform (&value, &transformed))
11934 g_value_unset (&value);
11938 g_value_unset (&value);
11940 str = g_value_get_string (&transformed);
11943 g_value_unset (&transformed);
11947 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
11948 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
11950 if (normalized_string && normalized_key)
11952 case_normalized_string = g_utf8_casefold (normalized_string, -1);
11953 case_normalized_key = g_utf8_casefold (normalized_key, -1);
11955 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
11959 g_value_unset (&transformed);
11960 g_free (normalized_key);
11961 g_free (normalized_string);
11962 g_free (case_normalized_key);
11963 g_free (case_normalized_string);
11969 pspp_sheet_view_search_iter (GtkTreeModel *model,
11970 PsppSheetSelection *selection,
11979 PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
11981 path = gtk_tree_model_get_path (model, iter);
11982 _pspp_sheet_view_find_node (tree_view, path, &node);
11986 gboolean done = FALSE;
11988 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
11993 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
11995 pspp_sheet_selection_select_iter (selection, iter);
11996 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
11999 gtk_tree_path_free (path);
12008 node = pspp_sheet_view_node_next (tree_view, node);
12014 has_next = gtk_tree_model_iter_next (model, iter);
12017 gtk_tree_path_next (path);
12020 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12025 gtk_tree_path_free (path);
12027 /* we've run out of tree, done with this func */
12039 pspp_sheet_view_search_init (GtkWidget *entry,
12040 PsppSheetView *tree_view)
12046 GtkTreeModel *model;
12047 PsppSheetSelection *selection;
12049 g_return_if_fail (GTK_IS_ENTRY (entry));
12050 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12052 text = gtk_entry_get_text (GTK_ENTRY (entry));
12054 model = pspp_sheet_view_get_model (tree_view);
12055 selection = pspp_sheet_view_get_selection (tree_view);
12058 pspp_sheet_selection_unselect_all (selection);
12059 if (tree_view->priv->typeselect_flush_timeout
12060 && !tree_view->priv->search_custom_entry_set)
12062 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12063 tree_view->priv->typeselect_flush_timeout =
12064 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12065 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12072 if (!gtk_tree_model_get_iter_first (model, &iter))
12075 ret = pspp_sheet_view_search_iter (model, selection,
12080 tree_view->priv->selected_iter = 1;
12084 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12085 PsppSheetView *tree_view)
12087 if (tree_view->priv->edited_column == NULL)
12090 _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12091 tree_view->priv->edited_column = NULL;
12093 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12094 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12096 g_signal_handlers_disconnect_by_func (cell_editable,
12097 pspp_sheet_view_remove_widget,
12099 g_signal_handlers_disconnect_by_func (cell_editable,
12100 pspp_sheet_view_editable_button_press_event,
12102 g_signal_handlers_disconnect_by_func (cell_editable,
12103 pspp_sheet_view_editable_clicked,
12106 gtk_container_remove (GTK_CONTAINER (tree_view),
12107 GTK_WIDGET (cell_editable));
12109 /* FIXME should only redraw a single node */
12110 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12114 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12115 GtkTreePath *cursor_path)
12118 GdkRectangle background_area;
12119 GdkRectangle cell_area;
12120 GtkCellEditable *editable_widget = NULL;
12121 gchar *path_string;
12122 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12123 gint retval = FALSE;
12126 g_assert (tree_view->priv->focus_column);
12128 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12131 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12132 if (cursor_node < 0)
12135 path_string = gtk_tree_path_to_string (cursor_path);
12136 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12138 pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12139 tree_view->priv->model,
12141 pspp_sheet_view_get_background_area (tree_view,
12143 tree_view->priv->focus_column,
12145 pspp_sheet_view_get_cell_area (tree_view,
12147 tree_view->priv->focus_column,
12150 if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12159 if (editable_widget != NULL)
12163 GtkCellRenderer *cell;
12166 cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12168 _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12171 area.width -= right + left;
12173 pspp_sheet_view_real_start_editing (tree_view,
12174 tree_view->priv->focus_column,
12183 g_free (path_string);
12188 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12189 GdkEventButton *event,
12190 PsppSheetView *sheet_view)
12194 node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12195 "pspp-sheet-view-node"));
12196 return pspp_sheet_view_row_head_clicked (sheet_view,
12198 sheet_view->priv->edited_column,
12203 pspp_sheet_view_editable_clicked (GtkButton *button,
12204 PsppSheetView *sheet_view)
12206 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12211 is_all_selected (GtkWidget *widget)
12213 GtkEntryBuffer *buffer;
12214 gint start_pos, end_pos;
12216 if (!GTK_IS_ENTRY (widget))
12219 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12220 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12221 &start_pos, &end_pos)
12223 && end_pos == gtk_entry_buffer_get_length (buffer));
12227 is_at_left (GtkWidget *widget)
12229 return (GTK_IS_ENTRY (widget)
12230 && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12234 is_at_right (GtkWidget *widget)
12236 GtkEntryBuffer *buffer;
12239 if (!GTK_IS_ENTRY (widget))
12242 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12243 length = gtk_entry_buffer_get_length (buffer);
12244 return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12248 pspp_sheet_view_event (GtkWidget *widget,
12249 GdkEventKey *event,
12250 PsppSheetView *tree_view)
12252 PsppSheetViewColumn *column;
12259 /* Intercept only key press events.
12260 It would make sense to use "key-press-event" instead of "event", but
12261 GtkEntry attaches its own signal handler to "key-press-event" that runs
12262 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12264 if (event->type != GDK_KEY_PRESS)
12267 keyval = event->keyval;
12269 switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12272 switch (event->keyval)
12274 case GDK_Left: case GDK_KP_Left:
12275 case GDK_Home: case GDK_KP_Home:
12276 if (!is_all_selected (widget) && !is_at_left (widget))
12280 case GDK_Right: case GDK_KP_Right:
12281 case GDK_End: case GDK_KP_End:
12282 if (!is_all_selected (widget) && !is_at_right (widget))
12286 case GDK_Up: case GDK_KP_Up:
12287 case GDK_Down: case GDK_KP_Down:
12290 case GDK_Page_Up: case GDK_KP_Page_Up:
12291 case GDK_Page_Down: case GDK_KP_Page_Down:
12302 case GDK_Tab: case GDK_KP_Tab:
12303 case GDK_ISO_Left_Tab:
12312 case GDK_SHIFT_MASK:
12313 switch (event->keyval)
12316 case GDK_ISO_Left_Tab:
12325 case GDK_CONTROL_MASK:
12326 switch (event->keyval)
12328 case GDK_Left: case GDK_KP_Left:
12329 if (!is_all_selected (widget) && !is_at_left (widget))
12333 case GDK_Right: case GDK_KP_Right:
12334 if (!is_all_selected (widget) && !is_at_right (widget))
12338 case GDK_Up: case GDK_KP_Up:
12339 case GDK_Down: case GDK_KP_Down:
12351 row = tree_view->priv->edited_row;
12352 column = tree_view->priv->edited_column;
12353 path = gtk_tree_path_new_from_indices (row, -1);
12355 pspp_sheet_view_stop_editing (tree_view, cancel);
12356 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12358 pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12359 gtk_tree_path_free (path);
12361 handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12362 G_OBJECT (tree_view));
12364 g_signal_stop_emission_by_name (widget, "event");
12366 pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12367 pspp_sheet_view_start_editing (tree_view, path);
12368 gtk_tree_path_free (path);
12374 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12377 PsppSheetView *sheet_view = data;
12379 g_signal_connect (widget, "event",
12380 G_CALLBACK (pspp_sheet_view_event),
12383 if (GTK_IS_CONTAINER (widget))
12384 gtk_container_foreach (GTK_CONTAINER (widget),
12385 pspp_sheet_view_override_cell_keypresses,
12390 pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
12391 PsppSheetViewColumn *column,
12393 GtkCellEditable *cell_editable,
12394 GdkRectangle *cell_area,
12398 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12399 gint pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
12402 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12404 tree_view->priv->edited_column = column;
12405 _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12407 row = gtk_tree_path_get_indices (path)[0];
12408 tree_view->priv->edited_row = row;
12409 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12410 cell_area->y += pre_val - (int)gtk_adjustment_get_value (tree_view->priv->vadjustment);
12412 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12413 pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12414 tree_view->priv->anchor_column = column;
12416 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12418 pspp_sheet_view_put (tree_view,
12419 GTK_WIDGET (cell_editable),
12423 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12424 (GdkEvent *)event);
12426 gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12427 g_signal_connect (cell_editable, "remove-widget",
12428 G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12429 if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12430 GTK_IS_BUTTON (cell_editable))
12432 g_signal_connect (cell_editable, "button-press-event",
12433 G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12435 g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12436 GINT_TO_POINTER (row));
12437 g_signal_connect (cell_editable, "clicked",
12438 G_CALLBACK (pspp_sheet_view_editable_clicked),
12442 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12447 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12448 gboolean cancel_editing)
12450 PsppSheetViewColumn *column;
12451 GtkCellRenderer *cell;
12453 if (tree_view->priv->edited_column == NULL)
12457 * This is very evil. We need to do this, because
12458 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12459 * later on. If pspp_sheet_view_row_changed notices
12460 * tree_view->priv->edited_column != NULL, it'll call
12461 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12463 * Please read that again if you intend to modify anything here.
12466 column = tree_view->priv->edited_column;
12467 tree_view->priv->edited_column = NULL;
12469 cell = _pspp_sheet_view_column_get_edited_cell (column);
12470 gtk_cell_renderer_stop_editing (cell, cancel_editing);
12472 if (!cancel_editing)
12473 gtk_cell_editable_editing_done (column->editable_widget);
12475 tree_view->priv->edited_column = column;
12477 gtk_cell_editable_remove_widget (column->editable_widget);
12482 * pspp_sheet_view_set_hover_selection:
12483 * @tree_view: a #PsppSheetView
12484 * @hover: %TRUE to enable hover selection mode
12486 * Enables of disables the hover selection mode of @tree_view.
12487 * Hover selection makes the selected row follow the pointer.
12488 * Currently, this works only for the selection modes
12489 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12494 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12497 hover = hover != FALSE;
12499 if (hover != tree_view->priv->hover_selection)
12501 tree_view->priv->hover_selection = hover;
12503 g_object_notify (G_OBJECT (tree_view), "hover-selection");
12508 * pspp_sheet_view_get_hover_selection:
12509 * @tree_view: a #PsppSheetView
12511 * Returns whether hover selection mode is turned on for @tree_view.
12513 * Return value: %TRUE if @tree_view is in hover selection mode
12518 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12520 return tree_view->priv->hover_selection;
12524 * pspp_sheet_view_set_rubber_banding:
12525 * @tree_view: a #PsppSheetView
12526 * @enable: %TRUE to enable rubber banding
12528 * Enables or disables rubber banding in @tree_view. If the selection mode is
12529 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12530 * banding will allow the user to select multiple rows by dragging the mouse.
12535 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12538 enable = enable != FALSE;
12540 if (enable != tree_view->priv->rubber_banding_enable)
12542 tree_view->priv->rubber_banding_enable = enable;
12544 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12549 * pspp_sheet_view_get_rubber_banding:
12550 * @tree_view: a #PsppSheetView
12552 * Returns whether rubber banding is turned on for @tree_view. If the
12553 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12554 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12555 * select multiple rows by dragging the mouse.
12557 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12562 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12564 return tree_view->priv->rubber_banding_enable;
12568 * pspp_sheet_view_is_rubber_banding_active:
12569 * @tree_view: a #PsppSheetView
12571 * Returns whether a rubber banding operation is currently being done
12574 * Return value: %TRUE if a rubber banding operation is currently being
12575 * done in @tree_view.
12580 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12582 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12584 if (tree_view->priv->rubber_banding_enable
12585 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12592 pspp_sheet_view_grab_notify (GtkWidget *widget,
12593 gboolean was_grabbed)
12595 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12597 tree_view->priv->in_grab = !was_grabbed;
12601 tree_view->priv->pressed_button = -1;
12603 if (tree_view->priv->rubber_band_status)
12604 pspp_sheet_view_stop_rubber_band (tree_view);
12609 pspp_sheet_view_state_changed (GtkWidget *widget,
12610 GtkStateType previous_state)
12612 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12614 if (gtk_widget_get_realized (widget))
12616 GtkStyle *style = gtk_widget_get_style (widget);
12617 gdk_window_set_background (tree_view->priv->bin_window, &style->base[gtk_widget_get_state (widget)]);
12620 gtk_widget_queue_draw (widget);
12624 * pspp_sheet_view_get_grid_lines:
12625 * @tree_view: a #PsppSheetView
12627 * Returns which grid lines are enabled in @tree_view.
12629 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12634 PsppSheetViewGridLines
12635 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12637 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12639 return tree_view->priv->grid_lines;
12643 * pspp_sheet_view_set_grid_lines:
12644 * @tree_view: a #PsppSheetView
12645 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12648 * Sets which grid lines to draw in @tree_view.
12653 pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view,
12654 PsppSheetViewGridLines grid_lines)
12656 PsppSheetViewPrivate *priv;
12657 PsppSheetViewGridLines old_grid_lines;
12659 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12661 priv = tree_view->priv;
12663 old_grid_lines = priv->grid_lines;
12664 priv->grid_lines = grid_lines;
12666 if (old_grid_lines != grid_lines)
12668 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12670 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12675 * pspp_sheet_view_get_special_cells:
12676 * @tree_view: a #PsppSheetView
12678 * Returns which grid lines are enabled in @tree_view.
12680 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12681 * the sheet view contain special cells.
12683 PsppSheetViewSpecialCells
12684 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12686 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12688 return tree_view->priv->special_cells;
12692 * pspp_sheet_view_set_special_cells:
12693 * @tree_view: a #PsppSheetView
12694 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12695 * the sheet view contain special cells.
12697 * Sets whether rows in the sheet view contain special cells, controlling the
12698 * rendering of row selections.
12701 pspp_sheet_view_set_special_cells (PsppSheetView *tree_view,
12702 PsppSheetViewSpecialCells special_cells)
12704 PsppSheetViewPrivate *priv;
12706 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12708 priv = tree_view->priv;
12710 if (priv->special_cells != special_cells)
12712 priv->special_cells = special_cells;
12713 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12714 g_object_notify (G_OBJECT (tree_view), "special-cells");
12719 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12721 /* XXX (re)calculate fixed_height if necessary */
12722 return tree_view->priv->fixed_height;
12726 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
12729 g_return_if_fail (fixed_height > 0);
12731 if (tree_view->priv->fixed_height != fixed_height)
12733 tree_view->priv->fixed_height = fixed_height;
12734 g_object_notify (G_OBJECT (tree_view), "fixed-height");
12736 if (!tree_view->priv->fixed_height_set)
12738 tree_view->priv->fixed_height_set = TRUE;
12739 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
12744 * pspp_sheet_view_set_tooltip_row:
12745 * @tree_view: a #PsppSheetView
12746 * @tooltip: a #GtkTooltip
12747 * @path: a #GtkTreePath
12749 * Sets the tip area of @tooltip to be the area covered by the row at @path.
12750 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12751 * See also gtk_tooltip_set_tip_area().
12756 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
12757 GtkTooltip *tooltip,
12760 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12761 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12763 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
12767 * pspp_sheet_view_set_tooltip_cell:
12768 * @tree_view: a #PsppSheetView
12769 * @tooltip: a #GtkTooltip
12770 * @path: (allow-none): a #GtkTreePath or %NULL
12771 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
12772 * @cell: (allow-none): a #GtkCellRenderer or %NULL
12774 * Sets the tip area of @tooltip to the area @path, @column and @cell have
12775 * in common. For example if @path is %NULL and @column is set, the tip
12776 * area will be set to the full area covered by @column. See also
12777 * gtk_tooltip_set_tip_area().
12779 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12784 pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view,
12785 GtkTooltip *tooltip,
12787 PsppSheetViewColumn *column,
12788 GtkCellRenderer *cell)
12792 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12793 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12794 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
12795 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
12797 /* Determine x values. */
12798 if (column && cell)
12803 pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
12804 pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
12806 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12809 rect.width = width;
12815 pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
12816 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12819 rect.width = tmp.width;
12823 GtkAllocation allocation;
12824 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
12826 rect.width = allocation.width;
12829 /* Determine y values. */
12834 pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
12835 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12838 rect.height = tmp.height;
12843 rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
12846 gtk_tooltip_set_tip_area (tooltip, &rect);
12850 * pspp_sheet_view_get_tooltip_context:
12851 * @tree_view: a #PsppSheetView
12852 * @x: the x coordinate (relative to widget coordinates)
12853 * @y: the y coordinate (relative to widget coordinates)
12854 * @keyboard_tip: whether this is a keyboard tooltip or not
12855 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
12856 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
12857 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
12859 * This function is supposed to be used in a #GtkWidget::query-tooltip
12860 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
12861 * which are received in the signal handler, should be passed to this
12862 * function without modification.
12864 * The return value indicates whether there is a tree view row at the given
12865 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
12866 * tooltips the row returned will be the cursor row. When %TRUE, then any of
12867 * @model, @path and @iter which have been provided will be set to point to
12868 * that row and the corresponding model. @x and @y will always be converted
12869 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
12871 * Return value: whether or not the given tooltip context points to a row.
12876 pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view,
12879 gboolean keyboard_tip,
12880 GtkTreeModel **model,
12881 GtkTreePath **path,
12884 GtkTreePath *tmppath = NULL;
12886 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12887 g_return_val_if_fail (x != NULL, FALSE);
12888 g_return_val_if_fail (y != NULL, FALSE);
12892 pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
12899 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
12902 if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
12903 &tmppath, NULL, NULL, NULL))
12908 *model = pspp_sheet_view_get_model (tree_view);
12911 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
12917 gtk_tree_path_free (tmppath);
12923 pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget,
12926 gboolean keyboard_tip,
12927 GtkTooltip *tooltip,
12930 GValue value = { 0, };
12931 GValue transformed = { 0, };
12934 GtkTreeModel *model;
12935 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12937 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
12940 &model, &path, &iter))
12943 gtk_tree_model_get_value (model, &iter,
12944 tree_view->priv->tooltip_column, &value);
12946 g_value_init (&transformed, G_TYPE_STRING);
12948 if (!g_value_transform (&value, &transformed))
12950 g_value_unset (&value);
12951 gtk_tree_path_free (path);
12956 g_value_unset (&value);
12958 if (!g_value_get_string (&transformed))
12960 g_value_unset (&transformed);
12961 gtk_tree_path_free (path);
12966 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
12967 pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
12969 gtk_tree_path_free (path);
12970 g_value_unset (&transformed);
12976 * pspp_sheet_view_set_tooltip_column:
12977 * @tree_view: a #PsppSheetView
12978 * @column: an integer, which is a valid column number for @tree_view's model
12980 * If you only plan to have simple (text-only) tooltips on full rows, you
12981 * can use this function to have #PsppSheetView handle these automatically
12982 * for you. @column should be set to the column in @tree_view's model
12983 * containing the tooltip texts, or -1 to disable this feature.
12985 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
12986 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
12988 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
12989 * so &, <, etc have to be escaped in the text.
12994 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
12997 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12999 if (column == tree_view->priv->tooltip_column)
13004 g_signal_handlers_disconnect_by_func (tree_view,
13005 pspp_sheet_view_set_tooltip_query_cb,
13007 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13011 if (tree_view->priv->tooltip_column == -1)
13013 g_signal_connect (tree_view, "query-tooltip",
13014 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13015 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13019 tree_view->priv->tooltip_column = column;
13020 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13024 * pspp_sheet_view_get_tooltip_column:
13025 * @tree_view: a #PsppSheetView
13027 * Returns the column of @tree_view's model which is being used for
13028 * displaying tooltips on @tree_view's rows.
13030 * Return value: the index of the tooltip column that is currently being
13031 * used, or -1 if this is disabled.
13036 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13038 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13040 return tree_view->priv->tooltip_column;
13044 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13045 GValue *return_accu,
13046 const GValue *handler_return,
13049 gboolean continue_emission;
13050 gboolean signal_handled;
13052 signal_handled = g_value_get_boolean (handler_return);
13053 g_value_set_boolean (return_accu, signal_handled);
13054 continue_emission = !signal_handled;
13056 return continue_emission;
13061 pspp_sheet_view_grid_lines_get_type (void)
13063 static GType etype = 0;
13064 if (G_UNLIKELY(etype == 0)) {
13065 static const GEnumValue values[] = {
13066 { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13067 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13068 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13069 { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13072 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13078 pspp_sheet_view_special_cells_get_type (void)
13080 static GType etype = 0;
13081 if (G_UNLIKELY(etype == 0)) {
13082 static const GEnumValue values[] = {
13083 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13084 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13085 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13088 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);