1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012, 2013, 2015 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Library General Public
22 * License as published by the Free Software Foundation; either
23 * version 2 of the License, or (at your option) any later version.
25 * This library is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Library General Public License for more details.
30 * You should have received a copy of the GNU Library General Public
31 * License along with this library; if not, write to the
32 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
33 * Boston, MA 02111-1307, USA.
38 #include "ui/gui/pspp-sheet-private.h"
42 #include <gdk/gdkkeysyms.h>
43 #include <gdk/gdkkeysyms-compat.h>
46 #include "ui/gui/psppire-marshal.h"
47 #include "ui/gui/pspp-sheet-selection.h"
49 #define P_(STRING) STRING
50 #define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
51 #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
53 /* Many keyboard shortcuts for Mac are the same as for X
54 * except they use Command key instead of Control (e.g. Cut,
55 * Copy, Paste). This symbol is for those simple cases. */
56 #ifndef GDK_WINDOWING_QUARTZ
57 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_CONTROL_MASK
59 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_META_MASK
62 #define PSPP_SHEET_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
63 #define PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC (PSPP_SHEET_VIEW_PRIORITY_VALIDATE + 2)
64 #define PSPP_SHEET_VIEW_TIME_MS_PER_IDLE 30
65 #define SCROLL_EDGE_SIZE 15
66 #define EXPANDER_EXTRA_PADDING 4
67 #define PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT 5000
69 /* The "background" areas of all rows/cells add up to cover the entire tree.
70 * The background includes all inter-row and inter-cell spacing.
71 * The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
72 * i.e. just the cells, no spacing.
75 #define BACKGROUND_HEIGHT(tree_view) (tree_view->priv->fixed_height)
76 #define CELL_HEIGHT(tree_view, separator) ((BACKGROUND_HEIGHT (tree_view)) - (separator))
78 /* Translate from bin_window coordinates to rbtree (tree coordinates) and
81 #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->priv->dy)
82 #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->priv->dy)
84 /* This is in bin_window coordinates */
85 #define BACKGROUND_FIRST_PIXEL(tree_view,node) (RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, pspp_sheet_view_node_find_offset (tree_view, (node))))
86 #define CELL_FIRST_PIXEL(tree_view,node,separator) (BACKGROUND_FIRST_PIXEL (tree_view,node) + separator/2)
88 #define ROW_HEIGHT(tree_view) \
89 ((tree_view->priv->fixed_height > 0) ? (tree_view->priv->fixed_height) : (tree_view)->priv->expander_size)
92 typedef struct _PsppSheetViewChild PsppSheetViewChild;
93 struct _PsppSheetViewChild
96 PsppSheetViewColumn *column;
101 typedef struct _TreeViewDragInfo TreeViewDragInfo;
102 struct _TreeViewDragInfo
104 GdkModifierType start_button_mask;
105 GtkTargetList *_unused_source_target_list;
106 GdkDragAction source_actions;
108 GtkTargetList *_unused_dest_target_list;
110 guint source_set : 1;
126 START_INTERACTIVE_SEARCH,
138 PROP_HEADERS_VISIBLE,
139 PROP_HEADERS_CLICKABLE,
144 PROP_HOVER_SELECTION,
146 PROP_ENABLE_GRID_LINES,
150 PROP_FIXED_HEIGHT_SET
154 static void pspp_sheet_view_finalize (GObject *object);
155 static void pspp_sheet_view_set_property (GObject *object,
159 static void pspp_sheet_view_get_property (GObject *object,
164 static void pspp_sheet_view_dispose (GObject *object);
166 /* gtkwidget signals */
167 static void pspp_sheet_view_realize (GtkWidget *widget);
168 static void pspp_sheet_view_unrealize (GtkWidget *widget);
169 static void pspp_sheet_view_map (GtkWidget *widget);
170 static void pspp_sheet_view_size_request (GtkWidget *widget,
171 GtkRequisition *requisition);
172 static void pspp_sheet_view_size_allocate (GtkWidget *widget,
173 GtkAllocation *allocation);
174 static gboolean pspp_sheet_view_draw (GtkWidget *widget,
176 static gboolean pspp_sheet_view_key_press (GtkWidget *widget,
178 static gboolean pspp_sheet_view_key_release (GtkWidget *widget,
180 static gboolean pspp_sheet_view_motion (GtkWidget *widget,
181 GdkEventMotion *event);
182 static gboolean pspp_sheet_view_enter_notify (GtkWidget *widget,
183 GdkEventCrossing *event);
184 static gboolean pspp_sheet_view_leave_notify (GtkWidget *widget,
185 GdkEventCrossing *event);
186 static gboolean pspp_sheet_view_button_press (GtkWidget *widget,
187 GdkEventButton *event);
188 static gboolean pspp_sheet_view_button_release (GtkWidget *widget,
189 GdkEventButton *event);
190 static gboolean pspp_sheet_view_grab_broken (GtkWidget *widget,
191 GdkEventGrabBroken *event);
193 static void pspp_sheet_view_set_focus_child (GtkContainer *container,
195 static gint pspp_sheet_view_focus_out (GtkWidget *widget,
196 GdkEventFocus *event);
197 static gint pspp_sheet_view_focus (GtkWidget *widget,
198 GtkDirectionType direction);
199 static void pspp_sheet_view_grab_focus (GtkWidget *widget);
200 static void pspp_sheet_view_style_updated (GtkWidget *widget);
201 static void pspp_sheet_view_grab_notify (GtkWidget *widget,
202 gboolean was_grabbed);
203 static void pspp_sheet_view_state_changed (GtkWidget *widget,
204 GtkStateType previous_state);
206 /* container signals */
207 static void pspp_sheet_view_remove (GtkContainer *container,
209 static void pspp_sheet_view_forall (GtkContainer *container,
210 gboolean include_internals,
211 GtkCallback callback,
212 gpointer callback_data);
214 /* Source side drag signals */
215 static void pspp_sheet_view_drag_begin (GtkWidget *widget,
216 GdkDragContext *context);
217 static void pspp_sheet_view_drag_end (GtkWidget *widget,
218 GdkDragContext *context);
219 static void pspp_sheet_view_drag_data_get (GtkWidget *widget,
220 GdkDragContext *context,
221 GtkSelectionData *selection_data,
224 static void pspp_sheet_view_drag_data_delete (GtkWidget *widget,
225 GdkDragContext *context);
227 /* Target side drag signals */
228 static void pspp_sheet_view_drag_leave (GtkWidget *widget,
229 GdkDragContext *context,
231 static gboolean pspp_sheet_view_drag_motion (GtkWidget *widget,
232 GdkDragContext *context,
236 static gboolean pspp_sheet_view_drag_drop (GtkWidget *widget,
237 GdkDragContext *context,
241 static void pspp_sheet_view_drag_data_received (GtkWidget *widget,
242 GdkDragContext *context,
245 GtkSelectionData *selection_data,
249 /* tree_model signals */
250 static void pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
252 GtkAdjustment *vadj);
253 static gboolean pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
254 GtkMovementStep step,
256 static gboolean pspp_sheet_view_real_select_all (PsppSheetView *tree_view);
257 static gboolean pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view);
258 static gboolean pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
259 gboolean start_editing,
260 PsppSheetSelectMode mode);
261 static gboolean pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view);
262 static void pspp_sheet_view_row_changed (GtkTreeModel *model,
266 static void pspp_sheet_view_row_inserted (GtkTreeModel *model,
270 static void pspp_sheet_view_row_deleted (GtkTreeModel *model,
273 static void pspp_sheet_view_rows_reordered (GtkTreeModel *model,
279 /* Incremental reflow */
280 static gint validate_row (PsppSheetView *tree_view,
284 static void validate_visible_area (PsppSheetView *tree_view);
285 static gboolean validate_rows_handler (PsppSheetView *tree_view);
286 static gboolean presize_handler_callback (gpointer data);
287 static void install_presize_handler (PsppSheetView *tree_view);
288 static void install_scroll_sync_handler (PsppSheetView *tree_view);
289 static void pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
292 static void pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view);
293 static void pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view);
294 static void invalidate_empty_focus (PsppSheetView *tree_view);
296 /* Internal functions */
297 static GtkAdjustment *pspp_sheet_view_do_get_hadjustment (PsppSheetView *);
298 static GtkAdjustment *pspp_sheet_view_do_get_vadjustment (PsppSheetView *);
299 static void pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
300 GtkAdjustment *adjustment);
301 static void pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
302 GtkAdjustment *adjustment);
303 static void pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
306 gboolean add_shifted_binding,
307 GtkMovementStep step,
309 static void pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
311 const GdkRectangle *clip_rect);
312 static gint pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
315 static void pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
316 PsppSheetView *tree_view);
317 static void pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
319 static void pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
320 PsppSheetViewColumn *column,
321 gboolean focus_to_cell);
322 static gboolean pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
323 GdkEventMotion *event);
324 static void pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view);
325 static gboolean pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
327 PsppSheetSelectMode mode);
328 static void pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
330 PsppSheetSelectMode mode);
331 static void pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
333 PsppSheetSelectMode mode);
334 static void pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
336 PsppSheetSelectMode mode);
337 static void pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
339 static void pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
341 PsppSheetSelectMode mode);
342 static void pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
344 gboolean clear_and_select,
346 PsppSheetSelectMode mode);
347 static gboolean pspp_sheet_view_has_special_cell (PsppSheetView *tree_view);
348 static void pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view);
349 static void update_prelight (PsppSheetView *tree_view,
352 static void initialize_fixed_height_mode (PsppSheetView *tree_view);
354 /* interactive search */
355 static void pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view);
356 static void pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
357 PsppSheetView *tree_view);
358 static void pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
359 GtkWidget *search_dialog,
361 static void pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
365 static void pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
366 PsppSheetView *tree_view);
368 static void pspp_sheet_view_search_activate (GtkEntry *entry,
369 PsppSheetView *tree_view);
370 static gboolean pspp_sheet_view_real_search_enable_popdown(gpointer data);
371 static void pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
373 static gboolean pspp_sheet_view_search_delete_event (GtkWidget *widget,
375 PsppSheetView *tree_view);
376 static gboolean pspp_sheet_view_search_button_press_event (GtkWidget *widget,
377 GdkEventButton *event,
378 PsppSheetView *tree_view);
379 static gboolean pspp_sheet_view_search_scroll_event (GtkWidget *entry,
380 GdkEventScroll *event,
381 PsppSheetView *tree_view);
382 static gboolean pspp_sheet_view_search_key_press_event (GtkWidget *entry,
384 PsppSheetView *tree_view);
385 static gboolean pspp_sheet_view_search_move (GtkWidget *window,
386 PsppSheetView *tree_view,
388 static gboolean pspp_sheet_view_search_equal_func (GtkTreeModel *model,
392 gpointer search_data);
393 static gboolean pspp_sheet_view_search_iter (GtkTreeModel *model,
394 PsppSheetSelection *selection,
399 static void pspp_sheet_view_search_init (GtkWidget *entry,
400 PsppSheetView *tree_view);
401 static void pspp_sheet_view_put (PsppSheetView *tree_view,
402 GtkWidget *child_widget,
404 PsppSheetViewColumn *column);
405 static gboolean pspp_sheet_view_start_editing (PsppSheetView *tree_view,
406 GtkTreePath *cursor_path);
407 static gboolean pspp_sheet_view_editable_button_press_event (GtkWidget *,
410 static void pspp_sheet_view_editable_clicked (GtkButton *, PsppSheetView *);
411 static void pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
412 PsppSheetViewColumn *column,
414 GtkCellEditable *cell_editable,
415 GdkRectangle *cell_area,
418 static gboolean pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
419 gboolean keybinding);
420 static gboolean pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view);
421 static PsppSheetViewColumn *pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
422 PsppSheetViewColumn *column,
425 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
426 PsppSheetViewColumn *column,
427 const GdkRectangle *background_area,
428 gboolean subtract_focus_rect,
429 GdkRectangle *cell_area);
430 static gint pspp_sheet_view_find_offset (PsppSheetView *tree_view,
435 static void pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
439 static void pspp_sheet_view_buildable_init (GtkBuildableIface *iface);
442 static gboolean scroll_row_timeout (gpointer data);
443 static void add_scroll_timeout (PsppSheetView *tree_view);
444 static void remove_scroll_timeout (PsppSheetView *tree_view);
446 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
448 static GtkBindingSet *edit_bindings;
455 G_DEFINE_TYPE_WITH_CODE (PsppSheetView, pspp_sheet_view, GTK_TYPE_CONTAINER,
456 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
457 pspp_sheet_view_buildable_init)
458 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
461 pspp_sheet_view_get_preferred_width (GtkWidget *widget,
465 GtkRequisition requisition;
467 pspp_sheet_view_size_request (widget, &requisition);
469 *minimal_width = *natural_width = requisition.width;
473 pspp_sheet_view_get_preferred_height (GtkWidget *widget,
474 gint *minimal_height,
475 gint *natural_height)
477 GtkRequisition requisition;
479 pspp_sheet_view_size_request (widget, &requisition);
481 *minimal_height = *natural_height = requisition.height;
485 pspp_sheet_view_class_init (PsppSheetViewClass *class)
487 GObjectClass *o_class;
488 GtkWidgetClass *widget_class;
489 GtkContainerClass *container_class;
490 GtkBindingSet *binding_set[2];
493 binding_set[0] = gtk_binding_set_by_class (class);
495 binding_set[1] = gtk_binding_set_new ("PsppSheetViewEditing");
496 edit_bindings = binding_set[1];
498 o_class = (GObjectClass *) class;
499 widget_class = (GtkWidgetClass *) class;
500 container_class = (GtkContainerClass *) class;
502 /* GObject signals */
503 o_class->set_property = pspp_sheet_view_set_property;
504 o_class->get_property = pspp_sheet_view_get_property;
505 o_class->finalize = pspp_sheet_view_finalize;
506 o_class->dispose = pspp_sheet_view_dispose;
508 /* GtkWidget signals */
509 widget_class->map = pspp_sheet_view_map;
510 widget_class->realize = pspp_sheet_view_realize;
511 widget_class->unrealize = pspp_sheet_view_unrealize;
512 widget_class->get_preferred_width = pspp_sheet_view_get_preferred_width;
513 widget_class->get_preferred_height = pspp_sheet_view_get_preferred_height;
514 widget_class->size_allocate = pspp_sheet_view_size_allocate;
515 widget_class->button_press_event = pspp_sheet_view_button_press;
516 widget_class->button_release_event = pspp_sheet_view_button_release;
517 widget_class->grab_broken_event = pspp_sheet_view_grab_broken;
518 /*widget_class->configure_event = pspp_sheet_view_configure;*/
519 widget_class->motion_notify_event = pspp_sheet_view_motion;
520 widget_class->draw = pspp_sheet_view_draw;
521 widget_class->key_press_event = pspp_sheet_view_key_press;
522 widget_class->key_release_event = pspp_sheet_view_key_release;
523 widget_class->enter_notify_event = pspp_sheet_view_enter_notify;
524 widget_class->leave_notify_event = pspp_sheet_view_leave_notify;
525 widget_class->focus_out_event = pspp_sheet_view_focus_out;
526 widget_class->drag_begin = pspp_sheet_view_drag_begin;
527 widget_class->drag_end = pspp_sheet_view_drag_end;
528 widget_class->drag_data_get = pspp_sheet_view_drag_data_get;
529 widget_class->drag_data_delete = pspp_sheet_view_drag_data_delete;
530 widget_class->drag_leave = pspp_sheet_view_drag_leave;
531 widget_class->drag_motion = pspp_sheet_view_drag_motion;
532 widget_class->drag_drop = pspp_sheet_view_drag_drop;
533 widget_class->drag_data_received = pspp_sheet_view_drag_data_received;
534 widget_class->focus = pspp_sheet_view_focus;
535 widget_class->grab_focus = pspp_sheet_view_grab_focus;
536 widget_class->style_updated = pspp_sheet_view_style_updated;
537 widget_class->grab_notify = pspp_sheet_view_grab_notify;
538 widget_class->state_changed = pspp_sheet_view_state_changed;
540 /* GtkContainer signals */
541 container_class->remove = pspp_sheet_view_remove;
542 container_class->forall = pspp_sheet_view_forall;
543 container_class->set_focus_child = pspp_sheet_view_set_focus_child;
545 class->set_scroll_adjustments = pspp_sheet_view_set_adjustments;
546 class->move_cursor = pspp_sheet_view_real_move_cursor;
547 class->select_all = pspp_sheet_view_real_select_all;
548 class->unselect_all = pspp_sheet_view_real_unselect_all;
549 class->select_cursor_row = pspp_sheet_view_real_select_cursor_row;
550 class->toggle_cursor_row = pspp_sheet_view_real_toggle_cursor_row;
551 class->start_interactive_search = pspp_sheet_view_start_interactive_search;
555 g_object_class_install_property (o_class,
557 g_param_spec_object ("model",
558 P_("TreeView Model"),
559 P_("The model for the tree view"),
561 GTK_PARAM_READWRITE));
563 g_object_class_override_property (o_class, PROP_HADJUSTMENT, "hadjustment");
564 g_object_class_override_property (o_class, PROP_VADJUSTMENT, "vadjustment");
565 g_object_class_override_property (o_class, PROP_HSCROLL_POLICY, "hscroll-policy");
566 g_object_class_override_property (o_class, PROP_VSCROLL_POLICY, "vscroll-policy");
568 g_object_class_install_property (o_class,
569 PROP_HEADERS_VISIBLE,
570 g_param_spec_boolean ("headers-visible",
571 P_("Headers Visible"),
572 P_("Show the column header buttons"),
574 GTK_PARAM_READWRITE));
576 g_object_class_install_property (o_class,
577 PROP_HEADERS_CLICKABLE,
578 g_param_spec_boolean ("headers-clickable",
579 P_("Headers Clickable"),
580 P_("Column headers respond to click events"),
582 GTK_PARAM_READWRITE));
584 g_object_class_install_property (o_class,
586 g_param_spec_boolean ("reorderable",
588 P_("View is reorderable"),
590 GTK_PARAM_READWRITE));
592 g_object_class_install_property (o_class,
594 g_param_spec_boolean ("rules-hint",
596 P_("Set a hint to the theme engine to draw rows in alternating colors"),
598 GTK_PARAM_READWRITE));
600 g_object_class_install_property (o_class,
602 g_param_spec_boolean ("enable-search",
604 P_("View allows user to search through columns interactively"),
606 GTK_PARAM_READWRITE));
608 g_object_class_install_property (o_class,
610 g_param_spec_int ("search-column",
612 P_("Model column to search through during interactive search"),
616 GTK_PARAM_READWRITE));
619 * PsppSheetView:hover-selection:
621 * Enables of disables the hover selection mode of @tree_view.
622 * Hover selection makes the selected row follow the pointer.
623 * Currently, this works only for the selection modes
624 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
626 * This mode is primarily intended for treeviews in popups, e.g.
627 * in #GtkComboBox or #GtkEntryCompletion.
631 g_object_class_install_property (o_class,
632 PROP_HOVER_SELECTION,
633 g_param_spec_boolean ("hover-selection",
634 P_("Hover Selection"),
635 P_("Whether the selection should follow the pointer"),
637 GTK_PARAM_READWRITE));
639 g_object_class_install_property (o_class,
641 g_param_spec_boolean ("rubber-banding",
642 P_("Rubber Banding"),
643 P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
645 GTK_PARAM_READWRITE));
647 g_object_class_install_property (o_class,
648 PROP_ENABLE_GRID_LINES,
649 g_param_spec_enum ("enable-grid-lines",
650 P_("Enable Grid Lines"),
651 P_("Whether grid lines should be drawn in the tree view"),
652 PSPP_TYPE_SHEET_VIEW_GRID_LINES,
653 PSPP_SHEET_VIEW_GRID_LINES_NONE,
654 GTK_PARAM_READWRITE));
656 g_object_class_install_property (o_class,
658 g_param_spec_int ("tooltip-column",
659 P_("Tooltip Column"),
660 P_("The column in the model containing the tooltip texts for the rows"),
664 GTK_PARAM_READWRITE));
666 g_object_class_install_property (o_class,
668 g_param_spec_enum ("special-cells",
670 P_("Whether rows have special cells."),
671 PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS,
672 PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT,
673 GTK_PARAM_READWRITE));
675 g_object_class_install_property (o_class,
677 g_param_spec_int ("fixed-height",
679 P_("Height of a single row. Normally the height of a row is determined automatically. Writing this property sets fixed-height-set to true, preventing this property's value from changing."),
683 GTK_PARAM_READWRITE));
685 g_object_class_install_property (o_class,
686 PROP_FIXED_HEIGHT_SET,
687 g_param_spec_boolean ("fixed-height-set",
688 P_("Fixed Height Set"),
689 P_("Whether fixed-height was set externally."),
691 GTK_PARAM_READWRITE));
693 /* Style properties */
694 #define _TREE_VIEW_EXPANDER_SIZE 12
695 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
696 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
698 gtk_widget_class_install_style_property (widget_class,
699 g_param_spec_int ("expander-size",
701 P_("Size of the expander arrow"),
704 _TREE_VIEW_EXPANDER_SIZE,
705 GTK_PARAM_READABLE));
707 gtk_widget_class_install_style_property (widget_class,
708 g_param_spec_int ("vertical-separator",
709 P_("Vertical Separator Width"),
710 P_("Vertical space between cells. Must be an even number"),
713 _TREE_VIEW_VERTICAL_SEPARATOR,
714 GTK_PARAM_READABLE));
716 gtk_widget_class_install_style_property (widget_class,
717 g_param_spec_int ("horizontal-separator",
718 P_("Horizontal Separator Width"),
719 P_("Horizontal space between cells. Must be an even number"),
722 _TREE_VIEW_HORIZONTAL_SEPARATOR,
723 GTK_PARAM_READABLE));
725 gtk_widget_class_install_style_property (widget_class,
726 g_param_spec_boolean ("allow-rules",
728 P_("Allow drawing of alternating color rows"),
730 GTK_PARAM_READABLE));
732 gtk_widget_class_install_style_property (widget_class,
733 g_param_spec_boxed ("even-row-color",
734 P_("Even Row Color"),
735 P_("Color to use for even rows"),
737 GTK_PARAM_READABLE));
739 gtk_widget_class_install_style_property (widget_class,
740 g_param_spec_boxed ("odd-row-color",
742 P_("Color to use for odd rows"),
744 GTK_PARAM_READABLE));
746 gtk_widget_class_install_style_property (widget_class,
747 g_param_spec_boolean ("row-ending-details",
748 P_("Row Ending details"),
749 P_("Enable extended row background theming"),
751 GTK_PARAM_READABLE));
753 gtk_widget_class_install_style_property (widget_class,
754 g_param_spec_int ("grid-line-width",
755 P_("Grid line width"),
756 P_("Width, in pixels, of the tree view grid lines"),
758 GTK_PARAM_READABLE));
760 gtk_widget_class_install_style_property (widget_class,
761 g_param_spec_int ("tree-line-width",
762 P_("Tree line width"),
763 P_("Width, in pixels, of the tree view lines"),
765 GTK_PARAM_READABLE));
767 gtk_widget_class_install_style_property (widget_class,
768 g_param_spec_string ("tree-line-pattern",
769 P_("Tree line pattern"),
770 P_("Dash pattern used to draw the tree view lines"),
772 GTK_PARAM_READABLE));
777 * PsppSheetView::set-scroll-adjustments
778 * @horizontal: the horizontal #GtkAdjustment
779 * @vertical: the vertical #GtkAdjustment
781 * Set the scroll adjustments for the tree view. Usually scrolled containers
782 * like #GtkScrolledWindow will emit this signal to connect two instances
783 * of #GtkScrollbar to the scroll directions of the #PsppSheetView.
785 widget_class->set_scroll_adjustments_signal =
786 g_signal_new ("set-scroll-adjustments",
787 G_TYPE_FROM_CLASS (o_class),
788 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
789 G_STRUCT_OFFSET (PsppSheetViewClass, set_scroll_adjustments),
791 psppire_marshal_VOID__OBJECT_OBJECT,
794 GTK_TYPE_ADJUSTMENT);
798 * PsppSheetView::row-activated:
799 * @tree_view: the object on which the signal is emitted
800 * @path: the #GtkTreePath for the activated row
801 * @column: the #PsppSheetViewColumn in which the activation occurred
803 * The "row-activated" signal is emitted when the method
804 * pspp_sheet_view_row_activated() is called or the user double clicks
805 * a treeview row. It is also emitted when a non-editable row is
806 * selected and one of the keys: Space, Shift+Space, Return or
809 * For selection handling refer to the <link linkend="TreeWidget">tree
810 * widget conceptual overview</link> as well as #PsppSheetSelection.
812 tree_view_signals[ROW_ACTIVATED] =
813 g_signal_new ("row-activated",
814 G_TYPE_FROM_CLASS (o_class),
815 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
816 G_STRUCT_OFFSET (PsppSheetViewClass, row_activated),
818 psppire_marshal_VOID__BOXED_OBJECT,
821 PSPP_TYPE_SHEET_VIEW_COLUMN);
824 * PsppSheetView::columns-changed:
825 * @tree_view: the object on which the signal is emitted
827 * The number of columns of the treeview has changed.
829 tree_view_signals[COLUMNS_CHANGED] =
830 g_signal_new ("columns-changed",
831 G_TYPE_FROM_CLASS (o_class),
833 G_STRUCT_OFFSET (PsppSheetViewClass, columns_changed),
835 g_cclosure_marshal_VOID__VOID,
839 * PsppSheetView::cursor-changed:
840 * @tree_view: the object on which the signal is emitted
842 * The position of the cursor (focused cell) has changed.
844 tree_view_signals[CURSOR_CHANGED] =
845 g_signal_new ("cursor-changed",
846 G_TYPE_FROM_CLASS (o_class),
848 G_STRUCT_OFFSET (PsppSheetViewClass, cursor_changed),
850 g_cclosure_marshal_VOID__VOID,
853 tree_view_signals[MOVE_CURSOR] =
854 g_signal_new ("move-cursor",
855 G_TYPE_FROM_CLASS (o_class),
856 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
857 G_STRUCT_OFFSET (PsppSheetViewClass, move_cursor),
859 psppire_marshal_BOOLEAN__ENUM_INT,
861 GTK_TYPE_MOVEMENT_STEP,
864 tree_view_signals[SELECT_ALL] =
865 g_signal_new ("select-all",
866 G_TYPE_FROM_CLASS (o_class),
867 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
868 G_STRUCT_OFFSET (PsppSheetViewClass, select_all),
870 psppire_marshal_BOOLEAN__VOID,
873 tree_view_signals[UNSELECT_ALL] =
874 g_signal_new ("unselect-all",
875 G_TYPE_FROM_CLASS (o_class),
876 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
877 G_STRUCT_OFFSET (PsppSheetViewClass, unselect_all),
879 psppire_marshal_BOOLEAN__VOID,
882 tree_view_signals[SELECT_CURSOR_ROW] =
883 g_signal_new ("select-cursor-row",
884 G_TYPE_FROM_CLASS (o_class),
885 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
886 G_STRUCT_OFFSET (PsppSheetViewClass, select_cursor_row),
888 psppire_marshal_BOOLEAN__BOOLEAN,
890 G_TYPE_BOOLEAN, G_TYPE_INT);
892 tree_view_signals[TOGGLE_CURSOR_ROW] =
893 g_signal_new ("toggle-cursor-row",
894 G_TYPE_FROM_CLASS (o_class),
895 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
896 G_STRUCT_OFFSET (PsppSheetViewClass, toggle_cursor_row),
898 psppire_marshal_BOOLEAN__VOID,
901 tree_view_signals[START_INTERACTIVE_SEARCH] =
902 g_signal_new ("start-interactive-search",
903 G_TYPE_FROM_CLASS (o_class),
904 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
905 G_STRUCT_OFFSET (PsppSheetViewClass, start_interactive_search),
907 psppire_marshal_BOOLEAN__VOID,
911 for (i = 0; i < 2; i++)
913 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Up, 0, TRUE,
914 GTK_MOVEMENT_DISPLAY_LINES, -1);
915 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Up, 0, TRUE,
916 GTK_MOVEMENT_DISPLAY_LINES, -1);
918 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Down, 0, TRUE,
919 GTK_MOVEMENT_DISPLAY_LINES, 1);
920 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Down, 0, TRUE,
921 GTK_MOVEMENT_DISPLAY_LINES, 1);
923 pspp_sheet_view_add_move_binding (binding_set[i], GDK_p, GDK_CONTROL_MASK, FALSE,
924 GTK_MOVEMENT_DISPLAY_LINES, -1);
926 pspp_sheet_view_add_move_binding (binding_set[i], GDK_n, GDK_CONTROL_MASK, FALSE,
927 GTK_MOVEMENT_DISPLAY_LINES, 1);
929 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Home, 0, TRUE,
930 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
931 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Home, 0, TRUE,
932 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
934 pspp_sheet_view_add_move_binding (binding_set[i], GDK_End, 0, TRUE,
935 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
936 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_End, 0, TRUE,
937 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
939 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Up, 0, TRUE,
940 GTK_MOVEMENT_PAGES, -1);
941 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Up, 0, TRUE,
942 GTK_MOVEMENT_PAGES, -1);
944 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Down, 0, TRUE,
945 GTK_MOVEMENT_PAGES, 1);
946 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Down, 0, TRUE,
947 GTK_MOVEMENT_PAGES, 1);
950 gtk_binding_entry_add_signal (binding_set[i], GDK_Up, GDK_CONTROL_MASK, "move-cursor", 2,
951 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
954 gtk_binding_entry_add_signal (binding_set[i], GDK_Down, GDK_CONTROL_MASK, "move-cursor", 2,
955 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
958 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, 0, "move-cursor", 2,
959 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
962 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, 0, "move-cursor", 2,
963 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
966 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, 0, "move-cursor", 2,
967 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
970 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, GDK_SHIFT_MASK, "move-cursor", 2,
971 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
974 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, 0, "move-cursor", 2,
975 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
978 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, 0, "move-cursor", 2,
979 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
982 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, GDK_CONTROL_MASK,
984 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
987 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, GDK_CONTROL_MASK,
989 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
992 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, GDK_CONTROL_MASK,
994 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
997 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, GDK_CONTROL_MASK,
999 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1002 gtk_binding_entry_add_signal (binding_set[i], GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
1004 gtk_binding_entry_add_signal (binding_set[i], GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
1007 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1008 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1010 gtk_binding_entry_add_signal (binding_set[0], GDK_a, GDK_CONTROL_MASK, "select-all", 0);
1011 gtk_binding_entry_add_signal (binding_set[0], GDK_slash, GDK_CONTROL_MASK, "select-all", 0);
1013 gtk_binding_entry_add_signal (binding_set[0], GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
1014 gtk_binding_entry_add_signal (binding_set[0], GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0);
1016 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1017 G_TYPE_BOOLEAN, TRUE,
1018 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1019 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1020 G_TYPE_BOOLEAN, TRUE,
1021 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1023 gtk_binding_entry_add_signal (binding_set[0], GDK_space, 0, "select-cursor-row", 1,
1024 G_TYPE_BOOLEAN, TRUE,
1026 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, 0, "select-cursor-row", 1,
1027 G_TYPE_BOOLEAN, TRUE,
1029 gtk_binding_entry_add_signal (binding_set[0], GDK_Return, 0, "select-cursor-row", 1,
1030 G_TYPE_BOOLEAN, TRUE,
1032 gtk_binding_entry_add_signal (binding_set[0], GDK_ISO_Enter, 0, "select-cursor-row", 1,
1033 G_TYPE_BOOLEAN, TRUE,
1035 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Enter, 0, "select-cursor-row", 1,
1036 G_TYPE_BOOLEAN, TRUE,
1039 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, 0, "select-cursor-parent", 0);
1040 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0);
1042 g_type_class_add_private (o_class, sizeof (PsppSheetViewPrivate));
1046 pspp_sheet_view_buildable_init (GtkBuildableIface *iface)
1048 iface->add_child = pspp_sheet_view_buildable_add_child;
1052 pspp_sheet_view_init (PsppSheetView *tree_view)
1054 tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, PSPP_TYPE_SHEET_VIEW, PsppSheetViewPrivate);
1056 gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
1057 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE);
1059 tree_view->priv->flags = PSPP_SHEET_VIEW_DRAW_KEYFOCUS
1060 | PSPP_SHEET_VIEW_HEADERS_VISIBLE;
1062 /* We need some padding */
1063 tree_view->priv->selected = range_tower_create ();
1064 tree_view->priv->dy = 0;
1065 tree_view->priv->cursor_offset = 0;
1066 tree_view->priv->n_columns = 0;
1067 tree_view->priv->header_height = 1;
1068 tree_view->priv->x_drag = 0;
1069 tree_view->priv->drag_pos = -1;
1070 tree_view->priv->header_has_focus = FALSE;
1071 tree_view->priv->pressed_button = -1;
1072 tree_view->priv->press_start_x = -1;
1073 tree_view->priv->press_start_y = -1;
1074 tree_view->priv->reorderable = FALSE;
1075 tree_view->priv->presize_handler_timer = 0;
1076 tree_view->priv->scroll_sync_timer = 0;
1077 tree_view->priv->fixed_height = -1;
1078 tree_view->priv->fixed_height_set = FALSE;
1079 pspp_sheet_view_set_adjustments (tree_view, NULL, NULL);
1080 tree_view->priv->selection = _pspp_sheet_selection_new_with_tree_view (tree_view);
1081 tree_view->priv->enable_search = TRUE;
1082 tree_view->priv->search_column = -1;
1083 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
1084 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
1085 tree_view->priv->search_custom_entry_set = FALSE;
1086 tree_view->priv->typeselect_flush_timeout = 0;
1087 tree_view->priv->init_hadjust_value = TRUE;
1088 tree_view->priv->width = 0;
1090 tree_view->priv->hover_selection = FALSE;
1092 tree_view->priv->rubber_banding_enable = FALSE;
1094 tree_view->priv->grid_lines = PSPP_SHEET_VIEW_GRID_LINES_NONE;
1096 tree_view->priv->tooltip_column = -1;
1098 tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT;
1100 tree_view->priv->post_validation_flag = FALSE;
1102 tree_view->priv->last_button_x = -1;
1103 tree_view->priv->last_button_y = -1;
1105 tree_view->priv->event_last_x = -10000;
1106 tree_view->priv->event_last_y = -10000;
1108 tree_view->priv->prelight_node = -1;
1109 tree_view->priv->rubber_band_start_node = -1;
1110 tree_view->priv->rubber_band_end_node = -1;
1112 tree_view->priv->anchor_column = NULL;
1114 tree_view->priv->button_style = NULL;
1116 tree_view->dispose_has_run = FALSE;
1118 pspp_sheet_view_do_set_vadjustment (tree_view, NULL);
1119 pspp_sheet_view_do_set_hadjustment (tree_view, NULL);
1120 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (tree_view)),
1121 GTK_STYLE_CLASS_VIEW);
1130 pspp_sheet_view_set_property (GObject *object,
1132 const GValue *value,
1135 PsppSheetView *tree_view;
1137 tree_view = PSPP_SHEET_VIEW (object);
1142 pspp_sheet_view_set_model (tree_view, g_value_get_object (value));
1144 case PROP_HADJUSTMENT:
1145 pspp_sheet_view_do_set_hadjustment (tree_view, g_value_get_object (value));
1147 case PROP_VADJUSTMENT:
1148 pspp_sheet_view_do_set_vadjustment (tree_view, g_value_get_object (value));
1150 case PROP_HSCROLL_POLICY:
1151 tree_view->priv->hscroll_policy = g_value_get_enum (value);
1152 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1154 case PROP_VSCROLL_POLICY:
1155 tree_view->priv->vscroll_policy = g_value_get_enum (value);
1156 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1158 case PROP_HEADERS_VISIBLE:
1159 pspp_sheet_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1161 case PROP_HEADERS_CLICKABLE:
1162 pspp_sheet_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1164 case PROP_REORDERABLE:
1165 pspp_sheet_view_set_reorderable (tree_view, g_value_get_boolean (value));
1167 case PROP_RULES_HINT:
1168 pspp_sheet_view_set_rules_hint (tree_view, g_value_get_boolean (value));
1170 case PROP_ENABLE_SEARCH:
1171 pspp_sheet_view_set_enable_search (tree_view, g_value_get_boolean (value));
1173 case PROP_SEARCH_COLUMN:
1174 pspp_sheet_view_set_search_column (tree_view, g_value_get_int (value));
1176 case PROP_HOVER_SELECTION:
1177 tree_view->priv->hover_selection = g_value_get_boolean (value);
1179 case PROP_RUBBER_BANDING:
1180 tree_view->priv->rubber_banding_enable = g_value_get_boolean (value);
1182 case PROP_ENABLE_GRID_LINES:
1183 pspp_sheet_view_set_grid_lines (tree_view, g_value_get_enum (value));
1185 case PROP_TOOLTIP_COLUMN:
1186 pspp_sheet_view_set_tooltip_column (tree_view, g_value_get_int (value));
1188 case PROP_SPECIAL_CELLS:
1189 pspp_sheet_view_set_special_cells (tree_view, g_value_get_enum (value));
1191 case PROP_FIXED_HEIGHT:
1192 pspp_sheet_view_set_fixed_height (tree_view, g_value_get_int (value));
1194 case PROP_FIXED_HEIGHT_SET:
1195 if (g_value_get_boolean (value))
1197 if (!tree_view->priv->fixed_height_set
1198 && tree_view->priv->fixed_height >= 0)
1200 tree_view->priv->fixed_height_set = true;
1201 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1206 if (tree_view->priv->fixed_height_set)
1208 tree_view->priv->fixed_height_set = false;
1209 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1210 install_presize_handler (tree_view);
1215 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1221 pspp_sheet_view_get_property (GObject *object,
1226 PsppSheetView *tree_view;
1228 tree_view = PSPP_SHEET_VIEW (object);
1233 g_value_set_object (value, tree_view->priv->model);
1235 case PROP_HADJUSTMENT:
1236 g_value_set_object (value, tree_view->priv->hadjustment);
1238 case PROP_VADJUSTMENT:
1239 g_value_set_object (value, tree_view->priv->vadjustment);
1241 case PROP_HSCROLL_POLICY:
1242 g_value_set_enum (value, tree_view->priv->hscroll_policy);
1244 case PROP_VSCROLL_POLICY:
1245 g_value_set_enum (value, tree_view->priv->vscroll_policy);
1247 case PROP_HEADERS_VISIBLE:
1248 g_value_set_boolean (value, pspp_sheet_view_get_headers_visible (tree_view));
1250 case PROP_HEADERS_CLICKABLE:
1251 g_value_set_boolean (value, pspp_sheet_view_get_headers_clickable (tree_view));
1253 case PROP_REORDERABLE:
1254 g_value_set_boolean (value, tree_view->priv->reorderable);
1256 case PROP_RULES_HINT:
1257 g_value_set_boolean (value, tree_view->priv->has_rules);
1259 case PROP_ENABLE_SEARCH:
1260 g_value_set_boolean (value, tree_view->priv->enable_search);
1262 case PROP_SEARCH_COLUMN:
1263 g_value_set_int (value, tree_view->priv->search_column);
1265 case PROP_HOVER_SELECTION:
1266 g_value_set_boolean (value, tree_view->priv->hover_selection);
1268 case PROP_RUBBER_BANDING:
1269 g_value_set_boolean (value, tree_view->priv->rubber_banding_enable);
1271 case PROP_ENABLE_GRID_LINES:
1272 g_value_set_enum (value, tree_view->priv->grid_lines);
1274 case PROP_TOOLTIP_COLUMN:
1275 g_value_set_int (value, tree_view->priv->tooltip_column);
1277 case PROP_SPECIAL_CELLS:
1278 g_value_set_enum (value, tree_view->priv->special_cells);
1280 case PROP_FIXED_HEIGHT:
1281 g_value_set_int (value, pspp_sheet_view_get_fixed_height (tree_view));
1283 case PROP_FIXED_HEIGHT_SET:
1284 g_value_set_boolean (value, tree_view->priv->fixed_height_set);
1287 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1293 pspp_sheet_view_dispose (GObject *object)
1295 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1297 if (tree_view->dispose_has_run)
1300 tree_view->dispose_has_run = TRUE;
1302 if (tree_view->priv->selection != NULL)
1304 _pspp_sheet_selection_set_tree_view (tree_view->priv->selection, NULL);
1305 g_object_unref (tree_view->priv->selection);
1306 tree_view->priv->selection = NULL;
1309 if (tree_view->priv->hadjustment)
1311 g_object_unref (tree_view->priv->hadjustment);
1312 tree_view->priv->hadjustment = NULL;
1314 if (tree_view->priv->vadjustment)
1316 g_object_unref (tree_view->priv->vadjustment);
1317 tree_view->priv->vadjustment = NULL;
1320 if (tree_view->priv->button_style)
1322 g_object_unref (tree_view->priv->button_style);
1323 tree_view->priv->button_style = NULL;
1327 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->dispose (object);
1333 pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
1334 GtkBuilder *builder,
1338 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), PSPP_SHEET_VIEW_COLUMN (child));
1342 pspp_sheet_view_finalize (GObject *object)
1344 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1346 pspp_sheet_view_stop_editing (tree_view, TRUE);
1348 if (tree_view->priv->selected != NULL)
1350 range_tower_destroy (tree_view->priv->selected);
1351 tree_view->priv->selected = NULL;
1355 tree_view->priv->prelight_node = -1;
1358 if (tree_view->priv->scroll_to_path != NULL)
1360 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
1361 tree_view->priv->scroll_to_path = NULL;
1364 if (tree_view->priv->drag_dest_row != NULL)
1366 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
1367 tree_view->priv->drag_dest_row = NULL;
1370 if (tree_view->priv->top_row != NULL)
1372 gtk_tree_row_reference_free (tree_view->priv->top_row);
1373 tree_view->priv->top_row = NULL;
1376 if (tree_view->priv->column_drop_func_data &&
1377 tree_view->priv->column_drop_func_data_destroy)
1379 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
1380 tree_view->priv->column_drop_func_data = NULL;
1383 if (tree_view->priv->destroy_count_destroy &&
1384 tree_view->priv->destroy_count_data)
1386 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
1387 tree_view->priv->destroy_count_data = NULL;
1390 gtk_tree_row_reference_free (tree_view->priv->cursor);
1391 tree_view->priv->cursor = NULL;
1393 gtk_tree_row_reference_free (tree_view->priv->anchor);
1394 tree_view->priv->anchor = NULL;
1396 /* destroy interactive search dialog */
1397 if (tree_view->priv->search_window)
1399 gtk_widget_destroy (tree_view->priv->search_window);
1400 tree_view->priv->search_window = NULL;
1401 tree_view->priv->search_entry = NULL;
1402 if (tree_view->priv->typeselect_flush_timeout)
1404 g_source_remove (tree_view->priv->typeselect_flush_timeout);
1405 tree_view->priv->typeselect_flush_timeout = 0;
1409 if (tree_view->priv->search_destroy && tree_view->priv->search_user_data)
1411 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
1412 tree_view->priv->search_user_data = NULL;
1415 if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data)
1417 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
1418 tree_view->priv->search_position_user_data = NULL;
1421 pspp_sheet_view_set_model (tree_view, NULL);
1424 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->finalize (object);
1429 /* GtkWidget Methods
1432 /* GtkWidget::map helper */
1434 pspp_sheet_view_map_buttons (PsppSheetView *tree_view)
1438 g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
1440 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
1442 PsppSheetViewColumn *column;
1444 for (list = tree_view->priv->columns; list; list = list->next)
1446 column = list->data;
1447 if (column->button != NULL &&
1448 gtk_widget_get_visible (column->button) &&
1449 !gtk_widget_get_mapped (column->button))
1450 gtk_widget_map (column->button);
1452 for (list = tree_view->priv->columns; list; list = list->next)
1454 column = list->data;
1455 if (column->visible == FALSE || column->window == NULL)
1457 if (column->resizable)
1459 gdk_window_raise (column->window);
1460 gdk_window_show (column->window);
1463 gdk_window_hide (column->window);
1465 gdk_window_show (tree_view->priv->header_window);
1470 pspp_sheet_view_map (GtkWidget *widget)
1472 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1475 gtk_widget_set_mapped (widget, TRUE);
1477 tmp_list = tree_view->priv->children;
1480 PsppSheetViewChild *child = tmp_list->data;
1481 tmp_list = tmp_list->next;
1483 if (gtk_widget_get_visible (child->widget))
1485 if (!gtk_widget_get_mapped (child->widget))
1486 gtk_widget_map (child->widget);
1489 gdk_window_show (tree_view->priv->bin_window);
1491 pspp_sheet_view_map_buttons (tree_view);
1493 gdk_window_show (gtk_widget_get_window (widget));
1497 pspp_sheet_view_realize (GtkWidget *widget)
1499 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1502 GdkWindowAttr attributes;
1503 gint attributes_mask;
1504 GtkAllocation allocation;
1506 gtk_widget_set_realized (widget, TRUE);
1508 gtk_widget_get_allocation (widget, &allocation);
1510 /* Make the main, clipping window */
1511 attributes.window_type = GDK_WINDOW_CHILD;
1512 attributes.x = allocation.x;
1513 attributes.y = allocation.y;
1514 attributes.width = allocation.width;
1515 attributes.height = allocation.height;
1516 attributes.wclass = GDK_INPUT_OUTPUT;
1517 attributes.visual = gtk_widget_get_visual (widget);
1518 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1520 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
1522 window = gdk_window_new (gtk_widget_get_parent_window (widget),
1523 &attributes, attributes_mask);
1524 gtk_widget_set_window (widget, window);
1526 gtk_widget_register_window (widget, window);
1527 gtk_widget_get_allocation (widget, &allocation);
1529 /* Make the window for the tree */
1531 attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
1532 attributes.width = MAX (tree_view->priv->width, allocation.width);
1533 attributes.height = allocation.height;
1534 attributes.event_mask = (GDK_EXPOSURE_MASK |
1536 GDK_POINTER_MOTION_MASK |
1537 GDK_ENTER_NOTIFY_MASK |
1538 GDK_LEAVE_NOTIFY_MASK |
1539 GDK_BUTTON_PRESS_MASK |
1540 GDK_BUTTON_RELEASE_MASK |
1541 gtk_widget_get_events (widget));
1543 tree_view->priv->bin_window = gdk_window_new (window,
1544 &attributes, attributes_mask);
1545 gtk_widget_register_window (widget, tree_view->priv->bin_window);
1546 gtk_widget_get_allocation (widget, &allocation);
1548 /* Make the column header window */
1551 attributes.width = MAX (tree_view->priv->width, allocation.width);
1552 attributes.height = tree_view->priv->header_height;
1553 attributes.event_mask = (GDK_EXPOSURE_MASK |
1555 GDK_BUTTON_PRESS_MASK |
1556 GDK_BUTTON_RELEASE_MASK |
1557 GDK_KEY_PRESS_MASK |
1558 GDK_KEY_RELEASE_MASK |
1559 gtk_widget_get_events (widget));
1561 tree_view->priv->header_window = gdk_window_new (window,
1562 &attributes, attributes_mask);
1563 gtk_widget_register_window (widget, tree_view->priv->header_window);
1565 { /* Ensure Background */
1566 GtkStyleContext *context;
1568 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
1570 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
1571 gtk_style_context_set_background (context, tree_view->priv->header_window);
1574 tmp_list = tree_view->priv->children;
1577 PsppSheetViewChild *child = tmp_list->data;
1578 tmp_list = tmp_list->next;
1580 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
1583 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
1584 _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list->data));
1586 /* Need to call those here, since they create GCs */
1587 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
1589 install_presize_handler (tree_view);
1593 pspp_sheet_view_unrealize (GtkWidget *widget)
1595 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1596 PsppSheetViewPrivate *priv = tree_view->priv;
1599 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget);
1601 if (priv->scroll_timeout != 0)
1603 g_source_remove (priv->scroll_timeout);
1604 priv->scroll_timeout = 0;
1607 if (priv->open_dest_timeout != 0)
1609 g_source_remove (priv->open_dest_timeout);
1610 priv->open_dest_timeout = 0;
1613 if (priv->presize_handler_timer != 0)
1615 g_source_remove (priv->presize_handler_timer);
1616 priv->presize_handler_timer = 0;
1619 if (priv->validate_rows_timer != 0)
1621 g_source_remove (priv->validate_rows_timer);
1622 priv->validate_rows_timer = 0;
1625 if (priv->scroll_sync_timer != 0)
1627 g_source_remove (priv->scroll_sync_timer);
1628 priv->scroll_sync_timer = 0;
1631 if (priv->typeselect_flush_timeout)
1633 g_source_remove (priv->typeselect_flush_timeout);
1634 priv->typeselect_flush_timeout = 0;
1637 for (list = priv->columns; list; list = list->next)
1638 _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list->data));
1640 gdk_window_set_user_data (priv->bin_window, NULL);
1641 gdk_window_destroy (priv->bin_window);
1642 priv->bin_window = NULL;
1644 gdk_window_set_user_data (priv->header_window, NULL);
1645 gdk_window_destroy (priv->header_window);
1646 priv->header_window = NULL;
1648 if (priv->drag_window)
1650 gdk_window_set_user_data (priv->drag_window, NULL);
1651 gdk_window_destroy (priv->drag_window);
1652 priv->drag_window = NULL;
1655 if (priv->drag_highlight_window)
1657 gdk_window_set_user_data (priv->drag_highlight_window, NULL);
1658 gdk_window_destroy (priv->drag_highlight_window);
1659 priv->drag_highlight_window = NULL;
1662 if (tree_view->priv->columns != NULL)
1664 list = tree_view->priv->columns;
1667 PsppSheetViewColumn *column;
1668 column = PSPP_SHEET_VIEW_COLUMN (list->data);
1670 pspp_sheet_view_remove_column (tree_view, column);
1672 tree_view->priv->columns = NULL;
1676 /* GtkWidget::size_request helper */
1678 pspp_sheet_view_size_request_columns (PsppSheetView *tree_view)
1682 tree_view->priv->header_height = 0;
1684 if (tree_view->priv->model)
1686 for (list = tree_view->priv->columns; list; list = list->next)
1688 GtkRequisition requisition;
1689 PsppSheetViewColumn *column = list->data;
1691 pspp_sheet_view_column_size_request (column, &requisition);
1692 column->button_request = requisition.width;
1693 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
1699 /* Called only by ::size_request */
1701 pspp_sheet_view_update_size (PsppSheetView *tree_view)
1704 PsppSheetViewColumn *column;
1707 if (tree_view->priv->model == NULL)
1709 tree_view->priv->width = 0;
1710 tree_view->priv->prev_width = 0;
1711 tree_view->priv->height = 0;
1715 tree_view->priv->prev_width = tree_view->priv->width;
1716 tree_view->priv->width = 0;
1718 /* keep this in sync with size_allocate below */
1719 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
1721 gint real_requested_width = 0;
1722 column = list->data;
1723 if (!column->visible)
1726 if (column->use_resized_width)
1728 real_requested_width = column->resized_width;
1732 real_requested_width = column->fixed_width;
1735 if (column->min_width != -1)
1736 real_requested_width = MAX (real_requested_width, column->min_width);
1737 if (column->max_width != -1)
1738 real_requested_width = MIN (real_requested_width, column->max_width);
1740 tree_view->priv->width += real_requested_width;
1743 tree_view->priv->height = tree_view->priv->fixed_height * tree_view->priv->row_count;
1747 pspp_sheet_view_size_request (GtkWidget *widget,
1748 GtkRequisition *requisition)
1750 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1753 /* we validate some rows initially just to make sure we have some size.
1754 * In practice, with a lot of static lists, this should get a good width.
1756 initialize_fixed_height_mode (tree_view);
1757 pspp_sheet_view_size_request_columns (tree_view);
1758 pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget));
1760 requisition->width = tree_view->priv->width;
1761 requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
1763 tmp_list = tree_view->priv->children;
1767 PsppSheetViewChild *child = tmp_list->data;
1768 GtkRequisition child_requisition;
1770 tmp_list = tmp_list->next;
1772 if (gtk_widget_get_visible (child->widget))
1773 gtk_widget_size_request (child->widget, &child_requisition);
1778 invalidate_column (PsppSheetView *tree_view,
1779 PsppSheetViewColumn *column)
1781 gint column_offset = 0;
1783 GtkWidget *widget = GTK_WIDGET (tree_view);
1786 if (!gtk_widget_get_realized (widget))
1789 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1790 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
1792 list = (rtl ? list->prev : list->next))
1794 PsppSheetViewColumn *tmpcolumn = list->data;
1795 if (tmpcolumn == column)
1797 GdkRectangle invalid_rect;
1798 GtkAllocation allocation;
1800 gtk_widget_get_allocation (widget, &allocation);
1801 invalid_rect.x = column_offset;
1803 invalid_rect.width = column->width;
1804 invalid_rect.height = allocation.height;
1806 gdk_window_invalidate_rect (gtk_widget_get_window (widget), &invalid_rect, TRUE);
1810 column_offset += tmpcolumn->width;
1815 invalidate_last_column (PsppSheetView *tree_view)
1820 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1822 for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
1824 last_column = (rtl ? last_column->next : last_column->prev))
1826 if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible)
1828 invalidate_column (tree_view, last_column->data);
1835 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_view,
1836 PsppSheetViewColumn *column)
1838 gint real_requested_width;
1840 if (column->use_resized_width)
1842 real_requested_width = column->resized_width;
1846 real_requested_width = column->fixed_width;
1849 if (column->min_width != -1)
1850 real_requested_width = MAX (real_requested_width, column->min_width);
1851 if (column->max_width != -1)
1852 real_requested_width = MIN (real_requested_width, column->max_width);
1854 return real_requested_width;
1858 span_intersects (int a0, int a_width,
1859 int b0, int b_width)
1861 int a1 = a0 + a_width;
1862 int b1 = b0 + b_width;
1863 return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1);
1866 /* GtkWidget::size_allocate helper */
1868 pspp_sheet_view_size_allocate_columns (GtkWidget *widget,
1869 gboolean *width_changed)
1871 PsppSheetView *tree_view;
1872 GList *list, *first_column, *last_column;
1873 PsppSheetViewColumn *column;
1874 GtkAllocation col_allocation;
1875 GtkAllocation allocation;
1877 gint extra, extra_per_column;
1878 gint full_requested_width = 0;
1879 gint number_of_expand_columns = 0;
1880 gboolean column_changed = FALSE;
1883 tree_view = PSPP_SHEET_VIEW (widget);
1885 for (last_column = g_list_last (tree_view->priv->columns);
1886 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
1887 last_column = last_column->prev)
1890 if (last_column == NULL)
1893 for (first_column = g_list_first (tree_view->priv->columns);
1894 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
1895 first_column = first_column->next)
1898 col_allocation.y = 0;
1899 col_allocation.height = tree_view->priv->header_height;
1901 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1903 /* find out how many extra space and expandable columns we have */
1904 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
1906 column = (PsppSheetViewColumn *)list->data;
1908 if (!column->visible)
1911 full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1914 number_of_expand_columns++;
1917 gtk_widget_get_allocation (widget, &allocation);
1918 extra = MAX (allocation.width - full_requested_width, 0);
1919 if (number_of_expand_columns > 0)
1920 extra_per_column = extra/number_of_expand_columns;
1922 extra_per_column = 0;
1924 for (list = (rtl ? last_column : first_column);
1925 list != (rtl ? first_column->prev : last_column->next);
1926 list = (rtl ? list->prev : list->next))
1928 gint real_requested_width = 0;
1931 column = list->data;
1932 old_width = column->width;
1934 if (!column->visible)
1937 /* We need to handle the dragged button specially.
1939 if (column == tree_view->priv->drag_column)
1941 GtkAllocation drag_allocation;
1942 drag_allocation.width = gdk_window_get_width (tree_view->priv->drag_window);
1943 drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window);
1944 drag_allocation.x = 0;
1945 drag_allocation.y = 0;
1946 pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column,
1948 width += drag_allocation.width;
1952 real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1954 col_allocation.x = width;
1955 column->width = real_requested_width;
1959 if (number_of_expand_columns == 1)
1961 /* We add the remander to the last column as
1963 column->width += extra;
1967 column->width += extra_per_column;
1968 extra -= extra_per_column;
1969 number_of_expand_columns --;
1973 if (column->width != old_width)
1974 g_object_notify (G_OBJECT (column), "width");
1976 col_allocation.width = column->width;
1977 width += column->width;
1979 if (column->width > old_width)
1980 column_changed = TRUE;
1982 pspp_sheet_view_column_size_allocate (column, &col_allocation);
1985 gdk_window_move_resize (column->window,
1986 col_allocation.x + (rtl ? 0 : col_allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
1988 TREE_VIEW_DRAG_WIDTH, col_allocation.height);
1991 /* We change the width here. The user might have been resizing columns,
1992 * so the total width of the tree view changes.
1994 tree_view->priv->width = width;
1996 *width_changed = TRUE;
1999 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2003 update_childrens_allocation (PsppSheetView *tree_view)
2006 for (tmp_list = tree_view->priv->children; tmp_list; tmp_list = tmp_list->next)
2008 PsppSheetViewChild *child = tmp_list->data;
2009 GtkAllocation allocation;
2012 /* totally ignore our child's requisition */
2013 path = _pspp_sheet_view_find_path (tree_view, child->node);
2014 pspp_sheet_view_get_cell_area (tree_view, path, child->column, &allocation);
2015 gtk_tree_path_free (path);
2016 gtk_widget_size_allocate (child->widget, &allocation);
2021 pspp_sheet_view_size_allocate (GtkWidget *widget,
2022 GtkAllocation *allocation)
2024 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2026 gboolean width_changed = FALSE;
2027 GtkAllocation old_allocation;
2028 gtk_widget_get_allocation (widget, &old_allocation);
2030 if (allocation->width != old_allocation.width)
2031 width_changed = TRUE;
2033 gtk_widget_set_allocation (widget, allocation);
2035 /* We size-allocate the columns first because the width of the
2036 * tree view (used in updating the adjustments below) might change.
2038 pspp_sheet_view_size_allocate_columns (widget, &width_changed);
2040 gtk_adjustment_set_page_size (tree_view->priv->hadjustment, allocation->width);
2041 gtk_adjustment_set_page_increment (tree_view->priv->hadjustment, allocation->width * 0.9);
2042 gtk_adjustment_set_step_increment (tree_view->priv->hadjustment, allocation->width * 0.1);
2043 gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0);
2044 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->hadjustment), tree_view->priv->width));
2046 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2048 if (allocation->width < tree_view->priv->width)
2050 if (tree_view->priv->init_hadjust_value)
2052 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2053 tree_view->priv->init_hadjust_value = FALSE;
2055 else if (allocation->width != old_allocation.width)
2057 gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) - allocation->width + old_allocation.width, 0, tree_view->priv->width - allocation->width));
2060 gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (tree_view->priv->width - (tree_view->priv->prev_width - gtk_adjustment_get_value (tree_view->priv->hadjustment)), 0, tree_view->priv->width - allocation->width));
2064 gtk_adjustment_set_value (tree_view->priv->hadjustment, 0);
2065 tree_view->priv->init_hadjust_value = TRUE;
2069 if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + allocation->width > tree_view->priv->width)
2070 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2072 gtk_adjustment_changed (tree_view->priv->hadjustment);
2074 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2075 gtk_adjustment_set_step_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.1);
2076 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.9);
2077 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
2078 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->vadjustment), tree_view->priv->height));
2080 gtk_adjustment_changed (tree_view->priv->vadjustment);
2082 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2083 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
2084 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2085 else if (gtk_adjustment_get_value (tree_view->priv->vadjustment) + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
2086 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2087 tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
2088 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2089 pspp_sheet_view_top_row_to_dy (tree_view);
2091 pspp_sheet_view_dy_to_top_row (tree_view);
2093 if (gtk_widget_get_realized (widget))
2095 gdk_window_move_resize (gtk_widget_get_window (widget),
2096 allocation->x, allocation->y,
2097 allocation->width, allocation->height);
2098 gdk_window_move_resize (tree_view->priv->header_window,
2099 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2101 MAX (tree_view->priv->width, allocation->width),
2102 tree_view->priv->header_height);
2103 gdk_window_move_resize (tree_view->priv->bin_window,
2104 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2105 TREE_VIEW_HEADER_HEIGHT (tree_view),
2106 MAX (tree_view->priv->width, allocation->width),
2107 allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2110 if (tree_view->priv->row_count == 0)
2111 invalidate_empty_focus (tree_view);
2113 if (gtk_widget_get_realized (widget))
2115 gboolean has_expand_column = FALSE;
2116 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2118 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2120 has_expand_column = TRUE;
2125 /* This little hack only works if we have an LTR locale, and no column has the */
2128 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2129 ! has_expand_column)
2130 invalidate_last_column (tree_view);
2132 gtk_widget_queue_draw (widget);
2134 update_childrens_allocation(tree_view);
2138 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2140 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2142 GtkWidget *widget = GTK_WIDGET (tree_view);
2144 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2145 gtk_widget_grab_focus (widget);
2146 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2150 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2153 return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2157 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2160 range_tower_set1 (tree_view->priv->selected, node, 1);
2164 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2167 range_tower_set0 (tree_view->priv->selected, node, 1);
2171 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2174 return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2178 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2181 return node > 0 ? node - 1 : -1;
2185 all_columns_selected (PsppSheetView *tree_view)
2189 for (list = tree_view->priv->columns; list; list = list->next)
2191 PsppSheetViewColumn *column = list->data;
2192 if (column->selectable && !column->selected)
2200 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2202 PsppSheetViewColumn *column,
2203 GdkEventButton *event)
2205 PsppSheetSelection *selection;
2206 PsppSheetSelectionMode mode;
2208 gboolean update_anchor;
2212 g_return_val_if_fail (tree_view != NULL, FALSE);
2213 g_return_val_if_fail (column != NULL, FALSE);
2215 selection = tree_view->priv->selection;
2216 mode = pspp_sheet_selection_get_mode (selection);
2217 if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2220 if (!column->row_head)
2225 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2226 if (event->type != GDK_BUTTON_PRESS
2227 || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2233 path = gtk_tree_path_new_from_indices (node, -1);
2236 pspp_sheet_selection_unselect_all (selection);
2237 pspp_sheet_selection_select_path (selection, path);
2238 pspp_sheet_selection_select_all_columns (selection);
2239 update_anchor = TRUE;
2242 else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2244 if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2245 || !all_columns_selected (tree_view))
2247 pspp_sheet_selection_unselect_all (selection);
2248 pspp_sheet_selection_select_path (selection, path);
2249 pspp_sheet_selection_select_all_columns (selection);
2250 update_anchor = TRUE;
2254 update_anchor = handled = FALSE;
2256 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2257 && modifiers == GDK_CONTROL_MASK)
2259 if (!all_columns_selected (tree_view))
2261 pspp_sheet_selection_unselect_all (selection);
2262 pspp_sheet_selection_select_all_columns (selection);
2265 if (pspp_sheet_selection_path_is_selected (selection, path))
2266 pspp_sheet_selection_unselect_path (selection, path);
2268 pspp_sheet_selection_select_path (selection, path);
2269 update_anchor = TRUE;
2272 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2273 && modifiers == GDK_SHIFT_MASK)
2275 GtkTreeRowReference *anchor = tree_view->priv->anchor;
2276 GtkTreePath *anchor_path;
2278 if (all_columns_selected (tree_view)
2279 && gtk_tree_row_reference_valid (anchor))
2281 update_anchor = FALSE;
2282 anchor_path = gtk_tree_row_reference_get_path (anchor);
2286 update_anchor = TRUE;
2287 anchor_path = gtk_tree_path_copy (path);
2290 pspp_sheet_selection_unselect_all (selection);
2291 pspp_sheet_selection_select_range (selection, anchor_path, path);
2292 pspp_sheet_selection_select_all_columns (selection);
2294 gtk_tree_path_free (anchor_path);
2299 update_anchor = handled = FALSE;
2303 if (tree_view->priv->anchor)
2304 gtk_tree_row_reference_free (tree_view->priv->anchor);
2305 tree_view->priv->anchor =
2306 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2307 tree_view->priv->model,
2311 gtk_tree_path_free (path);
2316 find_click (PsppSheetView *tree_view,
2319 PsppSheetViewColumn **column,
2320 GdkRectangle *background_area,
2321 GdkRectangle *cell_area)
2328 /* find the node that was clicked */
2329 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2332 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2337 background_area->y = y_offset + y;
2338 background_area->height = ROW_HEIGHT (tree_view);
2339 background_area->x = 0;
2341 /* Let the column have a chance at selecting it. */
2342 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2343 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2344 list; list = (rtl ? list->prev : list->next))
2346 PsppSheetViewColumn *candidate = list->data;
2348 if (!candidate->visible)
2351 background_area->width = candidate->width;
2352 if ((background_area->x > x) ||
2353 (background_area->x + background_area->width <= x))
2355 background_area->x += background_area->width;
2359 /* we found the focus column */
2361 pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2363 *column = candidate;
2371 pspp_sheet_view_button_press (GtkWidget *widget,
2372 GdkEventButton *event)
2374 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2376 PsppSheetViewColumn *column = NULL;
2378 GdkRectangle background_area;
2379 GdkRectangle cell_area;
2382 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2383 pspp_sheet_view_stop_editing (tree_view, FALSE);
2386 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2387 * we're done handling the button press.
2390 if (event->window == tree_view->priv->bin_window)
2395 gint pre_val, aft_val;
2396 PsppSheetViewColumn *column = NULL;
2397 GtkCellRenderer *focus_cell = NULL;
2398 gboolean row_double_click = FALSE;
2401 if (tree_view->priv->row_count == 0)
2403 grab_focus_and_unset_draw_keyfocus (tree_view);
2407 if (!find_click (tree_view, event->x, event->y, &node, &column,
2408 &background_area, &cell_area))
2410 grab_focus_and_unset_draw_keyfocus (tree_view);
2414 tree_view->priv->focus_column = column;
2416 if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2420 pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2422 path = _pspp_sheet_view_find_path (tree_view, node);
2424 /* we only handle selection modifications on the first button press
2426 if (event->type == GDK_BUTTON_PRESS)
2428 PsppSheetSelectionMode mode = 0;
2430 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2431 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
2432 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2433 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
2435 focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2437 pspp_sheet_view_column_focus_cell (column, focus_cell);
2439 if (event->state & GDK_CONTROL_MASK)
2441 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, mode);
2442 pspp_sheet_view_real_toggle_cursor_row (tree_view);
2444 else if (event->state & GDK_SHIFT_MASK)
2446 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
2447 pspp_sheet_view_real_select_cursor_row (tree_view, FALSE, mode);
2451 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
2454 if (tree_view->priv->anchor_column == NULL ||
2455 !(event->state & GDK_SHIFT_MASK))
2456 tree_view->priv->anchor_column = column;
2457 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2458 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2459 tree_view->priv->anchor_column,
2463 /* the treeview may have been scrolled because of _set_cursor,
2467 aft_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2468 dval = pre_val - aft_val;
2470 cell_area.y += dval;
2471 background_area.y += dval;
2473 /* Save press to possibly begin a drag
2475 if (!tree_view->priv->in_grab &&
2476 tree_view->priv->pressed_button < 0)
2478 tree_view->priv->pressed_button = event->button;
2479 tree_view->priv->press_start_x = event->x;
2480 tree_view->priv->press_start_y = event->y;
2481 tree_view->priv->press_start_node = node;
2483 if (tree_view->priv->rubber_banding_enable
2484 && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2485 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2487 tree_view->priv->press_start_y += tree_view->priv->dy;
2488 tree_view->priv->rubber_band_x = event->x;
2489 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2490 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2492 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2493 tree_view->priv->rubber_band_ctrl = TRUE;
2494 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2495 tree_view->priv->rubber_band_shift = TRUE;
2500 /* Test if a double click happened on the same row. */
2501 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2503 int double_click_time, double_click_distance;
2505 g_object_get (gtk_settings_get_for_screen (
2506 gtk_widget_get_screen (widget)),
2507 "gtk-double-click-time", &double_click_time,
2508 "gtk-double-click-distance", &double_click_distance,
2511 /* Same conditions as _gdk_event_button_generate */
2512 if (tree_view->priv->last_button_x != -1 &&
2513 (event->time < tree_view->priv->last_button_time + double_click_time) &&
2514 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2515 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2517 /* We do no longer compare paths of this row and the
2518 * row clicked previously. We use the double click
2519 * distance to decide whether this is a valid click,
2520 * allowing the mouse to slightly move over another row.
2522 row_double_click = TRUE;
2524 tree_view->priv->last_button_time = 0;
2525 tree_view->priv->last_button_x = -1;
2526 tree_view->priv->last_button_y = -1;
2530 tree_view->priv->last_button_time = event->time;
2531 tree_view->priv->last_button_x = event->x;
2532 tree_view->priv->last_button_y = event->y;
2536 if (row_double_click)
2538 gtk_grab_remove (widget);
2539 pspp_sheet_view_row_activated (tree_view, path, column);
2541 if (tree_view->priv->pressed_button == event->button)
2542 tree_view->priv->pressed_button = -1;
2545 gtk_tree_path_free (path);
2547 /* If we activated the row through a double click we don't want to grab
2548 * focus back, as moving focus to another widget is pretty common.
2550 if (!row_double_click)
2551 grab_focus_and_unset_draw_keyfocus (tree_view);
2556 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2558 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2560 column = list->data;
2561 if (event->window == column->window &&
2562 column->resizable &&
2567 if (gdk_pointer_grab (column->window, FALSE,
2568 GDK_POINTER_MOTION_HINT_MASK |
2569 GDK_BUTTON1_MOTION_MASK |
2570 GDK_BUTTON_RELEASE_MASK,
2571 NULL, NULL, event->time))
2574 gtk_grab_add (widget);
2575 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2576 column->resized_width = column->width;
2578 /* block attached dnd signal handler */
2579 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2581 g_signal_handlers_block_matched (widget,
2582 G_SIGNAL_MATCH_DATA,
2586 tree_view->priv->drag_pos = i;
2587 tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2589 if (!gtk_widget_has_focus (widget))
2590 gtk_widget_grab_focus (widget);
2598 /* GtkWidget::button_release_event helper */
2600 pspp_sheet_view_button_release_drag_column (GtkWidget *widget,
2601 GdkEventButton *event)
2603 PsppSheetView *tree_view;
2607 tree_view = PSPP_SHEET_VIEW (widget);
2609 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2610 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2611 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2613 /* Move the button back */
2614 g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2616 g_object_ref (tree_view->priv->drag_column->button);
2617 gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2618 gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2619 gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2620 g_object_unref (tree_view->priv->drag_column->button);
2621 gtk_widget_queue_resize (widget);
2622 if (tree_view->priv->drag_column->resizable)
2624 gdk_window_raise (tree_view->priv->drag_column->window);
2625 gdk_window_show (tree_view->priv->drag_column->window);
2628 gdk_window_hide (tree_view->priv->drag_column->window);
2630 gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2634 if (tree_view->priv->cur_reorder &&
2635 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2636 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2637 tree_view->priv->cur_reorder->right_column);
2641 if (tree_view->priv->cur_reorder &&
2642 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2643 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2644 tree_view->priv->cur_reorder->left_column);
2646 tree_view->priv->drag_column = NULL;
2647 gdk_window_hide (tree_view->priv->drag_window);
2649 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2650 g_slice_free (PsppSheetViewColumnReorder, l->data);
2651 g_list_free (tree_view->priv->column_drag_info);
2652 tree_view->priv->column_drag_info = NULL;
2653 tree_view->priv->cur_reorder = NULL;
2655 if (tree_view->priv->drag_highlight_window)
2656 gdk_window_hide (tree_view->priv->drag_highlight_window);
2658 /* Reset our flags */
2659 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2660 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2665 /* GtkWidget::button_release_event helper */
2667 pspp_sheet_view_button_release_column_resize (GtkWidget *widget,
2668 GdkEventButton *event)
2670 PsppSheetView *tree_view;
2673 tree_view = PSPP_SHEET_VIEW (widget);
2675 tree_view->priv->drag_pos = -1;
2677 /* unblock attached dnd signal handler */
2678 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2680 g_signal_handlers_unblock_matched (widget,
2681 G_SIGNAL_MATCH_DATA,
2685 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2686 gtk_grab_remove (widget);
2687 gdk_display_pointer_ungrab (gdk_window_get_display (event->window),
2693 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2694 GdkEventButton *event)
2696 GtkCellEditable *cell_editable;
2701 PsppSheetViewColumn *column;
2702 GdkRectangle background_area;
2703 GdkRectangle cell_area;
2709 if (event->window != tree_view->priv->bin_window)
2712 /* Ignore a released button, if that button wasn't depressed */
2713 if (tree_view->priv->pressed_button != event->button)
2716 if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2720 /* decide if we edit */
2721 path = _pspp_sheet_view_find_path (tree_view, node);
2722 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2723 if (event->button != 1 || modifiers)
2726 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2727 pspp_sheet_view_column_cell_set_cell_data (column,
2728 tree_view->priv->model,
2731 if (!pspp_sheet_view_column_get_quick_edit (column)
2732 && _pspp_sheet_view_column_has_editable_cell (column))
2735 flags = 0; /* FIXME: get the right flags */
2736 path_string = gtk_tree_path_to_string (path);
2738 if (!_pspp_sheet_view_column_cell_event (column,
2746 if (cell_editable == NULL)
2749 pspp_sheet_view_real_set_cursor (tree_view, path,
2750 TRUE, TRUE, 0); /* XXX mode? */
2751 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2754 _pspp_sheet_view_column_get_neighbor_sizes (
2755 column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2758 area.width -= right + left;
2760 pspp_sheet_view_real_start_editing (tree_view,
2767 g_free (path_string);
2768 gtk_tree_path_free (path);
2773 pspp_sheet_view_button_release (GtkWidget *widget,
2774 GdkEventButton *event)
2776 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2778 pspp_sheet_view_stop_editing (tree_view, FALSE);
2779 if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2780 && pspp_sheet_view_button_release_edit (tree_view, event))
2782 if (tree_view->priv->pressed_button == event->button)
2783 tree_view->priv->pressed_button = -1;
2785 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2789 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2790 return pspp_sheet_view_button_release_drag_column (widget, event);
2792 if (tree_view->priv->rubber_band_status)
2793 pspp_sheet_view_stop_rubber_band (tree_view);
2795 if (tree_view->priv->pressed_button == event->button)
2796 tree_view->priv->pressed_button = -1;
2798 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2799 return pspp_sheet_view_button_release_column_resize (widget, event);
2805 pspp_sheet_view_grab_broken (GtkWidget *widget,
2806 GdkEventGrabBroken *event)
2808 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2810 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2811 pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2813 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2814 pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2819 /* GtkWidget::motion_event function set.
2823 do_prelight (PsppSheetView *tree_view,
2825 /* these are in bin_window coords */
2829 int prev_node = tree_view->priv->prelight_node;
2831 if (prev_node != node)
2833 tree_view->priv->prelight_node = node;
2836 _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2839 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2845 prelight_or_select (PsppSheetView *tree_view,
2847 /* these are in bin_window coords */
2851 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2853 if (tree_view->priv->hover_selection &&
2854 (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2855 !(tree_view->priv->edited_column &&
2856 tree_view->priv->edited_column->editable_widget))
2860 if (!pspp_sheet_view_node_is_selected (tree_view, node))
2864 path = _pspp_sheet_view_find_path (tree_view, node);
2865 pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2866 if (pspp_sheet_view_node_is_selected (tree_view, node))
2868 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2869 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE, 0); /* XXX mode? */
2871 gtk_tree_path_free (path);
2875 else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2876 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2879 do_prelight (tree_view, node, x, y);
2883 ensure_unprelighted (PsppSheetView *tree_view)
2885 do_prelight (tree_view,
2887 -1000, -1000); /* coords not possibly over an arrow */
2889 g_assert (tree_view->priv->prelight_node < 0);
2893 update_prelight (PsppSheetView *tree_view,
2900 if (tree_view->priv->row_count == 0)
2905 ensure_unprelighted (tree_view);
2909 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2913 pspp_sheet_view_find_offset (tree_view, new_y, &node);
2916 prelight_or_select (tree_view, node, x, y);
2922 /* Our motion arrow is either a box (in the case of the original spot)
2923 * or an arrow. It is expander_size wide.
2946 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2949 PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2950 GtkWidget *widget = GTK_WIDGET (tree_view);
2951 GdkBitmap *mask = NULL;
2956 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2957 GdkWindowAttr attributes;
2958 guint attributes_mask;
2961 reorder->left_column == tree_view->priv->drag_column ||
2962 reorder->right_column == tree_view->priv->drag_column)
2963 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2964 else if (reorder->left_column || reorder->right_column)
2966 GdkRectangle visible_rect;
2967 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2968 if (reorder->left_column)
2969 x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2971 x = reorder->right_column->allocation.x;
2973 if (x < visible_rect.x)
2974 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2975 else if (x > visible_rect.x + visible_rect.width)
2976 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2978 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2981 /* We want to draw the rectangle over the initial location. */
2982 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2987 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2989 if (tree_view->priv->drag_highlight_window)
2991 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2993 gdk_window_destroy (tree_view->priv->drag_highlight_window);
2996 attributes.window_type = GDK_WINDOW_CHILD;
2997 attributes.wclass = GDK_INPUT_OUTPUT;
2998 attributes.x = tree_view->priv->drag_column_x;
3000 width = attributes.width = tree_view->priv->drag_column->allocation.width;
3001 height = attributes.height = tree_view->priv->drag_column->allocation.height;
3002 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3003 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3004 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3005 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3006 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
3007 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3009 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3010 gc = gdk_gc_new (mask);
3012 gdk_gc_set_foreground (gc, &col);
3013 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3015 gdk_gc_set_foreground(gc, &col);
3016 gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
3017 g_object_unref (gc);
3019 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3021 if (mask) g_object_unref (mask);
3022 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3025 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
3031 width = tree_view->priv->expander_size;
3033 /* Get x, y, width, height of arrow */
3034 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
3035 if (reorder->left_column)
3037 x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
3038 height = reorder->left_column->allocation.height;
3042 x += reorder->right_column->allocation.x - width/2;
3043 height = reorder->right_column->allocation.height;
3045 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3046 height += tree_view->priv->expander_size;
3048 /* Create the new window */
3049 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3051 if (tree_view->priv->drag_highlight_window)
3053 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3055 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3058 attributes.window_type = GDK_WINDOW_TEMP;
3059 attributes.wclass = GDK_INPUT_OUTPUT;
3060 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3061 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3062 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3063 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3066 attributes.width = width;
3067 attributes.height = height;
3068 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3069 &attributes, attributes_mask);
3070 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3072 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3073 gc = gdk_gc_new (mask);
3075 gdk_gc_set_foreground (gc, &col);
3076 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3078 /* Draw the 2 arrows as per above */
3080 gdk_gc_set_foreground (gc, &col);
3081 for (i = 0; i < width; i ++)
3083 if (i == (width/2 - 1))
3085 gdk_draw_line (mask, gc, i, j, i, height - j);
3086 if (i < (width/2 - 1))
3091 g_object_unref (gc);
3092 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3094 if (mask) g_object_unref (mask);
3097 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3098 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3100 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3101 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3107 width = tree_view->priv->expander_size;
3109 /* Get x, y, width, height of arrow */
3110 width = width/2; /* remember, the arrow only takes half the available width */
3111 gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
3112 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3113 x += widget->allocation.width - width;
3115 if (reorder->left_column)
3116 height = reorder->left_column->allocation.height;
3118 height = reorder->right_column->allocation.height;
3120 y -= tree_view->priv->expander_size;
3121 height += 2*tree_view->priv->expander_size;
3123 /* Create the new window */
3124 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3125 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3127 if (tree_view->priv->drag_highlight_window)
3129 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3131 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3134 attributes.window_type = GDK_WINDOW_TEMP;
3135 attributes.wclass = GDK_INPUT_OUTPUT;
3136 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3137 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3138 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3139 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3142 attributes.width = width;
3143 attributes.height = height;
3144 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3145 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3147 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3148 gc = gdk_gc_new (mask);
3150 gdk_gc_set_foreground (gc, &col);
3151 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3153 /* Draw the 2 arrows as per above */
3155 gdk_gc_set_foreground (gc, &col);
3156 j = tree_view->priv->expander_size;
3157 for (i = 0; i < width; i ++)
3160 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3164 gdk_draw_line (mask, gc, k, j, k, height - j);
3165 gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3166 gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3169 g_object_unref (gc);
3170 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3172 if (mask) g_object_unref (mask);
3175 tree_view->priv->drag_column_window_state = arrow_type;
3176 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3180 g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3181 gdk_window_hide (tree_view->priv->drag_highlight_window);
3185 gdk_window_show (tree_view->priv->drag_highlight_window);
3186 gdk_window_raise (tree_view->priv->drag_highlight_window);
3191 pspp_sheet_view_motion_resize_column (GtkWidget *widget,
3192 GdkEventMotion *event)
3196 PsppSheetViewColumn *column;
3197 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3199 column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3201 if (event->is_hint || event->window != gtk_widget_get_window (widget))
3202 gdk_device_get_position (event->device, NULL, &x, NULL);
3206 if (tree_view->priv->hadjustment)
3207 x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
3209 new_width = pspp_sheet_view_new_column_width (tree_view,
3210 tree_view->priv->drag_pos, &x);
3211 if (x != tree_view->priv->x_drag &&
3212 (new_width != column->fixed_width))
3214 column->use_resized_width = TRUE;
3215 column->resized_width = new_width;
3218 column->resized_width -= tree_view->priv->last_extra_space_per_column;
3220 gtk_widget_queue_resize (widget);
3228 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3230 PsppSheetViewColumnReorder *reorder = NULL;
3234 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3235 for (list = tree_view->priv->column_drag_info; list; list = list->next)
3237 reorder = (PsppSheetViewColumnReorder *) list->data;
3238 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3243 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3246 tree_view->priv->cur_reorder = reorder;
3247 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3251 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3253 GdkRectangle visible_rect;
3258 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3259 y += tree_view->priv->dy;
3261 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3263 /* see if we are near the edge. */
3264 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3267 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3272 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->vadjustment) + offset, 0.0,
3273 gtk_adjustment_get_upper (tree_view->priv->vadjustment) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
3274 gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3278 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3280 GdkRectangle visible_rect;
3285 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3287 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3289 /* See if we are near the edge. */
3290 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3293 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3299 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) + offset,
3300 0.0, gtk_adjustment_get_upper (tree_view->priv->hadjustment) - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
3301 gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3308 pspp_sheet_view_motion_drag_column (GtkWidget *widget,
3309 GdkEventMotion *event)
3311 PsppSheetView *tree_view = (PsppSheetView *) widget;
3312 PsppSheetViewColumn *column = tree_view->priv->drag_column;
3314 GtkAllocation allocation;
3317 if ((column == NULL) ||
3318 (event->window != tree_view->priv->drag_window))
3321 /* Handle moving the header */
3322 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3323 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
3324 x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3325 MAX (tree_view->priv->width, allocation.width) - column->allocation.width);
3326 gdk_window_move (tree_view->priv->drag_window, x, y);
3328 /* autoscroll, if needed */
3329 pspp_sheet_view_horizontal_autoscroll (tree_view);
3330 /* Update the current reorder position and arrow; */
3331 pspp_sheet_view_update_current_reorder (tree_view);
3337 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3339 remove_scroll_timeout (tree_view);
3340 gtk_grab_remove (GTK_WIDGET (tree_view));
3342 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3344 GtkTreePath *tmp_path;
3346 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3348 /* The anchor path should be set to the start path */
3349 tmp_path = _pspp_sheet_view_find_path (tree_view,
3350 tree_view->priv->rubber_band_start_node);
3352 if (tree_view->priv->anchor)
3353 gtk_tree_row_reference_free (tree_view->priv->anchor);
3355 tree_view->priv->anchor =
3356 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3357 tree_view->priv->model,
3360 gtk_tree_path_free (tmp_path);
3362 /* ... and the cursor to the end path */
3363 tmp_path = _pspp_sheet_view_find_path (tree_view,
3364 tree_view->priv->rubber_band_end_node);
3365 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE, 0); /* XXX mode? */
3366 gtk_tree_path_free (tmp_path);
3368 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3371 /* Clear status variables */
3372 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3373 tree_view->priv->rubber_band_shift = 0;
3374 tree_view->priv->rubber_band_ctrl = 0;
3376 tree_view->priv->rubber_band_start_node = -1;
3377 tree_view->priv->rubber_band_end_node = -1;
3381 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3385 gboolean skip_start,
3388 if (start_node == end_node)
3391 /* We skip the first node and jump inside the loop */
3397 /* Small optimization by assuming insensitive nodes are never
3402 if (tree_view->priv->rubber_band_shift)
3403 pspp_sheet_view_node_select (tree_view, start_node);
3404 else if (tree_view->priv->rubber_band_ctrl)
3406 /* Toggle the selection state */
3407 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3408 pspp_sheet_view_node_unselect (tree_view, start_node);
3410 pspp_sheet_view_node_select (tree_view, start_node);
3413 pspp_sheet_view_node_select (tree_view, start_node);
3417 /* Mirror the above */
3418 if (tree_view->priv->rubber_band_shift)
3419 pspp_sheet_view_node_unselect (tree_view, start_node);
3420 else if (tree_view->priv->rubber_band_ctrl)
3422 /* Toggle the selection state */
3423 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3424 pspp_sheet_view_node_unselect (tree_view, start_node);
3426 pspp_sheet_view_node_select (tree_view, start_node);
3429 pspp_sheet_view_node_unselect (tree_view, start_node);
3432 _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3434 if (start_node == end_node)
3439 start_node = pspp_sheet_view_node_next (tree_view, start_node);
3442 /* Ran out of tree */
3445 if (skip_end && start_node == end_node)
3452 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3455 return node * tree_view->priv->fixed_height;
3459 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3463 int fixed_height = tree_view->priv->fixed_height;
3464 if (fixed_height <= 0
3466 || height >= tree_view->priv->row_count * fixed_height)
3473 *new_node = height / fixed_height;
3474 return height % fixed_height;
3479 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3484 pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3485 pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3487 /* Handle the start area first */
3488 if (tree_view->priv->rubber_band_start_node < 0)
3490 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3497 else if (start_node < tree_view->priv->rubber_band_start_node)
3499 /* New node is above the old one; selection became bigger */
3500 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3502 tree_view->priv->rubber_band_start_node,
3507 else if (start_node > tree_view->priv->rubber_band_start_node)
3509 /* New node is below the old one; selection became smaller */
3510 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3511 tree_view->priv->rubber_band_start_node,
3518 tree_view->priv->rubber_band_start_node = start_node;
3520 /* Next, handle the end area */
3521 if (tree_view->priv->rubber_band_end_node < 0)
3523 /* In the event this happens, start_node was also -1; this case is
3527 else if (end_node < 0)
3529 /* Find the last node in the tree */
3530 pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3533 /* Selection reached end of the tree */
3534 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3535 tree_view->priv->rubber_band_end_node,
3541 else if (end_node > tree_view->priv->rubber_band_end_node)
3543 /* New node is below the old one; selection became bigger */
3544 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3545 tree_view->priv->rubber_band_end_node,
3551 else if (end_node < tree_view->priv->rubber_band_end_node)
3553 /* New node is above the old one; selection became smaller */
3554 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3556 tree_view->priv->rubber_band_end_node,
3562 tree_view->priv->rubber_band_end_node = end_node;
3565 #define GDK_RECTANGLE_PTR(X) ((GdkRectangle *)(X))
3568 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view, GdkDevice *device)
3571 cairo_rectangle_int_t old_area;
3572 cairo_rectangle_int_t new_area;
3573 cairo_rectangle_int_t common;
3574 cairo_region_t *invalid_region;
3575 PsppSheetViewColumn *column;
3577 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3578 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3579 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3580 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3582 gdk_device_get_position (device, NULL, &x, &y);
3585 y = MAX (y, 0) + tree_view->priv->dy;
3587 new_area.x = MIN (tree_view->priv->press_start_x, x);
3588 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3589 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3590 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3592 invalid_region = cairo_region_create_rectangle (&old_area);
3593 cairo_region_union_rectangle (invalid_region, &new_area);
3595 gdk_rectangle_intersect (GDK_RECTANGLE_PTR (&old_area),
3596 GDK_RECTANGLE_PTR (&new_area), GDK_RECTANGLE_PTR (&common));
3597 if (common.width > 2 && common.height > 2)
3599 cairo_region_t *common_region;
3601 /* make sure the border is invalidated */
3607 common_region = cairo_region_create_rectangle (&common);
3609 cairo_region_subtract (invalid_region, common_region);
3610 cairo_region_destroy (common_region);
3613 #if GTK_MAJOR_VERSION == 3
3614 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
3617 cairo_rectangle_int_t extents;
3619 cairo_region_get_extents (invalid_region, &extents);
3620 ereg = gdk_region_rectangle (GDK_RECTANGLE_PTR (&extents));
3621 gdk_window_invalidate_region (tree_view->priv->bin_window, ereg, TRUE);
3622 gdk_region_destroy (ereg);
3626 cairo_region_destroy (invalid_region);
3628 tree_view->priv->rubber_band_x = x;
3629 tree_view->priv->rubber_band_y = y;
3630 pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3632 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3633 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3634 tree_view->priv->anchor_column,
3637 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3639 pspp_sheet_view_update_rubber_band_selection (tree_view);
3644 pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view,
3649 GdkRectangle rubber_rect;
3653 rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3654 rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3655 rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3656 rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3658 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3661 cr = gdk_cairo_create (tree_view->priv->bin_window);
3662 cairo_set_line_width (cr, 1.0);
3664 style = gtk_widget_get_style (GTK_WIDGET (tree_view));
3665 cairo_set_source_rgba (cr,
3666 style->fg[GTK_STATE_NORMAL].red / 65535.,
3667 style->fg[GTK_STATE_NORMAL].green / 65535.,
3668 style->fg[GTK_STATE_NORMAL].blue / 65535.,
3671 gdk_cairo_rectangle (cr, &rect);
3675 cairo_set_source_rgb (cr,
3676 style->fg[GTK_STATE_NORMAL].red / 65535.,
3677 style->fg[GTK_STATE_NORMAL].green / 65535.,
3678 style->fg[GTK_STATE_NORMAL].blue / 65535.);
3680 cairo_rectangle (cr,
3681 rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3682 rubber_rect.width - 1, rubber_rect.height - 1);
3691 pspp_sheet_view_motion_bin_window (GtkWidget *widget,
3692 GdkEventMotion *event)
3694 PsppSheetView *tree_view;
3698 tree_view = (PsppSheetView *) widget;
3700 if (tree_view->priv->row_count == 0)
3703 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3705 GdkRectangle background_area, cell_area;
3706 PsppSheetViewColumn *column;
3708 if (find_click (tree_view, event->x, event->y, &node, &column,
3709 &background_area, &cell_area)
3710 && tree_view->priv->focus_column == column
3711 && tree_view->priv->press_start_node == node)
3714 gtk_grab_add (GTK_WIDGET (tree_view));
3715 pspp_sheet_view_update_rubber_band (tree_view, event->device);
3717 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3719 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3721 pspp_sheet_view_update_rubber_band (tree_view, event->device);
3722 tree_view->priv->drag_device = event->device;
3723 add_scroll_timeout (tree_view);
3726 /* only check for an initiated drag when a button is pressed */
3727 if (tree_view->priv->pressed_button >= 0
3728 && !tree_view->priv->rubber_band_status)
3729 pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3731 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3735 pspp_sheet_view_find_offset (tree_view, new_y, &node);
3737 tree_view->priv->event_last_x = event->x;
3738 tree_view->priv->event_last_y = event->y;
3740 prelight_or_select (tree_view, node, event->x, event->y);
3746 pspp_sheet_view_motion (GtkWidget *widget,
3747 GdkEventMotion *event)
3749 PsppSheetView *tree_view;
3751 tree_view = (PsppSheetView *) widget;
3753 /* Resizing a column */
3754 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3755 return pspp_sheet_view_motion_resize_column (widget, event);
3758 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3759 return pspp_sheet_view_motion_drag_column (widget, event);
3761 /* Sanity check it */
3762 if (event->window == tree_view->priv->bin_window)
3763 return pspp_sheet_view_motion_bin_window (widget, event);
3768 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3769 * the tree is empty.
3772 invalidate_empty_focus (PsppSheetView *tree_view)
3776 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3781 area.width = gdk_window_get_width (tree_view->priv->bin_window);
3782 area.height = gdk_window_get_height (tree_view->priv->bin_window);
3783 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3786 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3790 draw_empty_focus (PsppSheetView *tree_view)
3792 GtkWidget *widget = GTK_WIDGET (tree_view);
3794 cairo_t *cr = gdk_cairo_create (tree_view->priv->bin_window);
3796 if (!gtk_widget_has_focus (widget))
3799 w = gdk_window_get_width (tree_view->priv->bin_window);
3800 h = gdk_window_get_height (tree_view->priv->bin_window);
3806 gtk_paint_focus (gtk_widget_get_style (widget),
3808 gtk_widget_get_state (widget),
3816 pspp_sheet_view_draw_vertical_grid_lines (PsppSheetView *tree_view,
3818 gint n_visible_columns,
3822 GList *list = tree_view->priv->columns;
3826 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3827 && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3830 /* Only draw the lines for visible rows and columns */
3831 for (list = tree_view->priv->columns; list; list = list->next, i++)
3833 PsppSheetViewColumn *column = list->data;
3836 if (! column->visible)
3839 current_x += column->width;
3841 /* Generally the grid lines should fit within the column, but for the
3842 last visible column we put it just past the end of the column.
3843 (Otherwise horizontal grid lines sometimes stick out by one pixel.) */
3845 if (i != n_visible_columns - 1)
3848 cairo_set_line_width (cr, 1.0);
3849 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
3850 cairo_move_to (cr, x + 0.5, min_y);
3851 cairo_line_to (cr, x + 0.5, max_y - min_y);
3856 /* Warning: Very scary function.
3857 * Modify at your own risk
3859 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3860 * FIXME: It's not...
3863 pspp_sheet_view_draw_bin (GtkWidget *widget,
3866 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3871 int drag_highlight = -1;
3874 gint y_offset, cell_offset;
3876 GdkRectangle background_area;
3877 GdkRectangle cell_area;
3879 gint bin_window_width;
3880 gint bin_window_height;
3881 GtkTreePath *cursor_path;
3882 GtkTreePath *drag_dest_path;
3883 GList *first_column, *last_column;
3884 gint vertical_separator;
3885 gint horizontal_separator;
3886 gint focus_line_width;
3887 gboolean allow_rules;
3889 gint n_visible_columns;
3890 gint grid_line_width;
3891 gboolean row_ending_details;
3892 gboolean draw_vgrid_lines, draw_hgrid_lines;
3894 GtkStyleContext *context;
3895 context = gtk_widget_get_style_context (widget);
3898 GtkAllocation allocation;
3899 gtk_widget_get_allocation (widget, &allocation);
3901 GdkRectangle exposed_rect;
3902 gdk_cairo_get_clip_rectangle (cr, &exposed_rect);
3906 Zarea.height = allocation.height;
3908 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3910 gtk_widget_style_get (widget,
3911 "horizontal-separator", &horizontal_separator,
3912 "vertical-separator", &vertical_separator,
3913 "allow-rules", &allow_rules,
3914 "focus-line-width", &focus_line_width,
3915 "row-ending-details", &row_ending_details,
3918 if (tree_view->priv->row_count == 0)
3920 draw_empty_focus (tree_view);
3925 /* clip event->area to the visible area */
3926 if (Zarea.height < 0.5)
3930 validate_visible_area (tree_view);
3932 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, Zarea.y);
3936 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3938 gdk_window_get_width (tree_view->priv->bin_window);
3941 gdk_window_get_height (tree_view->priv->bin_window);
3944 if (tree_view->priv->height < bin_window_height)
3946 gtk_paint_flat_box (gtk_widget_get_style (widget),
3948 gtk_widget_get_state (widget),
3952 0, tree_view->priv->height,
3954 bin_window_height - tree_view->priv->height);
3960 /* find the path for the node */
3961 path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3962 gtk_tree_model_get_iter (tree_view->priv->model,
3965 gtk_tree_path_free (path);
3968 drag_dest_path = NULL;
3970 if (tree_view->priv->cursor)
3971 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3974 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3976 if (tree_view->priv->drag_dest_row)
3977 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3980 _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3984 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3985 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3987 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
3988 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3990 if (draw_vgrid_lines || draw_hgrid_lines)
3991 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
3993 n_visible_columns = 0;
3994 for (list = tree_view->priv->columns; list; list = list->next)
3996 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
3998 n_visible_columns ++;
4001 /* Find the last column */
4002 for (last_column = g_list_last (tree_view->priv->columns);
4003 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
4004 last_column = last_column->prev)
4008 for (first_column = g_list_first (tree_view->priv->columns);
4009 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
4010 first_column = first_column->next)
4013 /* Actually process the expose event. To do this, we want to
4014 * start at the first node of the event, and walk the tree in
4015 * order, drawing each successive node.
4022 gboolean is_first = FALSE;
4023 gboolean is_last = FALSE;
4024 gboolean done = FALSE;
4027 max_height = ROW_HEIGHT (tree_view);
4031 background_area.y = y_offset + Zarea.y;
4032 background_area.height = max_height;
4033 max_y = background_area.y + max_height;
4037 if (node == tree_view->priv->prelight_node)
4038 flags |= GTK_CELL_RENDERER_PRELIT;
4040 selected = pspp_sheet_view_node_is_selected (tree_view, node);
4044 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4046 list = (rtl ? list->prev : list->next))
4048 PsppSheetViewColumn *column = list->data;
4049 const gchar *detail = NULL;
4050 gboolean selected_column;
4053 if (!column->visible)
4056 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
4057 selected_column = column->selected && column->selectable;
4059 selected_column = TRUE;
4062 if (cell_offset > Zarea.x + Zarea.width ||
4063 cell_offset + column->width < Zarea.x)
4065 cell_offset += column->width;
4070 if (selected && selected_column)
4071 flags |= GTK_CELL_RENDERER_SELECTED;
4073 flags &= ~GTK_CELL_RENDERER_SELECTED;
4075 if (column->show_sort_indicator)
4076 flags |= GTK_CELL_RENDERER_SORTED;
4078 flags &= ~GTK_CELL_RENDERER_SORTED;
4081 flags |= GTK_CELL_RENDERER_FOCUSED;
4083 flags &= ~GTK_CELL_RENDERER_FOCUSED;
4085 background_area.x = cell_offset;
4086 background_area.width = column->width;
4088 cell_area = background_area;
4089 cell_area.y += vertical_separator / 2;
4090 cell_area.x += horizontal_separator / 2;
4091 cell_area.height -= vertical_separator;
4092 cell_area.width -= horizontal_separator;
4094 if (draw_vgrid_lines)
4096 if (list == first_column)
4098 cell_area.width -= grid_line_width / 2;
4100 else if (list == last_column)
4102 cell_area.x += grid_line_width / 2;
4103 cell_area.width -= grid_line_width / 2;
4107 cell_area.x += grid_line_width / 2;
4108 cell_area.width -= grid_line_width;
4112 if (draw_hgrid_lines)
4114 cell_area.y += grid_line_width / 2;
4115 cell_area.height -= grid_line_width;
4119 if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4121 if (!gdk_rectangle_intersect (&background_area, &exposed_rect, NULL))
4124 cell_offset += column->width;
4129 pspp_sheet_view_column_cell_set_cell_data (column,
4130 tree_view->priv->model,
4133 /* Select the detail for drawing the cell. relevant
4134 * factors are parity, sortedness, and whether to
4137 if (allow_rules && tree_view->priv->has_rules)
4139 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4140 n_visible_columns >= 3)
4143 detail = "cell_odd_ruled_sorted";
4145 detail = "cell_even_ruled_sorted";
4150 detail = "cell_odd_ruled";
4152 detail = "cell_even_ruled";
4157 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4158 n_visible_columns >= 3)
4161 detail = "cell_odd_sorted";
4163 detail = "cell_even_sorted";
4168 detail = "cell_odd";
4170 detail = "cell_even";
4176 gtk_style_context_save (context);
4177 state = gtk_cell_renderer_get_state (NULL, widget, flags);
4178 gtk_style_context_set_state (context, state);
4179 gtk_style_context_add_class (context, GTK_STYLE_CLASS_CELL);
4181 /* Draw background */
4182 gtk_render_background (context, cr,
4185 background_area.width,
4186 background_area.height);
4189 gtk_render_frame (context, cr,
4192 background_area.width,
4193 background_area.height);
4195 if (draw_hgrid_lines)
4197 cairo_set_line_width (cr, 1.0);
4198 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
4200 if (background_area.y >= 0)
4203 gdk_draw_line (event->window,
4204 tree_view->priv->grid_line_gc[widget->state],
4205 background_area.x, background_area.y,
4206 background_area.x + background_area.width,
4209 cairo_move_to (cr, background_area.x, background_area.y - 0.5);
4210 cairo_line_to (cr, background_area.x + background_area.width,
4211 background_area.y - 0.5);
4215 if (y_offset + max_height <= Zarea.height - 0.5)
4218 gdk_draw_line (event->window,
4219 tree_view->priv->grid_line_gc[widget->state],
4220 background_area.x, background_area.y + max_height,
4221 background_area.x + background_area.width,
4222 background_area.y + max_height);
4225 cairo_move_to (cr, background_area.x, background_area.y + max_height - 0.5);
4226 cairo_line_to (cr, background_area.x + background_area.width,
4227 background_area.y + max_height - 0.5);
4233 _pspp_sheet_view_column_cell_render (column,
4240 cell_offset += column->width;
4241 gtk_style_context_restore (context);
4244 if (node == drag_highlight)
4246 /* Draw indicator for the drop
4248 gint highlight_y = -1;
4252 switch (tree_view->priv->drag_dest_pos)
4254 case PSPP_SHEET_VIEW_DROP_BEFORE:
4255 highlight_y = background_area.y - 1;
4256 if (highlight_y < 0)
4260 case PSPP_SHEET_VIEW_DROP_AFTER:
4261 highlight_y = background_area.y + background_area.height - 1;
4264 case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE:
4265 case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER:
4266 _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node);
4270 width = gdk_window_get_width (tree_view->priv->bin_window);
4272 if (row_ending_details)
4273 gtk_paint_focus (gtk_widget_get_style (widget),
4275 gtk_widget_get_state (widget),
4278 ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4279 : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4280 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4281 - focus_line_width / 2,
4282 width, ROW_HEIGHT (tree_view)
4283 - focus_line_width + 1);
4285 gtk_paint_focus (gtk_widget_get_style (widget),
4287 gtk_widget_get_state (widget),
4289 "treeview-drop-indicator",
4290 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4291 - focus_line_width / 2,
4292 width, ROW_HEIGHT (tree_view)
4293 - focus_line_width + 1);
4298 if (highlight_y >= 0)
4300 gdk_draw_line (event->window,
4301 widget->style->fg_gc[gtk_widget_get_state (widget)],
4304 rtl ? 0 : bin_window_width,
4310 y_offset += max_height;
4314 node = pspp_sheet_view_node_next (tree_view, node);
4317 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
4321 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
4328 while (y_offset < Zarea.height);
4331 pspp_sheet_view_draw_vertical_grid_lines (tree_view, cr, n_visible_columns,
4335 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4337 GdkRectangle *rectangles;
4340 gdk_region_get_rectangles (event->region,
4344 while (n_rectangles--)
4345 pspp_sheet_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
4347 g_free (rectangles);
4352 gtk_tree_path_free (cursor_path);
4355 gtk_tree_path_free (drag_dest_path);
4362 pspp_sheet_view_draw (GtkWidget *widget,
4365 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4366 GtkStyleContext *context;
4368 context = gtk_widget_get_style_context (widget);
4370 if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window))
4375 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->bin_window);
4376 pspp_sheet_view_draw_bin (widget, cr);
4379 /* We can't just chain up to Container::expose as it will try to send the
4380 * event to the headers, so we handle propagating it to our children
4381 * (eg. widgets being edited) ourselves.
4383 tmp_list = tree_view->priv->children;
4386 PsppSheetViewChild *child = tmp_list->data;
4387 tmp_list = tmp_list->next;
4389 gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
4394 gtk_render_background (context, cr,
4396 gtk_widget_get_allocated_width (widget),
4397 gtk_widget_get_allocated_height (widget));
4400 gtk_style_context_save (context);
4401 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_VIEW);
4403 if (gtk_cairo_should_draw_window (cr, tree_view->priv->header_window))
4405 gint n_visible_columns;
4408 for (list = tree_view->priv->columns; list != NULL; list = list->next)
4410 PsppSheetViewColumn *column = list->data;
4412 if (column == tree_view->priv->drag_column || !column->visible)
4415 if (span_intersects (column->allocation.x, column->allocation.width,
4416 (int) gtk_adjustment_get_value (tree_view->priv->hadjustment),
4417 (int) gtk_widget_get_allocated_width (widget))
4418 && column->button != NULL)
4419 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4420 column->button, cr);
4423 n_visible_columns = 0;
4424 for (list = tree_view->priv->columns; list; list = list->next)
4426 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4428 n_visible_columns ++;
4431 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->header_window);
4432 pspp_sheet_view_draw_vertical_grid_lines (tree_view,
4436 TREE_VIEW_HEADER_HEIGHT (tree_view));
4439 if (tree_view->priv->drag_window &&
4440 gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window))
4442 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4443 tree_view->priv->drag_column->button,
4447 gtk_style_context_restore (context);
4459 /* returns 0x1 when no column has been found -- yes it's hackish */
4460 static PsppSheetViewColumn *
4461 pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
4462 PsppSheetViewColumn *column,
4465 PsppSheetViewColumn *left_column = NULL;
4466 PsppSheetViewColumn *cur_column = NULL;
4469 if (!column->reorderable)
4470 return (PsppSheetViewColumn *)0x1;
4472 switch (drop_position)
4475 /* find first column where we can drop */
4476 tmp_list = tree_view->priv->columns;
4477 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4478 return (PsppSheetViewColumn *)0x1;
4482 g_assert (tmp_list);
4484 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4485 tmp_list = tmp_list->next;
4487 if (left_column && left_column->visible == FALSE)
4490 if (!tree_view->priv->column_drop_func)
4493 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4495 left_column = cur_column;
4502 if (!tree_view->priv->column_drop_func)
4505 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4508 return (PsppSheetViewColumn *)0x1;
4512 /* find first column after column where we can drop */
4513 tmp_list = tree_view->priv->columns;
4515 for (; tmp_list; tmp_list = tmp_list->next)
4516 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4519 if (!tmp_list || !tmp_list->next)
4520 return (PsppSheetViewColumn *)0x1;
4522 tmp_list = tmp_list->next;
4523 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4524 tmp_list = tmp_list->next;
4528 g_assert (tmp_list);
4530 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4531 tmp_list = tmp_list->next;
4533 if (left_column && left_column->visible == FALSE)
4535 left_column = cur_column;
4537 tmp_list = tmp_list->next;
4541 if (!tree_view->priv->column_drop_func)
4544 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4546 left_column = cur_column;
4553 if (!tree_view->priv->column_drop_func)
4556 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4559 return (PsppSheetViewColumn *)0x1;
4563 /* find first column before column where we can drop */
4564 tmp_list = tree_view->priv->columns;
4566 for (; tmp_list; tmp_list = tmp_list->next)
4567 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4570 if (!tmp_list || !tmp_list->prev)
4571 return (PsppSheetViewColumn *)0x1;
4573 tmp_list = tmp_list->prev;
4574 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4575 tmp_list = tmp_list->prev;
4579 g_assert (tmp_list);
4581 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4583 if (left_column && !left_column->visible)
4585 /*if (!tmp_list->prev)
4586 return (PsppSheetViewColumn *)0x1;
4589 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4590 tmp_list = tmp_list->prev->prev;
4593 cur_column = left_column;
4595 tmp_list = tmp_list->prev;
4599 if (!tree_view->priv->column_drop_func)
4602 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4605 cur_column = left_column;
4606 tmp_list = tmp_list->prev;
4609 if (!tree_view->priv->column_drop_func)
4612 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4615 return (PsppSheetViewColumn *)0x1;
4619 /* same as DROP_HOME case, but doing it backwards */
4620 tmp_list = g_list_last (tree_view->priv->columns);
4623 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4624 return (PsppSheetViewColumn *)0x1;
4628 g_assert (tmp_list);
4630 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4632 if (left_column && !left_column->visible)
4634 cur_column = left_column;
4635 tmp_list = tmp_list->prev;
4638 if (!tree_view->priv->column_drop_func)
4641 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4644 cur_column = left_column;
4645 tmp_list = tmp_list->prev;
4648 if (!tree_view->priv->column_drop_func)
4651 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4654 return (PsppSheetViewColumn *)0x1;
4658 return (PsppSheetViewColumn *)0x1;
4662 pspp_sheet_view_key_press (GtkWidget *widget,
4665 PsppSheetView *tree_view = (PsppSheetView *) widget;
4667 if (tree_view->priv->rubber_band_status)
4669 if (event->keyval == GDK_Escape)
4670 pspp_sheet_view_stop_rubber_band (tree_view);
4675 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4677 if (event->keyval == GDK_Escape)
4679 tree_view->priv->cur_reorder = NULL;
4680 pspp_sheet_view_button_release_drag_column (widget, NULL);
4685 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4687 GList *focus_column;
4690 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4692 for (focus_column = tree_view->priv->columns;
4694 focus_column = focus_column->next)
4696 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4698 if (column->button && gtk_widget_has_focus (column->button))
4703 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4704 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4705 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4707 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4709 if (!column->resizable)
4711 gtk_widget_error_bell (widget);
4715 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4716 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4718 gint old_width = column->resized_width;
4720 column->resized_width = MAX (column->resized_width,
4722 column->resized_width -= 2;
4723 if (column->resized_width < 0)
4724 column->resized_width = 0;
4726 if (column->min_width == -1)
4727 column->resized_width = MAX (column->button_request,
4728 column->resized_width);
4730 column->resized_width = MAX (column->min_width,
4731 column->resized_width);
4733 if (column->max_width != -1)
4734 column->resized_width = MIN (column->resized_width,
4737 column->use_resized_width = TRUE;
4739 if (column->resized_width != old_width)
4740 gtk_widget_queue_resize (widget);
4742 gtk_widget_error_bell (widget);
4744 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4745 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4747 gint old_width = column->resized_width;
4749 column->resized_width = MAX (column->resized_width,
4751 column->resized_width += 2;
4753 if (column->max_width != -1)
4754 column->resized_width = MIN (column->resized_width,
4757 column->use_resized_width = TRUE;
4759 if (column->resized_width != old_width)
4760 gtk_widget_queue_resize (widget);
4762 gtk_widget_error_bell (widget);
4769 (event->state & GDK_MOD1_MASK) &&
4770 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4771 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4772 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4773 || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4775 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4777 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4778 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4780 PsppSheetViewColumn *col;
4781 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
4782 if (col != (PsppSheetViewColumn *)0x1)
4783 pspp_sheet_view_move_column_after (tree_view, column, col);
4785 gtk_widget_error_bell (widget);
4787 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4788 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4790 PsppSheetViewColumn *col;
4791 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4792 if (col != (PsppSheetViewColumn *)0x1)
4793 pspp_sheet_view_move_column_after (tree_view, column, col);
4795 gtk_widget_error_bell (widget);
4797 else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4799 PsppSheetViewColumn *col;
4800 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4801 if (col != (PsppSheetViewColumn *)0x1)
4802 pspp_sheet_view_move_column_after (tree_view, column, col);
4804 gtk_widget_error_bell (widget);
4806 else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4808 PsppSheetViewColumn *col;
4809 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4810 if (col != (PsppSheetViewColumn *)0x1)
4811 pspp_sheet_view_move_column_after (tree_view, column, col);
4813 gtk_widget_error_bell (widget);
4820 /* Chain up to the parent class. It handles the keybindings. */
4821 if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4824 if (tree_view->priv->search_entry_avoid_unhandled_binding)
4826 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4830 /* We pass the event to the search_entry. If its text changes, then we start
4831 * the typeahead find capabilities. */
4832 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4833 && tree_view->priv->enable_search
4834 && !tree_view->priv->search_custom_entry_set)
4836 GdkEvent *new_event;
4838 const char *new_text;
4841 gboolean text_modified;
4842 gulong popup_menu_id;
4844 pspp_sheet_view_ensure_interactive_directory (tree_view);
4846 /* Make a copy of the current text */
4847 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4848 new_event = gdk_event_copy ((GdkEvent *) event);
4849 g_object_unref (((GdkEventKey *) new_event)->window);
4850 ((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window));
4851 gtk_widget_realize (tree_view->priv->search_window);
4853 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
4854 "popup-menu", G_CALLBACK (gtk_true),
4857 /* Move the entry off screen */
4858 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4859 gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4860 gdk_screen_get_width (screen) + 1,
4861 gdk_screen_get_height (screen) + 1);
4862 gtk_widget_show (tree_view->priv->search_window);
4864 /* Send the event to the window. If the preedit_changed signal is emitted
4865 * during this event, we will set priv->imcontext_changed */
4866 tree_view->priv->imcontext_changed = FALSE;
4867 retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4868 gdk_event_free (new_event);
4869 gtk_widget_hide (tree_view->priv->search_window);
4871 g_signal_handler_disconnect (tree_view->priv->search_entry,
4874 /* We check to make sure that the entry tried to handle the text, and that
4875 * the text has changed.
4877 new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
4878 text_modified = strcmp (old_text, new_text) != 0;
4880 if (tree_view->priv->imcontext_changed || /* we're in a preedit */
4881 (retval && text_modified)) /* ...or the text was modified */
4883 if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
4885 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
4890 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
4900 pspp_sheet_view_key_release (GtkWidget *widget,
4903 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4905 if (tree_view->priv->rubber_band_status)
4908 return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
4911 /* FIXME Is this function necessary? Can I get an enter_notify event
4912 * w/o either an expose event or a mouse motion event?
4915 pspp_sheet_view_enter_notify (GtkWidget *widget,
4916 GdkEventCrossing *event)
4918 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4922 /* Sanity check it */
4923 if (event->window != tree_view->priv->bin_window)
4926 if (tree_view->priv->row_count == 0)
4929 if (event->mode == GDK_CROSSING_GRAB ||
4930 event->mode == GDK_CROSSING_GTK_GRAB ||
4931 event->mode == GDK_CROSSING_GTK_UNGRAB ||
4932 event->mode == GDK_CROSSING_STATE_CHANGED)
4935 /* find the node internally */
4936 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
4939 pspp_sheet_view_find_offset (tree_view, new_y, &node);
4941 tree_view->priv->event_last_x = event->x;
4942 tree_view->priv->event_last_y = event->y;
4944 prelight_or_select (tree_view, node, event->x, event->y);
4950 pspp_sheet_view_leave_notify (GtkWidget *widget,
4951 GdkEventCrossing *event)
4953 PsppSheetView *tree_view;
4955 if (event->mode == GDK_CROSSING_GRAB)
4958 tree_view = PSPP_SHEET_VIEW (widget);
4960 if (tree_view->priv->prelight_node >= 0)
4961 _pspp_sheet_view_queue_draw_node (tree_view,
4962 tree_view->priv->prelight_node,
4965 tree_view->priv->event_last_x = -10000;
4966 tree_view->priv->event_last_y = -10000;
4968 prelight_or_select (tree_view,
4970 -1000, -1000); /* coords not possibly over an arrow */
4977 pspp_sheet_view_focus_out (GtkWidget *widget,
4978 GdkEventFocus *event)
4980 PsppSheetView *tree_view;
4982 tree_view = PSPP_SHEET_VIEW (widget);
4984 gtk_widget_queue_draw (widget);
4986 /* destroy interactive search dialog */
4987 if (tree_view->priv->search_window)
4988 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
4994 /* Incremental Reflow
4998 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
5001 GtkAllocation allocation;
5002 gint y = pspp_sheet_view_node_find_offset (tree_view, node)
5003 - gtk_adjustment_get_value (tree_view->priv->vadjustment)
5004 + TREE_VIEW_HEADER_HEIGHT (tree_view);
5006 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5008 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
5011 tree_view->priv->fixed_height);
5015 node_is_visible (PsppSheetView *tree_view,
5021 y = pspp_sheet_view_node_find_offset (tree_view, node);
5022 height = ROW_HEIGHT (tree_view);
5024 if (y >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5025 y + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5026 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5032 /* Returns the row height. */
5034 validate_row (PsppSheetView *tree_view,
5039 PsppSheetViewColumn *column;
5040 GList *list, *first_column, *last_column;
5042 gint horizontal_separator;
5043 gint vertical_separator;
5044 gint focus_line_width;
5045 gboolean draw_vgrid_lines, draw_hgrid_lines;
5047 gint grid_line_width;
5048 gboolean wide_separators;
5049 gint separator_height;
5051 gtk_widget_style_get (GTK_WIDGET (tree_view),
5052 "focus-padding", &focus_pad,
5053 "focus-line-width", &focus_line_width,
5054 "horizontal-separator", &horizontal_separator,
5055 "vertical-separator", &vertical_separator,
5056 "grid-line-width", &grid_line_width,
5057 "wide-separators", &wide_separators,
5058 "separator-height", &separator_height,
5062 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5063 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5065 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5066 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5068 for (last_column = g_list_last (tree_view->priv->columns);
5069 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5070 last_column = last_column->prev)
5073 for (first_column = g_list_first (tree_view->priv->columns);
5074 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5075 first_column = first_column->next)
5078 for (list = tree_view->priv->columns; list; list = list->next)
5083 column = list->data;
5085 if (! column->visible)
5088 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5089 pspp_sheet_view_column_cell_get_size (column,
5091 &tmp_width, &tmp_height);
5093 tmp_height += vertical_separator;
5094 height = MAX (height, tmp_height);
5096 tmp_width = tmp_width + horizontal_separator;
5098 if (draw_vgrid_lines)
5100 if (list->data == first_column || list->data == last_column)
5101 tmp_width += grid_line_width / 2.0;
5103 tmp_width += grid_line_width;
5106 if (tmp_width > column->requested_width)
5107 column->requested_width = tmp_width;
5110 if (draw_hgrid_lines)
5111 height += grid_line_width;
5113 tree_view->priv->post_validation_flag = TRUE;
5119 validate_visible_area (PsppSheetView *tree_view)
5121 GtkTreePath *path = NULL;
5122 GtkTreePath *above_path = NULL;
5125 gboolean size_changed = FALSE;
5127 gint area_above = 0;
5128 gint area_below = 0;
5129 GtkAllocation allocation;
5131 if (tree_view->priv->row_count == 0)
5134 if (tree_view->priv->scroll_to_path == NULL)
5137 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5139 total_height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5141 if (total_height == 0)
5144 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5147 /* we are going to scroll, and will update dy */
5148 _pspp_sheet_view_find_node (tree_view, path, &node);
5149 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5151 if (tree_view->priv->scroll_to_use_align)
5153 gint height = ROW_HEIGHT (tree_view);
5154 area_above = (total_height - height) *
5155 tree_view->priv->scroll_to_row_align;
5156 area_below = total_height - area_above - height;
5157 area_above = MAX (area_above, 0);
5158 area_below = MAX (area_below, 0);
5163 * 1) row not visible
5167 gint height = ROW_HEIGHT (tree_view);
5169 dy = pspp_sheet_view_node_find_offset (tree_view, node);
5171 if (dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5172 dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5173 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5175 /* row visible: keep the row at the same position */
5176 area_above = dy - gtk_adjustment_get_value (tree_view->priv->vadjustment);
5177 area_below = (gtk_adjustment_get_value (tree_view->priv->vadjustment) +
5178 gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5183 /* row not visible */
5185 && dy + height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5187 /* row at the beginning -- fixed */
5189 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment)
5190 - area_above - height;
5192 else if (dy >= (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5193 gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5195 /* row at the end -- fixed */
5196 area_above = dy - (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5197 gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
5198 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) -
5199 area_above - height;
5203 area_above = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - height;
5209 /* row somewhere in the middle, bring it to the top
5213 area_below = total_height - height;
5219 /* the scroll to isn't valid; ignore it.
5222 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5223 tree_view->priv->scroll_to_path = NULL;
5227 above_path = gtk_tree_path_copy (path);
5229 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5230 * backwards is much slower then forward, as there is no iter_prev function.
5231 * We go forwards first in case we run out of tree. Then we go backwards to
5234 while (node >= 0 && area_below > 0)
5236 gboolean done = FALSE;
5239 node = pspp_sheet_view_node_next (tree_view, node);
5242 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5244 gtk_tree_path_next (path);
5247 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5257 area_below -= ROW_HEIGHT (tree_view);
5259 gtk_tree_path_free (path);
5261 /* If we ran out of tree, and have extra area_below left, we need to add it
5264 area_above += area_below;
5266 _pspp_sheet_view_find_node (tree_view, above_path, &node);
5268 /* We walk backwards */
5269 while (area_above > 0)
5271 node = pspp_sheet_view_node_prev (tree_view, node);
5273 /* Always find the new path in the tree. We cannot just assume
5274 * a gtk_tree_path_prev() is enough here, as there might be children
5275 * in between this node and the previous sibling node. If this
5276 * appears to be a performance hotspot in profiles, we can look into
5277 * intrigate logic for keeping path, node and iter in sync like
5278 * we do for forward walks. (Which will be hard because of the lacking
5285 gtk_tree_path_free (above_path);
5286 above_path = _pspp_sheet_view_find_path (tree_view, node);
5288 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5290 area_above -= ROW_HEIGHT (tree_view);
5293 /* set the dy here to scroll to the path,
5294 * and sync the top row accordingly
5296 pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5297 pspp_sheet_view_top_row_to_dy (tree_view);
5299 /* update width/height and queue a resize */
5302 GtkRequisition requisition;
5304 /* We temporarily guess a size, under the assumption that it will be the
5305 * same when we get our next size_allocate. If we don't do this, we'll be
5306 * in an inconsistent state if we call top_row_to_dy. */
5308 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5309 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5310 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5311 gtk_adjustment_changed (tree_view->priv->hadjustment);
5312 gtk_adjustment_changed (tree_view->priv->vadjustment);
5313 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5316 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5317 tree_view->priv->scroll_to_path = NULL;
5320 gtk_tree_path_free (above_path);
5322 if (tree_view->priv->scroll_to_column)
5324 tree_view->priv->scroll_to_column = NULL;
5326 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5330 initialize_fixed_height_mode (PsppSheetView *tree_view)
5332 if (!tree_view->priv->row_count)
5335 if (tree_view->priv->fixed_height_set)
5338 if (tree_view->priv->fixed_height < 0)
5345 path = _pspp_sheet_view_find_path (tree_view, node);
5346 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5348 tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5350 gtk_tree_path_free (path);
5352 g_object_notify (G_OBJECT (tree_view), "fixed-height");
5356 /* Our strategy for finding nodes to validate is a little convoluted. We find
5357 * the left-most uninvalidated node. We then try walking right, validating
5358 * nodes. Once we find a valid node, we repeat the previous process of finding
5359 * the first invalid node.
5363 validate_rows_handler (PsppSheetView *tree_view)
5365 initialize_fixed_height_mode (tree_view);
5366 if (tree_view->priv->validate_rows_timer)
5368 g_source_remove (tree_view->priv->validate_rows_timer);
5369 tree_view->priv->validate_rows_timer = 0;
5376 do_presize_handler (PsppSheetView *tree_view)
5378 GtkRequisition requisition;
5380 validate_visible_area (tree_view);
5381 tree_view->priv->presize_handler_timer = 0;
5383 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5386 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5388 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5389 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5390 gtk_adjustment_changed (tree_view->priv->hadjustment);
5391 gtk_adjustment_changed (tree_view->priv->vadjustment);
5392 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5398 presize_handler_callback (gpointer data)
5400 do_presize_handler (PSPP_SHEET_VIEW (data));
5406 install_presize_handler (PsppSheetView *tree_view)
5408 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5411 if (! tree_view->priv->presize_handler_timer)
5413 tree_view->priv->presize_handler_timer =
5414 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5416 if (! tree_view->priv->validate_rows_timer)
5418 tree_view->priv->validate_rows_timer =
5419 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5424 scroll_sync_handler (PsppSheetView *tree_view)
5426 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5427 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5428 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5429 pspp_sheet_view_top_row_to_dy (tree_view);
5431 pspp_sheet_view_dy_to_top_row (tree_view);
5433 tree_view->priv->scroll_sync_timer = 0;
5439 install_scroll_sync_handler (PsppSheetView *tree_view)
5441 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5444 if (!tree_view->priv->scroll_sync_timer)
5446 tree_view->priv->scroll_sync_timer =
5447 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5452 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5456 gtk_tree_row_reference_free (tree_view->priv->top_row);
5460 tree_view->priv->top_row = NULL;
5461 tree_view->priv->top_row_dy = 0;
5465 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5466 tree_view->priv->top_row_dy = offset;
5470 /* Always call this iff dy is in the visible range. If the tree is empty, then
5471 * it's set to be NULL, and top_row_dy is 0;
5474 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5480 if (tree_view->priv->row_count == 0)
5482 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5486 offset = pspp_sheet_view_find_offset (tree_view,
5487 tree_view->priv->dy,
5492 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5496 path = _pspp_sheet_view_find_path (tree_view, node);
5497 pspp_sheet_view_set_top_row (tree_view, path, offset);
5498 gtk_tree_path_free (path);
5504 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5510 /* Avoid recursive calls */
5511 if (tree_view->priv->in_top_row_to_dy)
5514 if (tree_view->priv->top_row)
5515 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5522 _pspp_sheet_view_find_node (tree_view, path, &node);
5525 gtk_tree_path_free (path);
5529 /* keep dy and set new toprow */
5530 gtk_tree_row_reference_free (tree_view->priv->top_row);
5531 tree_view->priv->top_row = NULL;
5532 tree_view->priv->top_row_dy = 0;
5533 /* DO NOT install the idle handler */
5534 pspp_sheet_view_dy_to_top_row (tree_view);
5538 if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5540 /* new top row -- do NOT install the idle handler */
5541 pspp_sheet_view_dy_to_top_row (tree_view);
5545 new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5546 new_dy += tree_view->priv->top_row_dy;
5548 if (new_dy + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
5549 new_dy = tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
5551 new_dy = MAX (0, new_dy);
5553 tree_view->priv->in_top_row_to_dy = TRUE;
5554 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5555 tree_view->priv->in_top_row_to_dy = FALSE;
5560 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5562 install_presize_handler (tree_view);
5568 set_source_row (GdkDragContext *context,
5569 GtkTreeModel *model,
5570 GtkTreePath *source_row)
5572 g_object_set_data_full (G_OBJECT (context),
5573 "gtk-tree-view-source-row",
5574 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5575 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5579 get_source_row (GdkDragContext *context)
5581 GtkTreeRowReference *ref =
5582 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5585 return gtk_tree_row_reference_get_path (ref);
5592 GtkTreeRowReference *dest_row;
5593 guint path_down_mode : 1;
5594 guint empty_view_drop : 1;
5595 guint drop_append_mode : 1;
5600 dest_row_free (gpointer data)
5602 DestRow *dr = (DestRow *)data;
5604 gtk_tree_row_reference_free (dr->dest_row);
5605 g_slice_free (DestRow, dr);
5609 set_dest_row (GdkDragContext *context,
5610 GtkTreeModel *model,
5611 GtkTreePath *dest_row,
5612 gboolean path_down_mode,
5613 gboolean empty_view_drop,
5614 gboolean drop_append_mode)
5620 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5625 dr = g_slice_new (DestRow);
5627 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5628 dr->path_down_mode = path_down_mode != FALSE;
5629 dr->empty_view_drop = empty_view_drop != FALSE;
5630 dr->drop_append_mode = drop_append_mode != FALSE;
5632 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5633 dr, (GDestroyNotify) dest_row_free);
5637 get_dest_row (GdkDragContext *context,
5638 gboolean *path_down_mode)
5641 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5645 GtkTreePath *path = NULL;
5648 *path_down_mode = dr->path_down_mode;
5651 path = gtk_tree_row_reference_get_path (dr->dest_row);
5652 else if (dr->empty_view_drop)
5653 path = gtk_tree_path_new_from_indices (0, -1);
5657 if (path && dr->drop_append_mode)
5658 gtk_tree_path_next (path);
5666 /* Get/set whether drag_motion requested the drag data and
5667 * drag_data_received should thus not actually insert the data,
5668 * since the data doesn't result from a drop.
5671 set_status_pending (GdkDragContext *context,
5672 GdkDragAction suggested_action)
5674 g_object_set_data (G_OBJECT (context),
5675 "gtk-tree-view-status-pending",
5676 GINT_TO_POINTER (suggested_action));
5679 static GdkDragAction
5680 get_status_pending (GdkDragContext *context)
5682 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5683 "gtk-tree-view-status-pending"));
5686 static TreeViewDragInfo*
5687 get_info (PsppSheetView *tree_view)
5689 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5693 destroy_info (TreeViewDragInfo *di)
5695 g_slice_free (TreeViewDragInfo, di);
5698 static TreeViewDragInfo*
5699 ensure_info (PsppSheetView *tree_view)
5701 TreeViewDragInfo *di;
5703 di = get_info (tree_view);
5707 di = g_slice_new0 (TreeViewDragInfo);
5709 g_object_set_data_full (G_OBJECT (tree_view),
5710 "gtk-tree-view-drag-info",
5712 (GDestroyNotify) destroy_info);
5719 remove_info (PsppSheetView *tree_view)
5721 g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5726 drag_scan_timeout (gpointer data)
5728 PsppSheetView *tree_view;
5730 GdkModifierType state;
5731 GtkTreePath *path = NULL;
5732 PsppSheetViewColumn *column = NULL;
5733 GdkRectangle visible_rect;
5735 GDK_THREADS_ENTER ();
5737 tree_view = PSPP_SHEET_VIEW (data);
5739 gdk_window_get_pointer (tree_view->priv->bin_window,
5742 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5744 /* See if we are near the edge. */
5745 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5746 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5747 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5748 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5750 pspp_sheet_view_get_path_at_pos (tree_view,
5751 tree_view->priv->bin_window,
5760 pspp_sheet_view_scroll_to_cell (tree_view,
5766 gtk_tree_path_free (path);
5770 GDK_THREADS_LEAVE ();
5777 add_scroll_timeout (PsppSheetView *tree_view)
5779 if (tree_view->priv->scroll_timeout == 0)
5781 tree_view->priv->scroll_timeout =
5782 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5787 remove_scroll_timeout (PsppSheetView *tree_view)
5789 if (tree_view->priv->scroll_timeout != 0)
5791 g_source_remove (tree_view->priv->scroll_timeout);
5792 tree_view->priv->scroll_timeout = 0;
5797 check_model_dnd (GtkTreeModel *model,
5798 GType required_iface,
5799 const gchar *signal)
5801 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5803 g_warning ("You must override the default '%s' handler "
5804 "on PsppSheetView when using models that don't support "
5805 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5806 "is to connect to '%s' and call "
5807 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5808 "the default handler from running. Look at the source code "
5809 "for the default handler in gtktreeview.c to get an idea what "
5810 "your handler should do. (gtktreeview.c is in the GTK source "
5811 "code.) If you're using GTK from a language other than C, "
5812 "there may be a more natural way to override default handlers, e.g. via derivation.",
5813 signal, g_type_name (required_iface), signal);
5821 scroll_row_timeout (gpointer data)
5823 PsppSheetView *tree_view = data;
5825 pspp_sheet_view_horizontal_autoscroll (tree_view);
5826 pspp_sheet_view_vertical_autoscroll (tree_view);
5828 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5830 pspp_sheet_view_update_rubber_band (tree_view, tree_view->priv->drag_device);
5836 /* Returns TRUE if event should not be propagated to parent widgets */
5838 set_destination_row (PsppSheetView *tree_view,
5839 GdkDragContext *context,
5840 /* coordinates relative to the widget */
5843 GdkDragAction *suggested_action,
5846 GtkTreePath *path = NULL;
5847 PsppSheetViewDropPosition pos;
5848 PsppSheetViewDropPosition old_pos;
5849 TreeViewDragInfo *di;
5851 GtkTreePath *old_dest_path = NULL;
5852 gboolean can_drop = FALSE;
5854 *suggested_action = 0;
5857 widget = GTK_WIDGET (tree_view);
5859 di = get_info (tree_view);
5861 if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5863 /* someone unset us as a drag dest, note that if
5864 * we return FALSE drag_leave isn't called
5867 pspp_sheet_view_set_drag_dest_row (tree_view,
5869 PSPP_SHEET_VIEW_DROP_BEFORE);
5871 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5873 return FALSE; /* no longer a drop site */
5876 *target = gtk_drag_dest_find_target (widget, context,
5877 gtk_drag_dest_get_target_list (widget));
5878 if (*target == GDK_NONE)
5883 if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
5889 GtkTreeModel *model;
5891 /* the row got dropped on empty space, let's setup a special case
5895 gtk_tree_path_free (path);
5897 model = pspp_sheet_view_get_model (tree_view);
5899 n_children = gtk_tree_model_iter_n_children (model, NULL);
5902 pos = PSPP_SHEET_VIEW_DROP_AFTER;
5903 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
5907 pos = PSPP_SHEET_VIEW_DROP_BEFORE;
5908 path = gtk_tree_path_new_from_indices (0, -1);
5918 /* If we left the current row's "open" zone, unset the timeout for
5921 pspp_sheet_view_get_drag_dest_row (tree_view,
5926 gtk_tree_path_free (old_dest_path);
5928 if (TRUE /* FIXME if the location droppable predicate */)
5936 GtkWidget *source_widget;
5938 *suggested_action = gdk_drag_context_get_suggested_action (context);
5939 source_widget = gtk_drag_get_source_widget (context);
5941 if (source_widget == widget)
5943 /* Default to MOVE, unless the user has
5944 * pressed ctrl or shift to affect available actions
5946 if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
5947 *suggested_action = GDK_ACTION_MOVE;
5950 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5955 /* can't drop here */
5956 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5958 PSPP_SHEET_VIEW_DROP_BEFORE);
5962 gtk_tree_path_free (path);
5968 get_logical_dest_row (PsppSheetView *tree_view,
5969 gboolean *path_down_mode,
5970 gboolean *drop_append_mode)
5972 /* adjust path to point to the row the drop goes in front of */
5973 GtkTreePath *path = NULL;
5974 PsppSheetViewDropPosition pos;
5976 g_return_val_if_fail (path_down_mode != NULL, NULL);
5977 g_return_val_if_fail (drop_append_mode != NULL, NULL);
5979 *path_down_mode = FALSE;
5980 *drop_append_mode = 0;
5982 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
5987 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
5989 else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
5990 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
5991 *path_down_mode = TRUE;
5995 GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
5997 g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
5999 if (!gtk_tree_model_get_iter (model, &iter, path) ||
6000 !gtk_tree_model_iter_next (model, &iter))
6001 *drop_append_mode = 1;
6004 *drop_append_mode = 0;
6005 gtk_tree_path_next (path);
6013 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
6014 GdkEventMotion *event)
6016 GtkWidget *widget = GTK_WIDGET (tree_view);
6017 GdkDragContext *context;
6018 TreeViewDragInfo *di;
6019 GtkTreePath *path = NULL;
6021 gint cell_x, cell_y;
6022 GtkTreeModel *model;
6023 gboolean retval = FALSE;
6025 di = get_info (tree_view);
6027 if (di == NULL || !di->source_set)
6030 if (tree_view->priv->pressed_button < 0)
6033 if (!gtk_drag_check_threshold (widget,
6034 tree_view->priv->press_start_x,
6035 tree_view->priv->press_start_y,
6036 event->x, event->y))
6039 model = pspp_sheet_view_get_model (tree_view);
6044 button = tree_view->priv->pressed_button;
6045 tree_view->priv->pressed_button = -1;
6047 pspp_sheet_view_get_path_at_pos (tree_view,
6048 tree_view->priv->press_start_x,
6049 tree_view->priv->press_start_y,
6058 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6059 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6063 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6066 /* Now we can begin the drag */
6070 context = gtk_drag_begin (widget,
6071 gtk_drag_source_get_target_list (widget),
6076 set_source_row (context, model, path);
6080 gtk_tree_path_free (path);
6088 pspp_sheet_view_drag_begin (GtkWidget *widget,
6089 GdkDragContext *context)
6092 PsppSheetView *tree_view;
6093 GtkTreePath *path = NULL;
6094 gint cell_x, cell_y;
6096 TreeViewDragInfo *di;
6098 tree_view = PSPP_SHEET_VIEW (widget);
6100 /* if the user uses a custom DND source impl, we don't set the icon here */
6101 di = get_info (tree_view);
6103 if (di == NULL || !di->source_set)
6106 pspp_sheet_view_get_path_at_pos (tree_view,
6107 tree_view->priv->press_start_x,
6108 tree_view->priv->press_start_y,
6114 g_return_if_fail (path != NULL);
6116 row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6119 gtk_drag_set_icon_pixmap (context,
6120 gdk_drawable_get_colormap (row_pix),
6123 /* the + 1 is for the black border in the icon */
6124 tree_view->priv->press_start_x + 1,
6127 g_object_unref (row_pix);
6128 gtk_tree_path_free (path);
6134 pspp_sheet_view_drag_end (GtkWidget *widget,
6135 GdkDragContext *context)
6140 /* Default signal implementations for the drag signals */
6142 pspp_sheet_view_drag_data_get (GtkWidget *widget,
6143 GdkDragContext *context,
6144 GtkSelectionData *selection_data,
6148 PsppSheetView *tree_view;
6149 GtkTreeModel *model;
6150 TreeViewDragInfo *di;
6151 GtkTreePath *source_row;
6153 tree_view = PSPP_SHEET_VIEW (widget);
6155 model = pspp_sheet_view_get_model (tree_view);
6160 di = get_info (PSPP_SHEET_VIEW (widget));
6165 source_row = get_source_row (context);
6167 if (source_row == NULL)
6170 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6171 * any model; for DragSource models there are some other targets
6175 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6176 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6181 /* If drag_data_get does nothing, try providing row data. */
6182 if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6184 gtk_tree_set_row_drag_data (selection_data,
6190 gtk_tree_path_free (source_row);
6195 pspp_sheet_view_drag_data_delete (GtkWidget *widget,
6196 GdkDragContext *context)
6198 TreeViewDragInfo *di;
6199 GtkTreeModel *model;
6200 PsppSheetView *tree_view;
6201 GtkTreePath *source_row;
6203 tree_view = PSPP_SHEET_VIEW (widget);
6204 model = pspp_sheet_view_get_model (tree_view);
6206 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6209 di = get_info (tree_view);
6214 source_row = get_source_row (context);
6216 if (source_row == NULL)
6219 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6222 gtk_tree_path_free (source_row);
6224 set_source_row (context, NULL, NULL);
6228 pspp_sheet_view_drag_leave (GtkWidget *widget,
6229 GdkDragContext *context,
6232 /* unset any highlight row */
6233 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6235 PSPP_SHEET_VIEW_DROP_BEFORE);
6237 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6242 pspp_sheet_view_drag_motion (GtkWidget *widget,
6243 GdkDragContext *context,
6244 /* coordinates relative to the widget */
6250 GtkTreePath *path = NULL;
6251 PsppSheetViewDropPosition pos;
6252 PsppSheetView *tree_view;
6253 GdkDragAction suggested_action = 0;
6256 tree_view = PSPP_SHEET_VIEW (widget);
6258 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6261 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6263 /* we only know this *after* set_desination_row */
6264 empty = tree_view->priv->empty_view_drop;
6266 if (path == NULL && !empty)
6268 /* Can't drop here. */
6269 gdk_drag_status (context, 0, time);
6273 if (tree_view->priv->open_dest_timeout == 0 &&
6274 (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6275 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6281 tree_view->priv->drag_device = gdk_drag_context_get_device (context);
6282 add_scroll_timeout (tree_view);
6285 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6287 /* Request data so we can use the source row when
6288 * determining whether to accept the drop
6290 set_status_pending (context, suggested_action);
6291 gtk_drag_get_data (widget, context, target, time);
6295 set_status_pending (context, 0);
6296 gdk_drag_status (context, suggested_action, time);
6301 gtk_tree_path_free (path);
6308 pspp_sheet_view_drag_drop (GtkWidget *widget,
6309 GdkDragContext *context,
6310 /* coordinates relative to the widget */
6315 PsppSheetView *tree_view;
6317 GdkDragAction suggested_action = 0;
6318 GdkAtom target = GDK_NONE;
6319 TreeViewDragInfo *di;
6320 GtkTreeModel *model;
6321 gboolean path_down_mode;
6322 gboolean drop_append_mode;
6324 tree_view = PSPP_SHEET_VIEW (widget);
6326 model = pspp_sheet_view_get_model (tree_view);
6328 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6330 di = get_info (tree_view);
6335 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6338 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6341 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6343 if (target != GDK_NONE && path != NULL)
6345 /* in case a motion had requested drag data, change things so we
6346 * treat drag data receives as a drop.
6348 set_status_pending (context, 0);
6349 set_dest_row (context, model, path,
6350 path_down_mode, tree_view->priv->empty_view_drop,
6355 gtk_tree_path_free (path);
6357 /* Unset this thing */
6358 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6360 PSPP_SHEET_VIEW_DROP_BEFORE);
6362 if (target != GDK_NONE)
6364 gtk_drag_get_data (widget, context, target, time);
6372 pspp_sheet_view_drag_data_received (GtkWidget *widget,
6373 GdkDragContext *context,
6374 /* coordinates relative to the widget */
6377 GtkSelectionData *selection_data,
6382 TreeViewDragInfo *di;
6383 gboolean accepted = FALSE;
6384 GtkTreeModel *model;
6385 PsppSheetView *tree_view;
6386 GtkTreePath *dest_row;
6387 GdkDragAction suggested_action;
6388 gboolean path_down_mode;
6389 gboolean drop_append_mode;
6391 tree_view = PSPP_SHEET_VIEW (widget);
6393 model = pspp_sheet_view_get_model (tree_view);
6395 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6398 di = get_info (tree_view);
6403 suggested_action = get_status_pending (context);
6405 if (suggested_action)
6407 /* We are getting this data due to a request in drag_motion,
6408 * rather than due to a request in drag_drop, so we are just
6409 * supposed to call drag_status, not actually paste in the
6412 path = get_logical_dest_row (tree_view, &path_down_mode,
6416 suggested_action = 0;
6417 else if (path_down_mode)
6418 gtk_tree_path_down (path);
6420 if (suggested_action)
6422 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6428 path_down_mode = FALSE;
6429 gtk_tree_path_up (path);
6431 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6434 suggested_action = 0;
6437 suggested_action = 0;
6441 gdk_drag_status (context, suggested_action, time);
6444 gtk_tree_path_free (path);
6446 /* If you can't drop, remove user drop indicator until the next motion */
6447 if (suggested_action == 0)
6448 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6450 PSPP_SHEET_VIEW_DROP_BEFORE);
6455 dest_row = get_dest_row (context, &path_down_mode);
6457 if (dest_row == NULL)
6460 if (gtk_selection_data_get_length (selection_data) >= 0)
6464 gtk_tree_path_down (dest_row);
6465 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6466 dest_row, selection_data))
6467 gtk_tree_path_up (dest_row);
6471 if (gtk_selection_data_get_length (selection_data) >= 0)
6473 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6479 gtk_drag_finish (context,
6481 (gdk_drag_context_get_actions (context) == GDK_ACTION_MOVE),
6484 if (gtk_tree_path_get_depth (dest_row) == 1
6485 && gtk_tree_path_get_indices (dest_row)[0] == 0)
6487 /* special special case drag to "0", scroll to first item */
6488 if (!tree_view->priv->scroll_to_path)
6489 pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6492 gtk_tree_path_free (dest_row);
6495 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6500 /* GtkContainer Methods
6505 pspp_sheet_view_remove (GtkContainer *container,
6508 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6509 PsppSheetViewChild *child = NULL;
6512 tmp_list = tree_view->priv->children;
6515 child = tmp_list->data;
6516 if (child->widget == widget)
6518 gtk_widget_unparent (widget);
6520 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6521 g_list_free_1 (tmp_list);
6522 g_slice_free (PsppSheetViewChild, child);
6526 tmp_list = tmp_list->next;
6529 tmp_list = tree_view->priv->columns;
6533 PsppSheetViewColumn *column;
6534 column = tmp_list->data;
6535 if (column->button == widget)
6537 gtk_widget_unparent (widget);
6540 tmp_list = tmp_list->next;
6545 pspp_sheet_view_forall (GtkContainer *container,
6546 gboolean include_internals,
6547 GtkCallback callback,
6548 gpointer callback_data)
6550 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6551 PsppSheetViewChild *child = NULL;
6552 PsppSheetViewColumn *column;
6555 tmp_list = tree_view->priv->children;
6558 child = tmp_list->data;
6559 tmp_list = tmp_list->next;
6561 (* callback) (child->widget, callback_data);
6563 if (include_internals == FALSE)
6566 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6568 column = tmp_list->data;
6571 (* callback) (column->button, callback_data);
6575 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6576 * cells. If so we draw one big row-spanning focus rectangle.
6579 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6583 if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6584 return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6586 for (list = tree_view->priv->columns; list; list = list->next)
6588 if (!((PsppSheetViewColumn *)list->data)->visible)
6590 if (_pspp_sheet_view_column_count_special_cells (list->data))
6598 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6599 PsppSheetViewColumn *focus_column,
6600 gboolean clamp_column_visible)
6602 g_return_if_fail (focus_column != NULL);
6604 tree_view->priv->focus_column = focus_column;
6606 if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view)) != focus_column->button)
6607 gtk_widget_grab_focus (focus_column->button);
6609 if (clamp_column_visible)
6610 pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6613 /* Returns TRUE if the focus is within the headers, after the focus operation is
6617 pspp_sheet_view_header_focus (PsppSheetView *tree_view,
6618 GtkDirectionType dir,
6619 gboolean clamp_column_visible)
6621 GtkWidget *focus_child;
6622 PsppSheetViewColumn *focus_column;
6623 GList *last_column, *first_column;
6627 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6630 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
6632 first_column = tree_view->priv->columns;
6633 while (first_column)
6635 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6637 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6639 first_column = first_column->next;
6642 /* No headers are visible, or are focusable. We can't focus in or out.
6644 if (first_column == NULL)
6647 last_column = g_list_last (tree_view->priv->columns);
6650 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6652 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6654 last_column = last_column->prev;
6658 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6662 case GTK_DIR_TAB_BACKWARD:
6663 case GTK_DIR_TAB_FORWARD:
6666 if (focus_child == NULL)
6668 if (tree_view->priv->focus_column != NULL &&
6669 pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6670 focus_column = tree_view->priv->focus_column;
6672 focus_column = first_column->data;
6673 pspp_sheet_view_focus_column (tree_view, focus_column,
6674 clamp_column_visible);
6681 if (focus_child == NULL)
6683 if (tree_view->priv->focus_column != NULL)
6684 focus_column = tree_view->priv->focus_column;
6685 else if (dir == GTK_DIR_LEFT)
6686 focus_column = last_column->data;
6688 focus_column = first_column->data;
6689 pspp_sheet_view_focus_column (tree_view, focus_column,
6690 clamp_column_visible);
6694 if (gtk_widget_child_focus (focus_child, dir))
6696 /* The focus moves inside the button. */
6697 /* This is probably a great example of bad UI */
6698 if (clamp_column_visible)
6699 pspp_sheet_view_clamp_column_visible (tree_view,
6700 tree_view->priv->focus_column,
6705 /* We need to move the focus among the row of buttons. */
6706 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6707 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6710 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6711 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6713 gtk_widget_error_bell (GTK_WIDGET (tree_view));
6719 PsppSheetViewColumn *column;
6721 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6722 tmp_list = tmp_list->next;
6724 tmp_list = tmp_list->prev;
6726 if (tmp_list == NULL)
6728 g_warning ("Internal button not found");
6731 column = tmp_list->data;
6732 if (column->visible &&
6733 pspp_sheet_view_column_can_focus (column))
6737 pspp_sheet_view_focus_column (tree_view, column,
6738 clamp_column_visible);
6746 g_assert_not_reached ();
6753 /* This function returns in 'path' the first focusable path, if the given path
6754 * is already focusable, it's the returned one.
6758 search_first_focusable_path (PsppSheetView *tree_view,
6760 gboolean search_forward,
6763 /* XXX this function is trivial given that the sheetview doesn't support
6767 if (!path || !*path)
6770 _pspp_sheet_view_find_node (tree_view, *path, &node);
6778 return (*path != NULL);
6782 pspp_sheet_view_focus (GtkWidget *widget,
6783 GtkDirectionType direction)
6785 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6786 GtkContainer *container = GTK_CONTAINER (widget);
6787 GtkWidget *focus_child;
6789 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6792 focus_child = gtk_container_get_focus_child (container);
6794 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6795 /* Case 1. Headers currently have focus. */
6802 pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6804 case GTK_DIR_TAB_BACKWARD:
6807 case GTK_DIR_TAB_FORWARD:
6809 gtk_widget_grab_focus (widget);
6812 g_assert_not_reached ();
6817 /* Case 2. We don't have focus at all. */
6818 if (!gtk_widget_has_focus (widget))
6820 if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6821 gtk_widget_grab_focus (widget);
6825 /* Case 3. We have focus already. */
6826 if (direction == GTK_DIR_TAB_BACKWARD)
6827 return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6828 else if (direction == GTK_DIR_TAB_FORWARD)
6831 /* Other directions caught by the keybindings */
6832 gtk_widget_grab_focus (widget);
6837 pspp_sheet_view_grab_focus (GtkWidget *widget)
6839 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6841 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6845 pspp_sheet_view_style_updated (GtkWidget *widget)
6847 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6849 PsppSheetViewColumn *column;
6850 GtkStyleContext *context;
6852 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->style_updated (widget);
6854 if (gtk_widget_get_realized (widget))
6856 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
6857 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
6858 gtk_style_context_set_background (context, tree_view->priv->header_window);
6859 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6862 gtk_widget_style_get (widget,
6863 "expander-size", &tree_view->priv->expander_size,
6865 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6867 for (list = tree_view->priv->columns; list; list = list->next)
6869 column = list->data;
6870 _pspp_sheet_view_column_cell_set_dirty (column);
6873 tree_view->priv->fixed_height = -1;
6875 /* Invalidate cached button style. */
6876 if (tree_view->priv->button_style)
6878 g_object_unref (tree_view->priv->button_style);
6879 tree_view->priv->button_style = NULL;
6882 gtk_widget_queue_resize (widget);
6887 pspp_sheet_view_set_focus_child (GtkContainer *container,
6890 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6893 for (list = tree_view->priv->columns; list; list = list->next)
6895 if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
6897 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
6902 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
6906 pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
6907 GtkAdjustment *hadj,
6908 GtkAdjustment *vadj)
6910 gboolean need_adjust = FALSE;
6912 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
6915 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
6917 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6919 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
6921 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6923 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
6925 g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
6926 pspp_sheet_view_adjustment_changed,
6928 g_object_unref (tree_view->priv->hadjustment);
6931 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
6933 g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
6934 pspp_sheet_view_adjustment_changed,
6936 g_object_unref (tree_view->priv->vadjustment);
6939 if (tree_view->priv->hadjustment != hadj)
6941 tree_view->priv->hadjustment = hadj;
6942 g_object_ref_sink (tree_view->priv->hadjustment);
6944 g_signal_connect (tree_view->priv->hadjustment, "value-changed",
6945 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6950 if (tree_view->priv->vadjustment != vadj)
6952 tree_view->priv->vadjustment = vadj;
6953 g_object_ref_sink (tree_view->priv->vadjustment);
6955 g_signal_connect (tree_view->priv->vadjustment, "value-changed",
6956 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6962 pspp_sheet_view_adjustment_changed (NULL, tree_view);
6967 pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
6968 GtkMovementStep step,
6971 PsppSheetSelectMode mode;
6972 GdkModifierType state;
6974 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
6975 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
6976 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
6977 step == GTK_MOVEMENT_DISPLAY_LINES ||
6978 step == GTK_MOVEMENT_PAGES ||
6979 step == GTK_MOVEMENT_BUFFER_ENDS ||
6980 step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
6982 if (tree_view->priv->row_count == 0)
6984 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
6987 pspp_sheet_view_stop_editing (tree_view, FALSE);
6988 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
6989 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
6992 if (gtk_get_current_event_state (&state))
6994 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
6995 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
6996 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
6997 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
6999 /* else we assume not pressed */
7003 case GTK_MOVEMENT_LOGICAL_POSITIONS:
7004 pspp_sheet_view_move_cursor_tab (tree_view, count);
7006 case GTK_MOVEMENT_VISUAL_POSITIONS:
7007 pspp_sheet_view_move_cursor_left_right (tree_view, count, mode);
7009 case GTK_MOVEMENT_DISPLAY_LINES:
7010 pspp_sheet_view_move_cursor_up_down (tree_view, count, mode);
7012 case GTK_MOVEMENT_PAGES:
7013 pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode);
7015 case GTK_MOVEMENT_BUFFER_ENDS:
7016 pspp_sheet_view_move_cursor_start_end (tree_view, count, mode);
7018 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7019 pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode);
7022 g_assert_not_reached ();
7029 pspp_sheet_view_put (PsppSheetView *tree_view,
7030 GtkWidget *child_widget,
7032 PsppSheetViewColumn *column)
7034 PsppSheetViewChild *child;
7036 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7037 g_return_if_fail (GTK_IS_WIDGET (child_widget));
7039 child = g_slice_new (PsppSheetViewChild);
7041 child->widget = child_widget;
7042 _pspp_sheet_view_find_node (tree_view, path, &child->node);
7043 if (child->node < 0)
7045 g_assert_not_reached ();
7047 child->column = column;
7049 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7051 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7052 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7054 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7057 /* TreeModel Callbacks
7061 pspp_sheet_view_row_changed (GtkTreeModel *model,
7066 PsppSheetView *tree_view = (PsppSheetView *)data;
7068 gboolean free_path = FALSE;
7069 GtkTreePath *cursor_path;
7071 g_return_if_fail (path != NULL || iter != NULL);
7073 if (tree_view->priv->cursor != NULL)
7074 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7078 if (tree_view->priv->edited_column &&
7079 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7080 pspp_sheet_view_stop_editing (tree_view, TRUE);
7082 if (cursor_path != NULL)
7083 gtk_tree_path_free (cursor_path);
7087 path = gtk_tree_model_get_path (model, iter);
7090 else if (iter == NULL)
7091 gtk_tree_model_get_iter (model, iter, path);
7093 _pspp_sheet_view_find_node (tree_view,
7099 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7100 pspp_sheet_view_node_queue_redraw (tree_view, node);
7104 gtk_tree_path_free (path);
7108 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7113 PsppSheetView *tree_view = (PsppSheetView *) data;
7116 gint height = tree_view->priv->fixed_height;
7117 gboolean free_path = FALSE;
7118 gboolean node_visible = TRUE;
7120 g_return_if_fail (path != NULL || iter != NULL);
7124 path = gtk_tree_model_get_path (model, iter);
7127 else if (iter == NULL)
7128 gtk_tree_model_get_iter (model, iter, path);
7130 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7132 /* Update all row-references */
7133 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7134 indices = gtk_tree_path_get_indices (path);
7135 tmpnode = indices[0];
7137 range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7141 if (node_visible && node_is_visible (tree_view, tmpnode))
7142 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7144 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7147 install_presize_handler (tree_view);
7149 gtk_tree_path_free (path);
7153 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7157 PsppSheetView *tree_view = (PsppSheetView *)data;
7160 g_return_if_fail (path != NULL);
7162 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7164 _pspp_sheet_view_find_node (tree_view, path, &node);
7169 range_tower_delete (tree_view->priv->selected, node, 1);
7171 /* Ensure we don't have a dangling pointer to a dead node */
7172 ensure_unprelighted (tree_view);
7174 /* Cancel editting if we've started */
7175 pspp_sheet_view_stop_editing (tree_view, TRUE);
7177 if (tree_view->priv->destroy_count_func)
7179 gint child_count = 0;
7180 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7183 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7185 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7187 gtk_tree_row_reference_free (tree_view->priv->top_row);
7188 tree_view->priv->top_row = NULL;
7191 install_scroll_sync_handler (tree_view);
7193 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7196 if (helper_data.changed)
7197 g_signal_emit_by_name (tree_view->priv->selection, "changed");
7202 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7203 GtkTreePath *parent,
7208 PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7211 /* XXX need to adjust selection */
7212 len = gtk_tree_model_iter_n_children (model, iter);
7217 gtk_tree_row_reference_reordered (G_OBJECT (data),
7222 if (gtk_tree_path_get_depth (parent) != 0)
7225 if (tree_view->priv->edited_column)
7226 pspp_sheet_view_stop_editing (tree_view, TRUE);
7228 /* we need to be unprelighted */
7229 ensure_unprelighted (tree_view);
7231 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7233 pspp_sheet_view_dy_to_top_row (tree_view);
7237 /* Internal tree functions
7242 pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view,
7243 PsppSheetViewColumn *column,
7247 PsppSheetViewColumn *tmp_column = NULL;
7258 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7261 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7263 list = (rtl ? list->prev : list->next))
7265 tmp_column = list->data;
7267 if (tmp_column == column)
7270 if (tmp_column->visible)
7271 total_width += tmp_column->width;
7274 if (tmp_column != column)
7276 g_warning (G_STRLOC": passed-in column isn't in the tree");
7285 if (column->visible)
7286 *x2 = total_width + column->width;
7288 *x2 = total_width; /* width of 0 */
7292 /* Make sure the node is visible vertically */
7294 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7297 gint node_dy, height;
7298 GtkTreePath *path = NULL;
7300 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7303 /* just return if the node is visible, avoiding a costly expose */
7304 node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7305 height = ROW_HEIGHT (tree_view);
7306 if (node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment)
7307 && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
7308 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
7311 path = _pspp_sheet_view_find_path (tree_view, node);
7314 /* We process updates because we want to clear old selected items when we scroll.
7315 * if this is removed, we get a "selection streak" at the bottom. */
7316 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7317 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7318 gtk_tree_path_free (path);
7323 pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
7324 PsppSheetViewColumn *column,
7325 gboolean focus_to_cell)
7332 x = column->allocation.x;
7333 width = column->allocation.width;
7335 if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7337 /* The column is larger than the horizontal page size. If the
7338 * column has cells which can be focussed individually, then we make
7339 * sure the cell which gets focus is fully visible (if even the
7340 * focus cell is bigger than the page size, we make sure the
7341 * left-hand side of the cell is visible).
7343 * If the column does not have those so-called special cells, we
7344 * make sure the left-hand side of the column is visible.
7347 if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7349 GtkTreePath *cursor_path;
7350 GdkRectangle background_area, cell_area, focus_area;
7352 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7354 pspp_sheet_view_get_cell_area (tree_view,
7355 cursor_path, column, &cell_area);
7356 pspp_sheet_view_get_background_area (tree_view,
7357 cursor_path, column,
7360 gtk_tree_path_free (cursor_path);
7362 _pspp_sheet_view_column_get_focus_area (column,
7368 width = focus_area.width;
7370 if (width < 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);
7380 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7382 gtk_adjustment_get_lower (tree_view->priv->hadjustment),
7383 gtk_adjustment_get_upper (tree_view->priv->hadjustment)
7384 - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)));
7388 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7389 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7390 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7391 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7392 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7397 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7402 path = gtk_tree_path_new ();
7404 gtk_tree_path_append_index (path, node);
7409 _pspp_sheet_view_find_node (PsppSheetView *tree_view,
7413 gint *indices = gtk_tree_path_get_indices (path);
7414 gint depth = gtk_tree_path_get_depth (path);
7417 if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7423 pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
7426 gboolean add_shifted_binding,
7427 GtkMovementStep step,
7431 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7436 if (add_shifted_binding)
7437 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7442 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7445 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7450 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7457 pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view,
7458 PsppSheetViewColumn *column)
7460 PsppSheetViewColumn *left_column;
7461 PsppSheetViewColumn *cur_column = NULL;
7462 PsppSheetViewColumnReorder *reorder;
7467 /* We want to precalculate the motion list such that we know what column slots
7471 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7473 /* First, identify all possible drop spots */
7475 tmp_list = g_list_last (tree_view->priv->columns);
7477 tmp_list = g_list_first (tree_view->priv->columns);
7481 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7482 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7484 if (cur_column->visible == FALSE)
7487 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7488 if (left_column != column && cur_column != column &&
7489 tree_view->priv->column_drop_func &&
7490 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7492 left_column = cur_column;
7495 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7496 reorder->left_column = left_column;
7497 left_column = reorder->right_column = cur_column;
7499 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7502 /* Add the last one */
7503 if (tree_view->priv->column_drop_func == NULL ||
7504 ((left_column != column) &&
7505 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7507 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7508 reorder->left_column = left_column;
7509 reorder->right_column = NULL;
7510 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7513 /* We quickly check to see if it even makes sense to reorder columns. */
7514 /* If there is nothing that can be moved, then we return */
7516 if (tree_view->priv->column_drag_info == NULL)
7519 /* We know there are always 2 slots possbile, as you can always return column. */
7520 /* If that's all there is, return */
7521 if (tree_view->priv->column_drag_info->next == NULL ||
7522 (tree_view->priv->column_drag_info->next->next == NULL &&
7523 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7524 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7526 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7527 g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7528 g_list_free (tree_view->priv->column_drag_info);
7529 tree_view->priv->column_drag_info = NULL;
7532 /* We fill in the ranges for the columns, now that we've isolated them */
7533 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7535 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7537 reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7539 reorder->left_align = left;
7540 if (tmp_list->next != NULL)
7542 g_assert (tmp_list->next->data);
7543 left = reorder->right_align = (reorder->right_column->allocation.x +
7544 reorder->right_column->allocation.width +
7545 ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7549 gint width = gdk_window_get_width (tree_view->priv->header_window);
7550 reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7556 _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view,
7557 PsppSheetViewColumn *column)
7559 GdkEvent *send_event;
7560 GtkAllocation allocation;
7562 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7563 GdkDisplay *display = gdk_screen_get_display (screen);
7565 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7566 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7567 g_return_if_fail (column->button);
7569 pspp_sheet_view_set_column_drag_info (tree_view, column);
7571 if (tree_view->priv->column_drag_info == NULL)
7574 if (tree_view->priv->drag_window == NULL)
7576 GdkWindowAttr attributes;
7577 guint attributes_mask;
7579 attributes.window_type = GDK_WINDOW_CHILD;
7580 attributes.wclass = GDK_INPUT_OUTPUT;
7581 attributes.x = column->allocation.x;
7583 attributes.width = column->allocation.width;
7584 attributes.height = column->allocation.height;
7585 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7586 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7587 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL ;
7589 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7592 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7595 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7596 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7598 gtk_grab_remove (column->button);
7600 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7601 send_event->crossing.send_event = TRUE;
7602 send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button)));
7603 send_event->crossing.subwindow = NULL;
7604 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7605 send_event->crossing.time = GDK_CURRENT_TIME;
7607 gtk_propagate_event (column->button, send_event);
7608 gdk_event_free (send_event);
7610 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7611 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7612 send_event->button.send_event = TRUE;
7613 send_event->button.time = GDK_CURRENT_TIME;
7614 send_event->button.x = -1;
7615 send_event->button.y = -1;
7616 send_event->button.axes = NULL;
7617 send_event->button.state = 0;
7618 send_event->button.button = 1;
7619 send_event->button.device =
7620 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
7622 send_event->button.x_root = 0;
7623 send_event->button.y_root = 0;
7625 gtk_propagate_event (column->button, send_event);
7626 gdk_event_free (send_event);
7628 /* Kids, don't try this at home */
7629 g_object_ref (column->button);
7630 gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7631 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7632 gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7633 g_object_unref (column->button);
7635 tree_view->priv->drag_column_x = column->allocation.x;
7636 allocation = column->allocation;
7638 gtk_widget_size_allocate (column->button, &allocation);
7639 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7641 tree_view->priv->drag_column = column;
7642 gdk_window_show (tree_view->priv->drag_window);
7644 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7646 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7647 while (gtk_events_pending ())
7648 gtk_main_iteration ();
7650 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7651 gdk_pointer_grab (tree_view->priv->drag_window,
7653 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7654 NULL, NULL, GDK_CURRENT_TIME);
7655 gdk_keyboard_grab (tree_view->priv->drag_window,
7661 _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view,
7663 const GdkRectangle *clip_rect)
7666 GtkAllocation allocation;
7668 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7671 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
7673 rect.width = MAX (tree_view->priv->width, allocation.width);
7675 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7676 rect.height = ROW_HEIGHT (tree_view);
7680 GdkRectangle new_rect;
7682 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7684 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7688 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7693 pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
7695 const GdkRectangle *clip_rect)
7699 _pspp_sheet_view_find_node (tree_view, path, &node);
7702 _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7706 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7709 GtkTreePath *cursor_path;
7711 if ((tree_view->priv->row_count == 0) ||
7712 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7716 if (tree_view->priv->cursor)
7717 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7719 if (cursor_path == NULL)
7721 /* There's no cursor. Move the cursor to the first selected row, if any
7722 * are selected, otherwise to the first row in the sheetview.
7724 GList *selected_rows;
7725 GtkTreeModel *model;
7726 PsppSheetSelection *selection;
7728 selection = pspp_sheet_view_get_selection (tree_view);
7729 selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7733 /* XXX we could avoid doing O(n) work to get this result */
7734 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7735 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7736 g_list_free (selected_rows);
7740 cursor_path = gtk_tree_path_new_first ();
7741 search_first_focusable_path (tree_view, &cursor_path,
7745 gtk_tree_row_reference_free (tree_view->priv->cursor);
7746 tree_view->priv->cursor = NULL;
7750 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7751 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7752 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0);
7754 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0);
7760 /* Now find a column for the cursor. */
7761 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7763 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7764 gtk_tree_path_free (cursor_path);
7766 if (tree_view->priv->focus_column == NULL)
7769 for (list = tree_view->priv->columns; list; list = list->next)
7771 if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7773 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7774 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7775 pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7785 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7787 PsppSheetSelectMode mode)
7789 gint selection_count;
7790 int cursor_node = -1;
7791 int new_cursor_node = -1;
7792 GtkTreePath *cursor_path = NULL;
7793 gboolean grab_focus = TRUE;
7795 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7799 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7800 /* FIXME: we lost the cursor; should we get the first? */
7803 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7804 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7806 if (cursor_node < 0)
7807 /* FIXME: we lost the cursor; should we get the first? */
7810 selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7812 if (selection_count == 0
7813 && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7814 && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
7816 /* Don't move the cursor, but just select the current node */
7817 new_cursor_node = cursor_node;
7822 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7824 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7827 gtk_tree_path_free (cursor_path);
7829 if (new_cursor_node)
7831 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7833 search_first_focusable_path (tree_view, &cursor_path,
7838 gtk_tree_path_free (cursor_path);
7842 * If the list has only one item and multi-selection is set then select
7843 * the row (if not yet selected).
7845 if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7846 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7847 new_cursor_node < 0)
7850 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7852 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7854 if (new_cursor_node < 0
7855 && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
7857 new_cursor_node = cursor_node;
7861 new_cursor_node = -1;
7865 if (new_cursor_node >= 0)
7867 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7868 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode);
7869 gtk_tree_path_free (cursor_path);
7873 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7875 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
7877 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
7879 GTK_DIR_UP : GTK_DIR_DOWN))
7881 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
7884 gtk_widget_child_focus (toplevel,
7886 GTK_DIR_TAB_BACKWARD :
7887 GTK_DIR_TAB_FORWARD);
7894 gtk_widget_error_bell (GTK_WIDGET (tree_view));
7899 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7901 return new_cursor_node >= 0;
7905 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
7907 PsppSheetSelectMode mode)
7909 int cursor_node = -1;
7910 GtkTreePath *old_cursor_path = NULL;
7911 GtkTreePath *cursor_path = NULL;
7912 int start_cursor_node = -1;
7915 gint vertical_separator;
7917 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7920 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
7921 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7923 /* This is sorta weird. Focus in should give us a cursor */
7926 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
7927 _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
7929 if (cursor_node < 0)
7931 /* FIXME: we lost the cursor. Should we try to get one? */
7932 gtk_tree_path_free (old_cursor_path);
7936 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7937 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
7938 y += tree_view->priv->cursor_offset;
7939 y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment);
7940 y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment), (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator);
7942 if (y >= tree_view->priv->height)
7943 y = tree_view->priv->height - 1;
7945 tree_view->priv->cursor_offset =
7946 pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
7948 if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
7950 cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7951 tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
7954 y -= tree_view->priv->cursor_offset;
7955 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7957 start_cursor_node = cursor_node;
7959 if (! search_first_focusable_path (tree_view, &cursor_path,
7963 /* It looks like we reached the end of the view without finding
7964 * a focusable row. We will step backwards to find the last
7967 cursor_node = start_cursor_node;
7968 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7970 search_first_focusable_path (tree_view, &cursor_path,
7979 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7981 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode);
7984 pspp_sheet_view_scroll_to_point (tree_view, -1, y);
7985 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7986 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
7988 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
7989 gtk_widget_error_bell (GTK_WIDGET (tree_view));
7991 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7994 gtk_tree_path_free (old_cursor_path);
7995 gtk_tree_path_free (cursor_path);
7999 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8001 PsppSheetSelectMode mode)
8003 int cursor_node = -1;
8004 GtkTreePath *cursor_path = NULL;
8005 PsppSheetViewColumn *column;
8008 gboolean found_column = FALSE;
8011 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8013 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8016 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8017 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8021 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8022 if (cursor_node < 0)
8024 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8026 gtk_tree_path_free (cursor_path);
8029 gtk_tree_path_free (cursor_path);
8031 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8032 if (tree_view->priv->focus_column)
8034 for (; list; list = (rtl ? list->prev : list->next))
8036 if (list->data == tree_view->priv->focus_column)
8043 gboolean left, right;
8045 column = list->data;
8046 if (column->visible == FALSE || column->row_head)
8049 pspp_sheet_view_column_cell_set_cell_data (column,
8050 tree_view->priv->model,
8055 right = list->prev ? TRUE : FALSE;
8056 left = list->next ? TRUE : FALSE;
8060 left = list->prev ? TRUE : FALSE;
8061 right = list->next ? TRUE : FALSE;
8064 if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8066 tree_view->priv->focus_column = column;
8067 found_column = TRUE;
8072 list = rtl ? list->prev : list->next;
8074 list = rtl ? list->next : list->prev;
8079 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8080 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8081 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8085 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8088 pspp_sheet_view_clamp_column_visible (tree_view,
8089 tree_view->priv->focus_column, TRUE);
8093 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8095 PsppSheetSelectMode mode)
8097 int cursor_node = -1;
8098 GtkTreePath *cursor_path = NULL;
8099 PsppSheetViewColumn *column;
8100 PsppSheetViewColumn *found_column;
8105 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8107 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8110 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8111 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8115 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8116 if (cursor_node < 0)
8118 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8120 gtk_tree_path_free (cursor_path);
8123 gtk_tree_path_free (cursor_path);
8125 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8126 if (tree_view->priv->focus_column)
8128 for (; list; list = (rtl ? list->prev : list->next))
8130 if (list->data == tree_view->priv->focus_column)
8135 found_column = NULL;
8138 gboolean left, right;
8140 column = list->data;
8141 if (column->visible == FALSE || column->row_head)
8144 pspp_sheet_view_column_cell_set_cell_data (column,
8145 tree_view->priv->model,
8150 right = list->prev ? TRUE : FALSE;
8151 left = list->next ? TRUE : FALSE;
8155 left = list->prev ? TRUE : FALSE;
8156 right = list->next ? TRUE : FALSE;
8159 if (column->tabbable
8160 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8161 found_column = column;
8165 list = rtl ? list->prev : list->next;
8167 list = rtl ? list->next : list->prev;
8172 tree_view->priv->focus_column = found_column;
8173 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8174 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8175 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8178 pspp_sheet_view_clamp_column_visible (tree_view,
8179 tree_view->priv->focus_column, TRUE);
8183 try_move_cursor_tab (PsppSheetView *tree_view,
8184 gboolean start_at_focus_column,
8187 PsppSheetViewColumn *column;
8189 int cursor_node = -1;
8190 GtkTreePath *cursor_path = NULL;
8194 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8195 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8199 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8200 if (cursor_node < 0)
8202 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8204 gtk_tree_path_free (cursor_path);
8207 gtk_tree_path_free (cursor_path);
8209 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8210 if (start_at_focus_column)
8213 ? g_list_last (tree_view->priv->columns)
8214 : g_list_first (tree_view->priv->columns));
8215 if (tree_view->priv->focus_column)
8217 for (; list; list = (rtl ? list->prev : list->next))
8219 if (list->data == tree_view->priv->focus_column)
8226 list = (rtl ^ (count == 1)
8227 ? g_list_first (tree_view->priv->columns)
8228 : g_list_last (tree_view->priv->columns));
8233 gboolean left, right;
8235 column = list->data;
8236 if (column->visible == FALSE || !column->tabbable)
8239 pspp_sheet_view_column_cell_set_cell_data (column,
8240 tree_view->priv->model,
8245 right = list->prev ? TRUE : FALSE;
8246 left = list->next ? TRUE : FALSE;
8250 left = list->prev ? TRUE : FALSE;
8251 right = list->next ? TRUE : FALSE;
8254 if (column->tabbable
8255 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8257 tree_view->priv->focus_column = column;
8258 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8259 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8260 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8265 list = rtl ? list->prev : list->next;
8267 list = rtl ? list->next : list->prev;
8274 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8277 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8280 if (!try_move_cursor_tab (tree_view, TRUE, count))
8282 if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0)
8283 && !try_move_cursor_tab (tree_view, FALSE, count))
8284 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8287 pspp_sheet_view_clamp_column_visible (tree_view,
8288 tree_view->priv->focus_column, TRUE);
8292 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8294 PsppSheetSelectMode mode)
8298 GtkTreePath *old_path;
8300 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8303 g_return_if_fail (tree_view->priv->row_count > 0);
8305 pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8309 /* Now go forward to find the first focusable row. */
8310 path = _pspp_sheet_view_find_path (tree_view, 0);
8311 search_first_focusable_path (tree_view, &path,
8312 TRUE, &cursor_node);
8316 /* Now go backwards to find last focusable row. */
8317 path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8318 search_first_focusable_path (tree_view, &path,
8319 FALSE, &cursor_node);
8325 if (gtk_tree_path_compare (old_path, path))
8327 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
8328 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8332 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8336 gtk_tree_path_free (old_path);
8337 gtk_tree_path_free (path);
8341 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8343 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8346 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8347 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8350 pspp_sheet_selection_select_all (tree_view->priv->selection);
8356 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8358 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8361 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8362 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8365 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8371 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8372 gboolean start_editing,
8373 PsppSheetSelectMode mode)
8376 int cursor_node = -1;
8377 GtkTreePath *cursor_path = NULL;
8379 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8382 if (tree_view->priv->cursor)
8383 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8385 if (cursor_path == NULL)
8388 _pspp_sheet_view_find_node (tree_view, cursor_path,
8391 if (cursor_node < 0)
8393 gtk_tree_path_free (cursor_path);
8397 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing &&
8398 tree_view->priv->focus_column)
8400 if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8402 gtk_tree_path_free (cursor_path);
8407 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8413 /* We bail out if the original (tree, node) don't exist anymore after
8414 * handling the selection-changed callback. We do return TRUE because
8415 * the key press has been handled at this point.
8417 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8419 if (cursor_node != new_node)
8422 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8424 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8425 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8427 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8428 pspp_sheet_view_row_activated (tree_view, cursor_path,
8429 tree_view->priv->focus_column);
8431 gtk_tree_path_free (cursor_path);
8437 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8440 int cursor_node = -1;
8441 GtkTreePath *cursor_path = NULL;
8443 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8447 if (tree_view->priv->cursor)
8448 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8450 if (cursor_path == NULL)
8453 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8454 if (cursor_node < 0)
8456 gtk_tree_path_free (cursor_path);
8460 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8463 PSPP_SHEET_SELECT_MODE_TOGGLE,
8466 /* We bail out if the original (tree, node) don't exist anymore after
8467 * handling the selection-changed callback. We do return TRUE because
8468 * the key press has been handled at this point.
8470 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8472 if (cursor_node != new_node)
8475 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8477 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8478 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8479 gtk_tree_path_free (cursor_path);
8485 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8487 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8488 tree_view->priv->typeselect_flush_timeout = 0;
8493 /* Cut and paste from gtkwindow.c */
8495 send_focus_change (GtkWidget *widget,
8498 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8500 fevent->focus_change.type = GDK_FOCUS_CHANGE;
8501 fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
8502 fevent->focus_change.in = in;
8504 gtk_widget_send_focus_change (widget, fevent);
8505 gdk_event_free (fevent);
8509 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8511 GtkWidget *frame, *vbox, *toplevel;
8514 if (tree_view->priv->search_custom_entry_set)
8517 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8518 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8520 if (tree_view->priv->search_window != NULL)
8522 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8523 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8524 GTK_WINDOW (tree_view->priv->search_window));
8525 else if (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)))
8526 gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
8527 GTK_WINDOW (tree_view->priv->search_window));
8528 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8532 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8533 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8535 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8536 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8537 GTK_WINDOW (tree_view->priv->search_window));
8539 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8540 GDK_WINDOW_TYPE_HINT_UTILITY);
8541 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8542 g_signal_connect (tree_view->priv->search_window, "delete-event",
8543 G_CALLBACK (pspp_sheet_view_search_delete_event),
8545 g_signal_connect (tree_view->priv->search_window, "key-press-event",
8546 G_CALLBACK (pspp_sheet_view_search_key_press_event),
8548 g_signal_connect (tree_view->priv->search_window, "button-press-event",
8549 G_CALLBACK (pspp_sheet_view_search_button_press_event),
8551 g_signal_connect (tree_view->priv->search_window, "scroll-event",
8552 G_CALLBACK (pspp_sheet_view_search_scroll_event),
8555 frame = gtk_frame_new (NULL);
8556 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8557 gtk_widget_show (frame);
8558 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8560 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
8561 gtk_widget_show (vbox);
8562 gtk_container_add (GTK_CONTAINER (frame), vbox);
8563 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8566 tree_view->priv->search_entry = gtk_entry_new ();
8567 gtk_widget_show (tree_view->priv->search_entry);
8568 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8569 G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8571 g_signal_connect (tree_view->priv->search_entry,
8572 "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8576 g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8578 G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8582 gtk_container_add (GTK_CONTAINER (vbox),
8583 tree_view->priv->search_entry);
8585 gtk_widget_realize (tree_view->priv->search_entry);
8588 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8589 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8592 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8593 gboolean keybinding)
8595 /* We only start interactive search if we have focus or the columns
8596 * have focus. If one of our children have focus, we don't want to
8600 gboolean found_focus = FALSE;
8601 GtkWidgetClass *entry_parent_class;
8603 if (!tree_view->priv->enable_search && !keybinding)
8606 if (tree_view->priv->search_custom_entry_set)
8609 if (tree_view->priv->search_window != NULL &&
8610 gtk_widget_get_visible (tree_view->priv->search_window))
8613 for (list = tree_view->priv->columns; list; list = list->next)
8615 PsppSheetViewColumn *column;
8617 column = list->data;
8618 if (! column->visible)
8621 if (column->button && gtk_widget_has_focus (column->button))
8628 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8634 if (tree_view->priv->search_column < 0)
8637 pspp_sheet_view_ensure_interactive_directory (tree_view);
8640 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8643 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8644 gtk_widget_show (tree_view->priv->search_window);
8645 if (tree_view->priv->search_entry_changed_id == 0)
8647 tree_view->priv->search_entry_changed_id =
8648 g_signal_connect (tree_view->priv->search_entry, "changed",
8649 G_CALLBACK (pspp_sheet_view_search_init),
8653 tree_view->priv->typeselect_flush_timeout =
8654 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8655 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8658 /* Grab focus will select all the text. We don't want that to happen, so we
8659 * call the parent instance and bypass the selection change. This is probably
8660 * really non-kosher. */
8661 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8662 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8664 /* send focus-in event */
8665 send_focus_change (tree_view->priv->search_entry, TRUE);
8667 /* search first matching iter */
8668 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8674 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8676 return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8679 /* this function returns the new width of the column being resized given
8680 * the column and x position of the cursor; the x cursor position is passed
8681 * in as a pointer and automagicly corrected if it's beyond min/max limits
8684 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8688 PsppSheetViewColumn *column;
8692 /* first translate the x position from gtk_widget_get_window (widget)
8693 * to clist->clist_window
8695 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8696 column = g_list_nth (tree_view->priv->columns, i)->data;
8697 width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8699 /* Clamp down the value */
8700 if (column->min_width == -1)
8701 width = MAX (column->button_request, width);
8703 width = MAX (column->min_width, width);
8704 if (column->max_width != -1)
8705 width = MIN (width, column->max_width);
8707 *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8713 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8717 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8718 PsppSheetView *tree_view)
8720 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8724 gdk_window_move (tree_view->priv->bin_window,
8725 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8726 TREE_VIEW_HEADER_HEIGHT (tree_view));
8727 gdk_window_move (tree_view->priv->header_window,
8728 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8730 dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8732 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8736 /* update our dy and top_row */
8737 tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8739 update_prelight (tree_view,
8740 tree_view->priv->event_last_x,
8741 tree_view->priv->event_last_y);
8743 if (!tree_view->priv->in_top_row_to_dy)
8744 pspp_sheet_view_dy_to_top_row (tree_view);
8747 update_childrens_allocation(tree_view);
8755 * pspp_sheet_view_new:
8757 * Creates a new #PsppSheetView widget.
8759 * Return value: A newly created #PsppSheetView widget.
8762 pspp_sheet_view_new (void)
8764 return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
8768 * pspp_sheet_view_new_with_model:
8769 * @model: the model.
8771 * Creates a new #PsppSheetView widget with the model initialized to @model.
8773 * Return value: A newly created #PsppSheetView widget.
8776 pspp_sheet_view_new_with_model (GtkTreeModel *model)
8778 return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
8785 * pspp_sheet_view_get_model:
8786 * @tree_view: a #PsppSheetView
8788 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
8791 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
8794 pspp_sheet_view_get_model (PsppSheetView *tree_view)
8796 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8798 return tree_view->priv->model;
8802 * pspp_sheet_view_set_model:
8803 * @tree_view: A #GtkTreeNode.
8804 * @model: (allow-none): The model.
8806 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
8807 * set, it will remove it before setting the new model. If @model is %NULL,
8808 * then it will unset the old model.
8811 pspp_sheet_view_set_model (PsppSheetView *tree_view,
8812 GtkTreeModel *model)
8814 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8815 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
8817 if (model == tree_view->priv->model)
8820 if (tree_view->priv->scroll_to_path)
8822 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8823 tree_view->priv->scroll_to_path = NULL;
8826 if (tree_view->priv->model)
8828 GList *tmplist = tree_view->priv->columns;
8830 if (tree_view->priv->selected)
8831 range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
8832 pspp_sheet_view_stop_editing (tree_view, TRUE);
8834 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8835 pspp_sheet_view_row_changed,
8837 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8838 pspp_sheet_view_row_inserted,
8840 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8841 pspp_sheet_view_row_deleted,
8843 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8844 pspp_sheet_view_rows_reordered,
8847 for (; tmplist; tmplist = tmplist->next)
8848 _pspp_sheet_view_column_unset_model (tmplist->data,
8849 tree_view->priv->model);
8851 tree_view->priv->prelight_node = -1;
8853 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
8854 tree_view->priv->drag_dest_row = NULL;
8855 gtk_tree_row_reference_free (tree_view->priv->cursor);
8856 tree_view->priv->cursor = NULL;
8857 gtk_tree_row_reference_free (tree_view->priv->anchor);
8858 tree_view->priv->anchor = NULL;
8859 gtk_tree_row_reference_free (tree_view->priv->top_row);
8860 tree_view->priv->top_row = NULL;
8861 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8862 tree_view->priv->scroll_to_path = NULL;
8864 tree_view->priv->scroll_to_column = NULL;
8866 g_object_unref (tree_view->priv->model);
8868 tree_view->priv->search_column = -1;
8869 tree_view->priv->fixed_height = -1;
8870 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
8871 tree_view->priv->last_button_x = -1;
8872 tree_view->priv->last_button_y = -1;
8875 tree_view->priv->model = model;
8877 if (tree_view->priv->model)
8881 if (tree_view->priv->search_column == -1)
8883 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
8885 GType type = gtk_tree_model_get_column_type (model, i);
8887 if (g_value_type_transformable (type, G_TYPE_STRING))
8889 tree_view->priv->search_column = i;
8895 g_object_ref (tree_view->priv->model);
8896 g_signal_connect (tree_view->priv->model,
8898 G_CALLBACK (pspp_sheet_view_row_changed),
8900 g_signal_connect (tree_view->priv->model,
8902 G_CALLBACK (pspp_sheet_view_row_inserted),
8904 g_signal_connect (tree_view->priv->model,
8906 G_CALLBACK (pspp_sheet_view_row_deleted),
8908 g_signal_connect (tree_view->priv->model,
8910 G_CALLBACK (pspp_sheet_view_rows_reordered),
8913 tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
8915 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
8916 install_presize_handler (tree_view);
8919 g_object_notify (G_OBJECT (tree_view), "model");
8921 if (tree_view->priv->selection)
8922 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
8924 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8925 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8929 * pspp_sheet_view_get_selection:
8930 * @tree_view: A #PsppSheetView.
8932 * Gets the #PsppSheetSelection associated with @tree_view.
8934 * Return value: A #PsppSheetSelection object.
8936 PsppSheetSelection *
8937 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
8939 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8941 return tree_view->priv->selection;
8945 * pspp_sheet_view_get_hadjustment:
8946 * @tree_view: A #PsppSheetView
8948 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
8950 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
8954 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
8956 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8958 return pspp_sheet_view_do_get_hadjustment (tree_view);
8961 static GtkAdjustment *
8962 pspp_sheet_view_do_get_hadjustment (PsppSheetView *tree_view)
8964 return tree_view->priv->hadjustment;
8968 * pspp_sheet_view_set_hadjustment:
8969 * @tree_view: A #PsppSheetView
8970 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
8972 * Sets the #GtkAdjustment for the current horizontal aspect.
8975 pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view,
8976 GtkAdjustment *adjustment)
8978 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8980 pspp_sheet_view_set_adjustments (tree_view,
8982 tree_view->priv->vadjustment);
8984 g_object_notify (G_OBJECT (tree_view), "hadjustment");
8988 pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
8989 GtkAdjustment *adjustment)
8991 PsppSheetViewPrivate *priv = tree_view->priv;
8993 if (adjustment && priv->hadjustment == adjustment)
8996 if (priv->hadjustment != NULL)
8998 g_signal_handlers_disconnect_by_func (priv->hadjustment,
8999 pspp_sheet_view_adjustment_changed,
9001 g_object_unref (priv->hadjustment);
9004 if (adjustment == NULL)
9005 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9008 g_signal_connect (adjustment, "value-changed",
9009 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9010 priv->hadjustment = g_object_ref_sink (adjustment);
9011 /* FIXME: Adjustment should probably be populated here with fresh values, but
9012 * internal details are too complicated for me to decipher right now.
9014 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9016 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9020 * pspp_sheet_view_get_vadjustment:
9021 * @tree_view: A #PsppSheetView
9023 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9025 * Return value: (transfer none): A #GtkAdjustment object, or %NULL
9026 * if none is currently being used.
9028 * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
9031 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9033 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9035 return pspp_sheet_view_do_get_vadjustment (tree_view);
9038 static GtkAdjustment *
9039 pspp_sheet_view_do_get_vadjustment (PsppSheetView *tree_view)
9041 return tree_view->priv->vadjustment;
9045 * pspp_sheet_view_set_vadjustment:
9046 * @tree_view: A #PsppSheetView
9047 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9049 * Sets the #GtkAdjustment for the current vertical aspect.
9051 * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
9054 pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view,
9055 GtkAdjustment *adjustment)
9057 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9058 g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
9060 pspp_sheet_view_do_set_vadjustment (tree_view, adjustment);
9064 pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
9065 GtkAdjustment *adjustment)
9067 PsppSheetViewPrivate *priv = tree_view->priv;
9069 if (adjustment && priv->vadjustment == adjustment)
9072 if (priv->vadjustment != NULL)
9074 g_signal_handlers_disconnect_by_func (priv->vadjustment,
9075 pspp_sheet_view_adjustment_changed,
9077 g_object_unref (priv->vadjustment);
9080 if (adjustment == NULL)
9081 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9084 g_signal_connect (adjustment, "value-changed",
9085 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9086 priv->vadjustment = g_object_ref_sink (adjustment);
9087 /* FIXME: Adjustment should probably be populated here with fresh values, but
9088 * internal details are too complicated for me to decipher right now.
9090 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9091 g_object_notify (G_OBJECT (tree_view), "vadjustment");
9094 /* Column and header operations */
9097 * pspp_sheet_view_get_headers_visible:
9098 * @tree_view: A #PsppSheetView.
9100 * Returns %TRUE if the headers on the @tree_view are visible.
9102 * Return value: Whether the headers are visible or not.
9105 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9107 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9109 return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9113 * pspp_sheet_view_set_headers_visible:
9114 * @tree_view: A #PsppSheetView.
9115 * @headers_visible: %TRUE if the headers are visible
9117 * Sets the visibility state of the headers.
9120 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9121 gboolean headers_visible)
9125 PsppSheetViewColumn *column;
9126 GtkAllocation allocation;
9128 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9130 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9132 headers_visible = !! headers_visible;
9134 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9137 if (headers_visible)
9138 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9140 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9142 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9144 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9145 if (headers_visible)
9147 gdk_window_move_resize (tree_view->priv->bin_window, x, y + TREE_VIEW_HEADER_HEIGHT (tree_view),
9148 tree_view->priv->width, allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view));
9150 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9151 pspp_sheet_view_map_buttons (tree_view);
9155 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9157 for (list = tree_view->priv->columns; list; list = list->next)
9159 column = list->data;
9161 gtk_widget_unmap (column->button);
9163 gdk_window_hide (tree_view->priv->header_window);
9167 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view));
9168 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2);
9169 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
9170 gtk_adjustment_set_upper (tree_view->priv->vadjustment, tree_view->priv->height);
9171 gtk_adjustment_changed (tree_view->priv->vadjustment);
9173 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9175 g_object_notify (G_OBJECT (tree_view), "headers-visible");
9179 * pspp_sheet_view_columns_autosize:
9180 * @tree_view: A #PsppSheetView.
9182 * Resizes all columns to their optimal width. Only works after the
9183 * treeview has been realized.
9186 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9188 gboolean dirty = FALSE;
9190 PsppSheetViewColumn *column;
9192 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9194 for (list = tree_view->priv->columns; list; list = list->next)
9196 column = list->data;
9197 _pspp_sheet_view_column_cell_set_dirty (column);
9202 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9206 * pspp_sheet_view_set_headers_clickable:
9207 * @tree_view: A #PsppSheetView.
9208 * @setting: %TRUE if the columns are clickable.
9210 * Allow the column title buttons to be clicked.
9213 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9218 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9220 for (list = tree_view->priv->columns; list; list = list->next)
9221 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9223 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9228 * pspp_sheet_view_get_headers_clickable:
9229 * @tree_view: A #PsppSheetView.
9231 * Returns whether all header columns are clickable.
9233 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9238 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9242 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9244 for (list = tree_view->priv->columns; list; list = list->next)
9245 if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9252 * pspp_sheet_view_set_rules_hint
9253 * @tree_view: a #PsppSheetView
9254 * @setting: %TRUE if the tree requires reading across rows
9256 * This function tells GTK+ that the user interface for your
9257 * application requires users to read across tree rows and associate
9258 * cells with one another. By default, GTK+ will then render the tree
9259 * with alternating row colors. Do <emphasis>not</emphasis> use it
9260 * just because you prefer the appearance of the ruled tree; that's a
9261 * question for the theme. Some themes will draw tree rows in
9262 * alternating colors even when rules are turned off, and users who
9263 * prefer that appearance all the time can choose those themes. You
9264 * should call this function only as a <emphasis>semantic</emphasis>
9265 * hint to the theme engine that your tree makes alternating colors
9266 * useful from a functional standpoint (since it has lots of columns,
9271 pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view,
9274 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9276 setting = setting != FALSE;
9278 if (tree_view->priv->has_rules != setting)
9280 tree_view->priv->has_rules = setting;
9281 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9284 g_object_notify (G_OBJECT (tree_view), "rules-hint");
9288 * pspp_sheet_view_get_rules_hint
9289 * @tree_view: a #PsppSheetView
9291 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9293 * Return value: %TRUE if rules are useful for the user of this tree
9296 pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view)
9298 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9300 return tree_view->priv->has_rules;
9303 /* Public Column functions
9307 * pspp_sheet_view_append_column:
9308 * @tree_view: A #PsppSheetView.
9309 * @column: The #PsppSheetViewColumn to add.
9311 * Appends @column to the list of columns.
9313 * Return value: The number of columns in @tree_view after appending.
9316 pspp_sheet_view_append_column (PsppSheetView *tree_view,
9317 PsppSheetViewColumn *column)
9319 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9320 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9321 g_return_val_if_fail (column->tree_view == NULL, -1);
9323 return pspp_sheet_view_insert_column (tree_view, column, -1);
9328 * pspp_sheet_view_remove_column:
9329 * @tree_view: A #PsppSheetView.
9330 * @column: The #PsppSheetViewColumn to remove.
9332 * Removes @column from @tree_view.
9334 * Return value: The number of columns in @tree_view after removing.
9337 pspp_sheet_view_remove_column (PsppSheetView *tree_view,
9338 PsppSheetViewColumn *column)
9340 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9341 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9342 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9344 if (tree_view->priv->focus_column == column)
9345 tree_view->priv->focus_column = NULL;
9347 if (tree_view->priv->edited_column == column)
9349 pspp_sheet_view_stop_editing (tree_view, TRUE);
9351 /* no need to, but just to be sure ... */
9352 tree_view->priv->edited_column = NULL;
9355 _pspp_sheet_view_column_unset_tree_view (column);
9357 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9358 tree_view->priv->n_columns--;
9360 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9364 _pspp_sheet_view_column_unrealize_button (column);
9365 for (list = tree_view->priv->columns; list; list = list->next)
9367 PsppSheetViewColumn *tmp_column;
9369 tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9370 if (tmp_column->visible)
9371 _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9374 if (tree_view->priv->n_columns == 0 &&
9375 pspp_sheet_view_get_headers_visible (tree_view) &&
9376 tree_view->priv->header_window)
9377 gdk_window_hide (tree_view->priv->header_window);
9379 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9382 g_object_unref (column);
9383 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9385 return tree_view->priv->n_columns;
9389 * pspp_sheet_view_insert_column:
9390 * @tree_view: A #PsppSheetView.
9391 * @column: The #PsppSheetViewColumn to be inserted.
9392 * @position: The position to insert @column in.
9394 * This inserts the @column into the @tree_view at @position. If @position is
9395 * -1, then the column is inserted at the end.
9397 * Return value: The number of columns in @tree_view after insertion.
9400 pspp_sheet_view_insert_column (PsppSheetView *tree_view,
9401 PsppSheetViewColumn *column,
9404 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9405 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9406 g_return_val_if_fail (column->tree_view == NULL, -1);
9408 g_object_ref_sink (column);
9410 if (tree_view->priv->n_columns == 0 &&
9411 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9412 pspp_sheet_view_get_headers_visible (tree_view))
9414 gdk_window_show (tree_view->priv->header_window);
9417 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9419 tree_view->priv->n_columns++;
9421 _pspp_sheet_view_column_set_tree_view (column, tree_view);
9423 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9427 _pspp_sheet_view_column_realize_button (column);
9429 for (list = tree_view->priv->columns; list; list = list->next)
9431 column = PSPP_SHEET_VIEW_COLUMN (list->data);
9432 if (column->visible)
9433 _pspp_sheet_view_column_cell_set_dirty (column);
9435 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9438 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9440 return tree_view->priv->n_columns;
9444 * pspp_sheet_view_insert_column_with_attributes:
9445 * @tree_view: A #PsppSheetView
9446 * @position: The position to insert the new column in.
9447 * @title: The title to set the header to.
9448 * @cell: The #GtkCellRenderer.
9449 * @Varargs: A %NULL-terminated list of attributes.
9451 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9452 * @position. If @position is -1, then the newly created column is inserted at
9453 * the end. The column is initialized with the attributes given.
9455 * Return value: The number of columns in @tree_view after insertion.
9458 pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view,
9461 GtkCellRenderer *cell,
9464 PsppSheetViewColumn *column;
9469 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9471 column = pspp_sheet_view_column_new ();
9472 pspp_sheet_view_column_set_title (column, title);
9473 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9475 va_start (args, cell);
9477 attribute = va_arg (args, gchar *);
9479 while (attribute != NULL)
9481 column_id = va_arg (args, gint);
9482 pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9483 attribute = va_arg (args, gchar *);
9488 pspp_sheet_view_insert_column (tree_view, column, position);
9490 return tree_view->priv->n_columns;
9494 * pspp_sheet_view_insert_column_with_data_func:
9495 * @tree_view: a #PsppSheetView
9496 * @position: Position to insert, -1 for append
9497 * @title: column title
9498 * @cell: cell renderer for column
9499 * @func: function to set attributes of cell renderer
9500 * @data: data for @func
9501 * @dnotify: destroy notifier for @data
9503 * Convenience function that inserts a new column into the #PsppSheetView
9504 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9505 * attributes (normally using data from the model). See also
9506 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9508 * Return value: number of columns in the tree view post-insert
9511 pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view,
9514 GtkCellRenderer *cell,
9515 PsppSheetCellDataFunc func,
9517 GDestroyNotify dnotify)
9519 PsppSheetViewColumn *column;
9521 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9523 column = pspp_sheet_view_column_new ();
9524 pspp_sheet_view_column_set_title (column, title);
9525 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9526 pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9528 pspp_sheet_view_insert_column (tree_view, column, position);
9530 return tree_view->priv->n_columns;
9534 * pspp_sheet_view_get_column:
9535 * @tree_view: A #PsppSheetView.
9536 * @n: The position of the column, counting from 0.
9538 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9540 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9543 PsppSheetViewColumn *
9544 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9547 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9549 if (n < 0 || n >= tree_view->priv->n_columns)
9552 if (tree_view->priv->columns == NULL)
9555 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9559 * pspp_sheet_view_get_columns:
9560 * @tree_view: A #PsppSheetView
9562 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9563 * The returned list must be freed with g_list_free ().
9565 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9568 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9570 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9572 return g_list_copy (tree_view->priv->columns);
9576 * pspp_sheet_view_move_column_after:
9577 * @tree_view: A #PsppSheetView
9578 * @column: The #PsppSheetViewColumn to be moved.
9579 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9581 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9582 * @column is placed in the first position.
9585 pspp_sheet_view_move_column_after (PsppSheetView *tree_view,
9586 PsppSheetViewColumn *column,
9587 PsppSheetViewColumn *base_column)
9589 GList *column_list_el, *base_el = NULL;
9591 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9593 column_list_el = g_list_find (tree_view->priv->columns, column);
9594 g_return_if_fail (column_list_el != NULL);
9598 base_el = g_list_find (tree_view->priv->columns, base_column);
9599 g_return_if_fail (base_el != NULL);
9602 if (column_list_el->prev == base_el)
9605 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9606 if (base_el == NULL)
9608 column_list_el->prev = NULL;
9609 column_list_el->next = tree_view->priv->columns;
9610 if (column_list_el->next)
9611 column_list_el->next->prev = column_list_el;
9612 tree_view->priv->columns = column_list_el;
9616 column_list_el->prev = base_el;
9617 column_list_el->next = base_el->next;
9618 if (column_list_el->next)
9619 column_list_el->next->prev = column_list_el;
9620 base_el->next = column_list_el;
9623 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9625 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9626 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9629 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9633 * pspp_sheet_view_set_column_drag_function:
9634 * @tree_view: A #PsppSheetView.
9635 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9636 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9637 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9639 * Sets a user function for determining where a column may be dropped when
9640 * dragged. This function is called on every column pair in turn at the
9641 * beginning of a column drag to determine where a drop can take place. The
9642 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9643 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9644 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9645 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9646 * @tree_view reverts to the default behavior of allowing all columns to be
9647 * dropped everywhere.
9650 pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view,
9651 PsppSheetViewColumnDropFunc func,
9653 GDestroyNotify destroy)
9655 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9657 if (tree_view->priv->column_drop_func_data_destroy)
9658 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9660 tree_view->priv->column_drop_func = func;
9661 tree_view->priv->column_drop_func_data = user_data;
9662 tree_view->priv->column_drop_func_data_destroy = destroy;
9666 * pspp_sheet_view_scroll_to_point:
9667 * @tree_view: a #PsppSheetView
9668 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9669 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9671 * Scrolls the tree view such that the top-left corner of the visible
9672 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9673 * in tree coordinates. The @tree_view must be realized before
9674 * this function is called. If it isn't, you probably want to be
9675 * using pspp_sheet_view_scroll_to_cell().
9677 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9680 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9684 GtkAdjustment *hadj;
9685 GtkAdjustment *vadj;
9687 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9688 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9690 hadj = tree_view->priv->hadjustment;
9691 vadj = tree_view->priv->vadjustment;
9694 gtk_adjustment_set_value (hadj, CLAMP (tree_x, gtk_adjustment_get_lower (hadj), gtk_adjustment_get_upper (hadj) - gtk_adjustment_get_page_size (hadj)));
9696 gtk_adjustment_set_value (vadj, CLAMP (tree_y, gtk_adjustment_get_lower (vadj), gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj)));
9700 * pspp_sheet_view_scroll_to_cell:
9701 * @tree_view: A #PsppSheetView.
9702 * @path: (allow-none): The path of the row to move to, or %NULL.
9703 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9704 * @use_align: whether to use alignment arguments, or %FALSE.
9705 * @row_align: The vertical alignment of the row specified by @path.
9706 * @col_align: The horizontal alignment of the column specified by @column.
9708 * Moves the alignments of @tree_view to the position specified by @column and
9709 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9710 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9711 * or @path need to be non-%NULL. @row_align determines where the row is
9712 * placed, and @col_align determines where @column is placed. Both are expected
9713 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9714 * right/bottom alignment, 0.5 means center.
9716 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9717 * tree does the minimum amount of work to scroll the cell onto the screen.
9718 * This means that the cell will be scrolled to the edge closest to its current
9719 * position. If the cell is currently visible on the screen, nothing is done.
9721 * This function only works if the model is set, and @path is a valid row on the
9722 * model. If the model changes before the @tree_view is realized, the centered
9723 * path will be modified to reflect this change.
9726 pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view,
9728 PsppSheetViewColumn *column,
9733 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9734 g_return_if_fail (tree_view->priv->model != NULL);
9735 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9736 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9737 g_return_if_fail (path != NULL || column != NULL);
9740 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9741 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9743 row_align = CLAMP (row_align, 0.0, 1.0);
9744 col_align = CLAMP (col_align, 0.0, 1.0);
9747 /* Note: Despite the benefits that come from having one code path for the
9748 * scrolling code, we short-circuit validate_visible_area's immplementation as
9749 * it is much slower than just going to the point.
9751 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9752 !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9753 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9755 if (tree_view->priv->scroll_to_path)
9756 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9758 tree_view->priv->scroll_to_path = NULL;
9759 tree_view->priv->scroll_to_column = NULL;
9762 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
9764 tree_view->priv->scroll_to_column = column;
9765 tree_view->priv->scroll_to_use_align = use_align;
9766 tree_view->priv->scroll_to_row_align = row_align;
9767 tree_view->priv->scroll_to_col_align = col_align;
9769 install_presize_handler (tree_view);
9773 GdkRectangle cell_rect;
9774 GdkRectangle vis_rect;
9775 gint dest_x, dest_y;
9777 pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
9778 pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
9780 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
9782 dest_x = vis_rect.x;
9783 dest_y = vis_rect.y;
9789 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
9793 if (cell_rect.x < vis_rect.x)
9794 dest_x = cell_rect.x;
9795 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
9796 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
9804 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
9805 dest_y = MAX (dest_y, 0);
9809 if (cell_rect.y < vis_rect.y)
9810 dest_y = cell_rect.y;
9811 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
9812 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
9816 pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
9821 * pspp_sheet_view_row_activated:
9822 * @tree_view: A #PsppSheetView
9823 * @path: The #GtkTreePath to be activated.
9824 * @column: The #PsppSheetViewColumn to be activated.
9826 * Activates the cell determined by @path and @column.
9829 pspp_sheet_view_row_activated (PsppSheetView *tree_view,
9831 PsppSheetViewColumn *column)
9833 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9835 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
9840 * pspp_sheet_view_get_reorderable:
9841 * @tree_view: a #PsppSheetView
9843 * Retrieves whether the user can reorder the tree via drag-and-drop. See
9844 * pspp_sheet_view_set_reorderable().
9846 * Return value: %TRUE if the tree can be reordered.
9849 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
9851 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9853 return tree_view->priv->reorderable;
9857 * pspp_sheet_view_set_reorderable:
9858 * @tree_view: A #PsppSheetView.
9859 * @reorderable: %TRUE, if the tree can be reordered.
9861 * This function is a convenience function to allow you to reorder
9862 * models that support the #GtkDragSourceIface and the
9863 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
9864 * these. If @reorderable is %TRUE, then the user can reorder the
9865 * model by dragging and dropping rows. The developer can listen to
9866 * these changes by connecting to the model's row_inserted and
9867 * row_deleted signals. The reordering is implemented by setting up
9868 * the tree view as a drag source and destination. Therefore, drag and
9869 * drop can not be used in a reorderable view for any other purpose.
9871 * This function does not give you any degree of control over the order -- any
9872 * reordering is allowed. If more control is needed, you should probably
9873 * handle drag and drop manually.
9876 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
9877 gboolean reorderable)
9879 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9881 reorderable = reorderable != FALSE;
9883 if (tree_view->priv->reorderable == reorderable)
9888 const GtkTargetEntry row_targets[] = {
9889 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
9892 pspp_sheet_view_enable_model_drag_source (tree_view,
9895 G_N_ELEMENTS (row_targets),
9897 pspp_sheet_view_enable_model_drag_dest (tree_view,
9899 G_N_ELEMENTS (row_targets),
9904 pspp_sheet_view_unset_rows_drag_source (tree_view);
9905 pspp_sheet_view_unset_rows_drag_dest (tree_view);
9908 tree_view->priv->reorderable = reorderable;
9910 g_object_notify (G_OBJECT (tree_view), "reorderable");
9913 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
9914 is pressed, other rows will be unselected.
9916 If CLAMP_NODE is true, then the sheetview will scroll to make the row
9919 pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
9921 gboolean clear_and_select,
9922 gboolean clamp_node,
9923 PsppSheetSelectMode mode)
9927 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9929 GtkTreePath *cursor_path;
9930 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9931 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
9932 gtk_tree_path_free (cursor_path);
9935 gtk_tree_row_reference_free (tree_view->priv->cursor);
9936 tree_view->priv->cursor = NULL;
9938 _pspp_sheet_view_find_node (tree_view, path, &node);
9939 tree_view->priv->cursor =
9940 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
9941 tree_view->priv->model,
9944 if (tree_view->priv->row_count > 0)
9948 if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
9949 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
9953 /* We have to re-find tree and node here again, somebody might have
9954 * cleared the node or the whole tree in the PsppSheetSelection::changed
9955 * callback. If the nodes differ we bail out here.
9957 _pspp_sheet_view_find_node (tree_view, path, &new_node);
9959 if (node != new_node)
9964 pspp_sheet_view_clamp_node_visible (tree_view, node);
9965 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
9969 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
9973 * pspp_sheet_view_get_cursor:
9974 * @tree_view: A #PsppSheetView
9975 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
9976 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
9978 * Fills in @path and @focus_column with the current path and focus column. If
9979 * the cursor isn't currently set, then *@path will be %NULL. If no column
9980 * currently has focus, then *@focus_column will be %NULL.
9982 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
9983 * you are done with it.
9986 pspp_sheet_view_get_cursor (PsppSheetView *tree_view,
9988 PsppSheetViewColumn **focus_column)
9990 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9994 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9995 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10002 *focus_column = tree_view->priv->focus_column;
10007 * pspp_sheet_view_set_cursor:
10008 * @tree_view: A #PsppSheetView
10009 * @path: A #GtkTreePath
10010 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10011 * @start_editing: %TRUE if the specified cell should start being edited.
10013 * Sets the current keyboard focus to be at @path, and selects it. This is
10014 * useful when you want to focus the user's attention on a particular row. If
10015 * @focus_column is not %NULL, then focus is given to the column specified by
10016 * it. Additionally, if @focus_column is specified, and @start_editing is
10017 * %TRUE, then editing should be started in the specified cell.
10018 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10019 * in order to give keyboard focus to the widget. Please note that editing
10020 * can only happen when the widget is realized.
10022 * If @path is invalid for @model, the current cursor (if any) will be unset
10023 * and the function will return without failing.
10026 pspp_sheet_view_set_cursor (PsppSheetView *tree_view,
10028 PsppSheetViewColumn *focus_column,
10029 gboolean start_editing)
10031 pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10032 NULL, start_editing);
10036 * pspp_sheet_view_set_cursor_on_cell:
10037 * @tree_view: A #PsppSheetView
10038 * @path: A #GtkTreePath
10039 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10040 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10041 * @start_editing: %TRUE if the specified cell should start being edited.
10043 * Sets the current keyboard focus to be at @path, and selects it. This is
10044 * useful when you want to focus the user's attention on a particular row. If
10045 * @focus_column is not %NULL, then focus is given to the column specified by
10046 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10047 * contains 2 or more editable or activatable cells, then focus is given to
10048 * the cell specified by @focus_cell. Additionally, if @focus_column is
10049 * specified, and @start_editing is %TRUE, then editing should be started in
10050 * the specified cell. This function is often followed by
10051 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10052 * widget. Please note that editing can only happen when the widget is
10055 * If @path is invalid for @model, the current cursor (if any) will be unset
10056 * and the function will return without failing.
10061 pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view,
10063 PsppSheetViewColumn *focus_column,
10064 GtkCellRenderer *focus_cell,
10065 gboolean start_editing)
10067 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10068 g_return_if_fail (path != NULL);
10069 g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10071 if (!tree_view->priv->model)
10076 g_return_if_fail (focus_column);
10077 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10080 /* cancel the current editing, if it exists */
10081 if (tree_view->priv->edited_column &&
10082 tree_view->priv->edited_column->editable_widget)
10083 pspp_sheet_view_stop_editing (tree_view, TRUE);
10085 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
10087 if (focus_column && focus_column->visible)
10090 gboolean column_in_tree = FALSE;
10092 for (list = tree_view->priv->columns; list; list = list->next)
10093 if (list->data == focus_column)
10095 column_in_tree = TRUE;
10098 g_return_if_fail (column_in_tree);
10099 tree_view->priv->focus_column = focus_column;
10101 pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10103 pspp_sheet_view_start_editing (tree_view, path);
10105 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10106 pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10112 * pspp_sheet_view_get_bin_window:
10113 * @tree_view: A #PsppSheetView
10115 * Returns the window that @tree_view renders to. This is used primarily to
10116 * compare to <literal>event->window</literal> to confirm that the event on
10117 * @tree_view is on the right window.
10119 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10122 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10124 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10126 return tree_view->priv->bin_window;
10130 * pspp_sheet_view_get_path_at_pos:
10131 * @tree_view: A #PsppSheetView.
10132 * @x: The x position to be identified (relative to bin_window).
10133 * @y: The y position to be identified (relative to bin_window).
10134 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10135 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10136 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10137 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10139 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10140 * (please see pspp_sheet_view_get_bin_window()).
10141 * That is, @x and @y are relative to an events coordinates. @x and @y must
10142 * come from an event on the @tree_view only where <literal>event->window ==
10143 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10144 * things like popup menus. If @path is non-%NULL, then it will be filled
10145 * with the #GtkTreePath at that point. This path should be freed with
10146 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10147 * with the column at that point. @cell_x and @cell_y return the coordinates
10148 * relative to the cell background (i.e. the @background_area passed to
10149 * gtk_cell_renderer_render()). This function is only meaningful if
10150 * @tree_view is realized. Therefore this function will always return %FALSE
10151 * if @tree_view is not realized or does not have a model.
10153 * For converting widget coordinates (eg. the ones you get from
10154 * GtkWidget::query-tooltip), please see
10155 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10157 * Return value: %TRUE if a row exists at that coordinate.
10160 pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view,
10163 GtkTreePath **path,
10164 PsppSheetViewColumn **column,
10171 g_return_val_if_fail (tree_view != NULL, FALSE);
10178 if (tree_view->priv->bin_window == NULL)
10181 if (tree_view->priv->row_count == 0)
10184 if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment))
10187 if (x < 0 || y < 0)
10190 if (column || cell_x)
10192 PsppSheetViewColumn *tmp_column;
10193 PsppSheetViewColumn *last_column = NULL;
10195 gint remaining_x = x;
10196 gboolean found = FALSE;
10199 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10200 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10202 list = (rtl ? list->prev : list->next))
10204 tmp_column = list->data;
10206 if (tmp_column->visible == FALSE)
10209 last_column = tmp_column;
10210 if (remaining_x <= tmp_column->width)
10215 *column = tmp_column;
10218 *cell_x = remaining_x;
10222 remaining_x -= tmp_column->width;
10225 /* If found is FALSE and there is a last_column, then it the remainder
10226 * space is in that area
10233 *column = last_column;
10236 *cell_x = last_column->width + remaining_x;
10245 y_offset = pspp_sheet_view_find_offset (tree_view,
10246 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10253 *cell_y = y_offset;
10256 *path = _pspp_sheet_view_find_path (tree_view, node);
10261 /* Computes 'cell_area' from 'background_area', which must be the background
10262 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10263 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10264 the cell area as passed to _pspp_sheet_view_column_cell_render().
10266 'column' is required to properly adjust 'cell_area->x' and
10267 'cell_area->width'. It may be set to NULL if these values are not of
10268 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10271 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
10272 PsppSheetViewColumn *column,
10273 const GdkRectangle *background_area,
10274 gboolean subtract_focus_rect,
10275 GdkRectangle *cell_area)
10277 gint vertical_separator;
10278 gint horizontal_separator;
10280 *cell_area = *background_area;
10282 gtk_widget_style_get (GTK_WIDGET (tree_view),
10283 "vertical-separator", &vertical_separator,
10284 "horizontal-separator", &horizontal_separator,
10286 cell_area->x += horizontal_separator / 2;
10287 cell_area->y += vertical_separator / 2;
10288 cell_area->width -= horizontal_separator;
10289 cell_area->height -= vertical_separator;
10291 if (subtract_focus_rect)
10293 int focus_line_width;
10295 gtk_widget_style_get (GTK_WIDGET (tree_view),
10296 "focus-line-width", &focus_line_width,
10298 cell_area->x += focus_line_width;
10299 cell_area->y += focus_line_width;
10300 cell_area->width -= 2 * focus_line_width;
10301 cell_area->height -= 2 * focus_line_width;
10304 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10306 gint grid_line_width;
10307 gtk_widget_style_get (GTK_WIDGET (tree_view),
10308 "grid-line-width", &grid_line_width,
10311 if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10312 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10315 PsppSheetViewColumn *first_column, *last_column;
10318 /* Find the last visible column. */
10319 last_column = NULL;
10320 for (list = g_list_last (tree_view->priv->columns);
10324 PsppSheetViewColumn *c = list->data;
10332 /* Find the first visible column. */
10333 first_column = NULL;
10334 for (list = g_list_first (tree_view->priv->columns);
10338 PsppSheetViewColumn *c = list->data;
10346 if (column == first_column)
10348 cell_area->width -= grid_line_width / 2;
10350 else if (column == last_column)
10352 cell_area->x += grid_line_width / 2;
10353 cell_area->width -= grid_line_width / 2;
10357 cell_area->x += grid_line_width / 2;
10358 cell_area->width -= grid_line_width;
10362 if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10363 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10365 cell_area->y += grid_line_width / 2;
10366 cell_area->height -= grid_line_width;
10370 if (column == NULL)
10373 cell_area->width = 0;
10378 * pspp_sheet_view_get_cell_area:
10379 * @tree_view: a #PsppSheetView
10380 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10381 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10382 * @rect: rectangle to fill with cell rect
10384 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10385 * row specified by @path and the column specified by @column. If @path is
10386 * %NULL, or points to a path not currently displayed, the @y and @height fields
10387 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10388 * fields will be filled with 0. The sum of all cell rects does not cover the
10389 * entire tree; there are extra pixels in between rows, for example. The
10390 * returned rectangle is equivalent to the @cell_area passed to
10391 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10395 pspp_sheet_view_get_cell_area (PsppSheetView *tree_view,
10397 PsppSheetViewColumn *column,
10398 GdkRectangle *rect)
10400 GdkRectangle background_area;
10402 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10403 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10404 g_return_if_fail (rect != NULL);
10405 g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10406 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10408 pspp_sheet_view_get_background_area (tree_view, path, column,
10410 pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10415 * pspp_sheet_view_get_background_area:
10416 * @tree_view: a #PsppSheetView
10417 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10418 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10419 * @rect: rectangle to fill with cell background rect
10421 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10422 * row specified by @path and the column specified by @column. If @path is
10423 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10424 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10425 * fields will be filled with 0. The returned rectangle is equivalent to the
10426 * @background_area passed to gtk_cell_renderer_render(). These background
10427 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10428 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10429 * itself, excluding surrounding borders.
10433 pspp_sheet_view_get_background_area (PsppSheetView *tree_view,
10435 PsppSheetViewColumn *column,
10436 GdkRectangle *rect)
10440 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10441 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10442 g_return_if_fail (rect != NULL);
10451 /* Get vertical coords */
10453 _pspp_sheet_view_find_node (tree_view, path, &node);
10457 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10459 rect->height = ROW_HEIGHT (tree_view);
10466 pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10467 rect->width = x2 - rect->x;
10472 * pspp_sheet_view_get_visible_rect:
10473 * @tree_view: a #PsppSheetView
10474 * @visible_rect: rectangle to fill
10476 * Fills @visible_rect with the currently-visible region of the
10477 * buffer, in tree coordinates. Convert to bin_window coordinates with
10478 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10479 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10480 * scrollable area of the tree.
10483 pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view,
10484 GdkRectangle *visible_rect)
10488 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10490 widget = GTK_WIDGET (tree_view);
10494 GtkAllocation allocation;
10495 gtk_widget_get_allocation (widget, &allocation);
10496 visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
10497 visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
10498 visible_rect->width = allocation.width;
10499 visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10504 * pspp_sheet_view_widget_to_tree_coords:
10505 * @tree_view: a #PsppSheetView
10506 * @wx: X coordinate relative to bin_window
10507 * @wy: Y coordinate relative to bin_window
10508 * @tx: return location for tree X coordinate
10509 * @ty: return location for tree Y coordinate
10511 * Converts bin_window coordinates to coordinates for the
10512 * tree (the full scrollable area of the tree).
10514 * Deprecated: 2.12: Due to historial reasons the name of this function is
10515 * incorrect. For converting coordinates relative to the widget to
10516 * bin_window coordinates, please see
10517 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10521 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10527 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10530 *tx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10532 *ty = wy + tree_view->priv->dy;
10536 * pspp_sheet_view_tree_to_widget_coords:
10537 * @tree_view: a #PsppSheetView
10538 * @tx: tree X coordinate
10539 * @ty: tree Y coordinate
10540 * @wx: return location for X coordinate relative to bin_window
10541 * @wy: return location for Y coordinate relative to bin_window
10543 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10544 * to bin_window coordinates.
10546 * Deprecated: 2.12: Due to historial reasons the name of this function is
10547 * incorrect. For converting bin_window coordinates to coordinates relative
10548 * to bin_window, please see
10549 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10553 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10559 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10562 *wx = tx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10564 *wy = ty - tree_view->priv->dy;
10569 * pspp_sheet_view_convert_widget_to_tree_coords:
10570 * @tree_view: a #PsppSheetView
10571 * @wx: X coordinate relative to the widget
10572 * @wy: Y coordinate relative to the widget
10573 * @tx: return location for tree X coordinate
10574 * @ty: return location for tree Y coordinate
10576 * Converts widget coordinates to coordinates for the
10577 * tree (the full scrollable area of the tree).
10582 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10590 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10592 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10595 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10601 * pspp_sheet_view_convert_tree_to_widget_coords:
10602 * @tree_view: a #PsppSheetView
10603 * @tx: X coordinate relative to the tree
10604 * @ty: Y coordinate relative to the tree
10605 * @wx: return location for widget X coordinate
10606 * @wy: return location for widget Y coordinate
10608 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10609 * to widget coordinates.
10614 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10622 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10624 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10627 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10633 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10634 * @tree_view: a #PsppSheetView
10635 * @wx: X coordinate relative to the widget
10636 * @wy: Y coordinate relative to the widget
10637 * @bx: return location for bin_window X coordinate
10638 * @by: return location for bin_window Y coordinate
10640 * Converts widget coordinates to coordinates for the bin_window
10641 * (see pspp_sheet_view_get_bin_window()).
10646 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10652 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10655 *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10657 *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10661 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10662 * @tree_view: a #PsppSheetView
10663 * @bx: bin_window X coordinate
10664 * @by: bin_window Y coordinate
10665 * @wx: return location for widget X coordinate
10666 * @wy: return location for widget Y coordinate
10668 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10669 * to widget relative coordinates.
10674 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10680 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10683 *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10685 *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10689 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10690 * @tree_view: a #PsppSheetView
10691 * @tx: tree X coordinate
10692 * @ty: tree Y coordinate
10693 * @bx: return location for X coordinate relative to bin_window
10694 * @by: return location for Y coordinate relative to bin_window
10696 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10697 * to bin_window coordinates.
10702 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10708 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10713 *by = ty - tree_view->priv->dy;
10717 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10718 * @tree_view: a #PsppSheetView
10719 * @bx: X coordinate relative to bin_window
10720 * @by: Y coordinate relative to bin_window
10721 * @tx: return location for tree X coordinate
10722 * @ty: return location for tree Y coordinate
10724 * Converts bin_window coordinates to coordinates for the
10725 * tree (the full scrollable area of the tree).
10730 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10736 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10741 *ty = by + tree_view->priv->dy;
10747 * pspp_sheet_view_get_visible_range:
10748 * @tree_view: A #PsppSheetView
10749 * @start_path: (allow-none): Return location for start of region, or %NULL.
10750 * @end_path: (allow-none): Return location for end of region, or %NULL.
10752 * Sets @start_path and @end_path to be the first and last visible path.
10753 * Note that there may be invisible paths in between.
10755 * The paths should be freed with gtk_tree_path_free() after use.
10757 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
10762 pspp_sheet_view_get_visible_range (PsppSheetView *tree_view,
10763 GtkTreePath **start_path,
10764 GtkTreePath **end_path)
10769 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10771 if (!tree_view->priv->row_count)
10778 pspp_sheet_view_find_offset (tree_view,
10779 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
10782 *start_path = _pspp_sheet_view_find_path (tree_view, node);
10791 if (tree_view->priv->height < gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
10792 y = tree_view->priv->height - 1;
10794 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1;
10796 pspp_sheet_view_find_offset (tree_view, y, &node);
10798 *end_path = _pspp_sheet_view_find_path (tree_view, node);
10807 unset_reorderable (PsppSheetView *tree_view)
10809 if (tree_view->priv->reorderable)
10811 tree_view->priv->reorderable = FALSE;
10812 g_object_notify (G_OBJECT (tree_view), "reorderable");
10817 * pspp_sheet_view_enable_model_drag_source:
10818 * @tree_view: a #PsppSheetView
10819 * @start_button_mask: Mask of allowed buttons to start drag
10820 * @targets: the table of targets that the drag will support
10821 * @n_targets: the number of items in @targets
10822 * @actions: the bitmask of possible actions for a drag from this
10825 * Turns @tree_view into a drag source for automatic DND. Calling this
10826 * method sets #PsppSheetView:reorderable to %FALSE.
10829 pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view,
10830 GdkModifierType start_button_mask,
10831 const GtkTargetEntry *targets,
10833 GdkDragAction actions)
10835 TreeViewDragInfo *di;
10837 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10839 gtk_drag_source_set (GTK_WIDGET (tree_view),
10845 di = ensure_info (tree_view);
10847 di->start_button_mask = start_button_mask;
10848 di->source_actions = actions;
10849 di->source_set = TRUE;
10851 unset_reorderable (tree_view);
10855 * pspp_sheet_view_enable_model_drag_dest:
10856 * @tree_view: a #PsppSheetView
10857 * @targets: the table of targets that the drag will support
10858 * @n_targets: the number of items in @targets
10859 * @actions: the bitmask of possible actions for a drag from this
10862 * Turns @tree_view into a drop destination for automatic DND. Calling
10863 * this method sets #PsppSheetView:reorderable to %FALSE.
10866 pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view,
10867 const GtkTargetEntry *targets,
10869 GdkDragAction actions)
10871 TreeViewDragInfo *di;
10873 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10875 gtk_drag_dest_set (GTK_WIDGET (tree_view),
10881 di = ensure_info (tree_view);
10882 di->dest_set = TRUE;
10884 unset_reorderable (tree_view);
10888 * pspp_sheet_view_unset_rows_drag_source:
10889 * @tree_view: a #PsppSheetView
10891 * Undoes the effect of
10892 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
10893 * #PsppSheetView:reorderable to %FALSE.
10896 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
10898 TreeViewDragInfo *di;
10900 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10902 di = get_info (tree_view);
10906 if (di->source_set)
10908 gtk_drag_source_unset (GTK_WIDGET (tree_view));
10909 di->source_set = FALSE;
10912 if (!di->dest_set && !di->source_set)
10913 remove_info (tree_view);
10916 unset_reorderable (tree_view);
10920 * pspp_sheet_view_unset_rows_drag_dest:
10921 * @tree_view: a #PsppSheetView
10923 * Undoes the effect of
10924 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
10925 * #PsppSheetView:reorderable to %FALSE.
10928 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
10930 TreeViewDragInfo *di;
10932 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10934 di = get_info (tree_view);
10940 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
10941 di->dest_set = FALSE;
10944 if (!di->dest_set && !di->source_set)
10945 remove_info (tree_view);
10948 unset_reorderable (tree_view);
10952 * pspp_sheet_view_set_drag_dest_row:
10953 * @tree_view: a #PsppSheetView
10954 * @path: (allow-none): The path of the row to highlight, or %NULL.
10955 * @pos: Specifies whether to drop before, after or into the row
10957 * Sets the row that is highlighted for feedback.
10960 pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view,
10962 PsppSheetViewDropPosition pos)
10964 GtkTreePath *current_dest;
10966 /* Note; this function is exported to allow a custom DND
10967 * implementation, so it can't touch TreeViewDragInfo
10970 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10972 current_dest = NULL;
10974 if (tree_view->priv->drag_dest_row)
10976 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
10977 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
10980 /* special case a drop on an empty model */
10981 tree_view->priv->empty_view_drop = 0;
10983 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
10984 && gtk_tree_path_get_depth (path) == 1
10985 && gtk_tree_path_get_indices (path)[0] == 0)
10989 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
10993 tree_view->priv->empty_view_drop = 1;
10996 tree_view->priv->drag_dest_pos = pos;
11000 tree_view->priv->drag_dest_row =
11001 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
11002 pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
11005 tree_view->priv->drag_dest_row = NULL;
11009 int node, new_node;
11011 _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11012 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11016 new_node = pspp_sheet_view_node_next (tree_view, node);
11018 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11020 new_node = pspp_sheet_view_node_prev (tree_view, node);
11022 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11024 gtk_tree_path_free (current_dest);
11029 * pspp_sheet_view_get_drag_dest_row:
11030 * @tree_view: a #PsppSheetView
11031 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11032 * @pos: (allow-none): Return location for the drop position, or %NULL
11034 * Gets information about the row that is highlighted for feedback.
11037 pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view,
11038 GtkTreePath **path,
11039 PsppSheetViewDropPosition *pos)
11041 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11045 if (tree_view->priv->drag_dest_row)
11046 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11049 if (tree_view->priv->empty_view_drop)
11050 *path = gtk_tree_path_new_from_indices (0, -1);
11057 *pos = tree_view->priv->drag_dest_pos;
11061 * pspp_sheet_view_get_dest_row_at_pos:
11062 * @tree_view: a #PsppSheetView
11063 * @drag_x: the position to determine the destination row for
11064 * @drag_y: the position to determine the destination row for
11065 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11066 * @pos: (allow-none): Return location for the drop position, or %NULL
11068 * Determines the destination row for a given position. @drag_x and
11069 * @drag_y are expected to be in widget coordinates. This function is only
11070 * meaningful if @tree_view is realized. Therefore this function will always
11071 * return %FALSE if @tree_view is not realized or does not have a model.
11073 * Return value: whether there is a row at the given position, %TRUE if this
11074 * is indeed the case.
11077 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view,
11080 GtkTreePath **path,
11081 PsppSheetViewDropPosition *pos)
11085 gdouble offset_into_row;
11088 PsppSheetViewColumn *column = NULL;
11089 GtkTreePath *tmp_path = NULL;
11091 /* Note; this function is exported to allow a custom DND
11092 * implementation, so it can't touch TreeViewDragInfo
11095 g_return_val_if_fail (tree_view != NULL, FALSE);
11096 g_return_val_if_fail (drag_x >= 0, FALSE);
11097 g_return_val_if_fail (drag_y >= 0, FALSE);
11102 if (tree_view->priv->bin_window == NULL)
11105 if (tree_view->priv->row_count == 0)
11108 /* If in the top third of a row, we drop before that row; if
11109 * in the bottom third, drop after that row; if in the middle,
11110 * and the row has children, drop into the row.
11112 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11115 if (!pspp_sheet_view_get_path_at_pos (tree_view,
11124 pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11127 offset_into_row = cell_y;
11132 gtk_tree_path_free (tmp_path);
11136 third = cell.height / 3.0;
11140 if (offset_into_row < third)
11142 *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11144 else if (offset_into_row < (cell.height / 2.0))
11146 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11148 else if (offset_into_row < third * 2.0)
11150 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11154 *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11162 #if GTK3_TRANSITION
11163 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11165 * pspp_sheet_view_create_row_drag_icon:
11166 * @tree_view: a #PsppSheetView
11167 * @path: a #GtkTreePath in @tree_view
11169 * Creates a #GdkPixmap representation of the row at @path.
11170 * This image is used for a drag icon.
11172 * Return value: a newly-allocated pixmap of the drag icon.
11175 pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view,
11182 GdkRectangle background_area;
11183 GdkRectangle expose_area;
11185 /* start drawing inside the black outline */
11187 GdkDrawable *drawable;
11188 gint bin_window_width;
11191 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11192 g_return_val_if_fail (path != NULL, NULL);
11194 widget = GTK_WIDGET (tree_view);
11196 if (!gtk_widget_get_realized (widget))
11199 _pspp_sheet_view_find_node (tree_view,
11206 if (!gtk_tree_model_get_iter (tree_view->priv->model,
11213 background_area.y = y;
11214 background_area.height = ROW_HEIGHT (tree_view);
11216 bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
11218 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11219 bin_window_width + 2,
11220 background_area.height + 2,
11225 expose_area.width = bin_window_width + 2;
11226 expose_area.height = background_area.height + 2;
11228 #if GTK3_TRANSITION
11229 gdk_draw_rectangle (drawable,
11230 widget->style->base_gc [gtk_widget_get_state (widget)],
11233 bin_window_width + 2,
11234 background_area.height + 2);
11237 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11239 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11241 list = (rtl ? list->prev : list->next))
11243 PsppSheetViewColumn *column = list->data;
11244 GdkRectangle cell_area;
11245 gint vertical_separator;
11247 if (!column->visible)
11250 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11252 background_area.x = cell_offset;
11253 background_area.width = column->width;
11255 gtk_widget_style_get (widget,
11256 "vertical-separator", &vertical_separator,
11259 cell_area = background_area;
11261 cell_area.y += vertical_separator / 2;
11262 cell_area.height -= vertical_separator;
11264 if (pspp_sheet_view_column_cell_is_visible (column))
11265 _pspp_sheet_view_column_cell_render (column,
11271 cell_offset += column->width;
11274 #if GTK3_TRANSITION
11275 gdk_draw_rectangle (drawable,
11276 widget->style->black_gc,
11279 bin_window_width + 1,
11280 background_area.height + 1);
11288 * pspp_sheet_view_set_destroy_count_func:
11289 * @tree_view: A #PsppSheetView
11290 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11291 * @data: (allow-none): User data to be passed to @func, or %NULL
11292 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11294 * This function should almost never be used. It is meant for private use by
11295 * ATK for determining the number of visible children that are removed when a row is deleted.
11298 pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view,
11299 PsppSheetDestroyCountFunc func,
11301 GDestroyNotify destroy)
11303 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11305 if (tree_view->priv->destroy_count_destroy)
11306 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11308 tree_view->priv->destroy_count_func = func;
11309 tree_view->priv->destroy_count_data = data;
11310 tree_view->priv->destroy_count_destroy = destroy;
11315 * Interactive search
11319 * pspp_sheet_view_set_enable_search:
11320 * @tree_view: A #PsppSheetView
11321 * @enable_search: %TRUE, if the user can search interactively
11323 * If @enable_search is set, then the user can type in text to search through
11324 * the tree interactively (this is sometimes called "typeahead find").
11326 * Note that even if this is %FALSE, the user can still initiate a search
11327 * using the "start-interactive-search" key binding.
11330 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11331 gboolean enable_search)
11333 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11335 enable_search = !!enable_search;
11337 if (tree_view->priv->enable_search != enable_search)
11339 tree_view->priv->enable_search = enable_search;
11340 g_object_notify (G_OBJECT (tree_view), "enable-search");
11345 * pspp_sheet_view_get_enable_search:
11346 * @tree_view: A #PsppSheetView
11348 * Returns whether or not the tree allows to start interactive searching
11349 * by typing in text.
11351 * Return value: whether or not to let the user search interactively
11354 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11356 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11358 return tree_view->priv->enable_search;
11363 * pspp_sheet_view_get_search_column:
11364 * @tree_view: A #PsppSheetView
11366 * Gets the column searched on by the interactive search code.
11368 * Return value: the column the interactive search code searches in.
11371 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11373 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11375 return (tree_view->priv->search_column);
11379 * pspp_sheet_view_set_search_column:
11380 * @tree_view: A #PsppSheetView
11381 * @column: the column of the model to search in, or -1 to disable searching
11383 * Sets @column as the column where the interactive search code should
11384 * search in for the current model.
11386 * If the search column is set, users can use the "start-interactive-search"
11387 * key binding to bring up search popup. The enable-search property controls
11388 * whether simply typing text will also start an interactive search.
11390 * Note that @column refers to a column of the current model. The search
11391 * column is reset to -1 when the model is changed.
11394 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11397 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11398 g_return_if_fail (column >= -1);
11400 if (tree_view->priv->search_column == column)
11403 tree_view->priv->search_column = column;
11404 g_object_notify (G_OBJECT (tree_view), "search-column");
11408 * pspp_sheet_view_get_search_equal_func:
11409 * @tree_view: A #PsppSheetView
11411 * Returns the compare function currently in use.
11413 * Return value: the currently used compare function for the search code.
11416 PsppSheetViewSearchEqualFunc
11417 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11419 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11421 return tree_view->priv->search_equal_func;
11425 * pspp_sheet_view_set_search_equal_func:
11426 * @tree_view: A #PsppSheetView
11427 * @search_equal_func: the compare function to use during the search
11428 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11429 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11431 * Sets the compare function for the interactive search capabilities; note
11432 * that somewhat like strcmp() returning 0 for equality
11433 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11436 pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view,
11437 PsppSheetViewSearchEqualFunc search_equal_func,
11438 gpointer search_user_data,
11439 GDestroyNotify search_destroy)
11441 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11442 g_return_if_fail (search_equal_func != NULL);
11444 if (tree_view->priv->search_destroy)
11445 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11447 tree_view->priv->search_equal_func = search_equal_func;
11448 tree_view->priv->search_user_data = search_user_data;
11449 tree_view->priv->search_destroy = search_destroy;
11450 if (tree_view->priv->search_equal_func == NULL)
11451 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11455 * pspp_sheet_view_get_search_entry:
11456 * @tree_view: A #PsppSheetView
11458 * Returns the #GtkEntry which is currently in use as interactive search
11459 * entry for @tree_view. In case the built-in entry is being used, %NULL
11460 * will be returned.
11462 * Return value: the entry currently in use as search entry.
11467 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11469 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11471 if (tree_view->priv->search_custom_entry_set)
11472 return GTK_ENTRY (tree_view->priv->search_entry);
11478 * pspp_sheet_view_set_search_entry:
11479 * @tree_view: A #PsppSheetView
11480 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11482 * Sets the entry which the interactive search code will use for this
11483 * @tree_view. This is useful when you want to provide a search entry
11484 * in our interface at all time at a fixed position. Passing %NULL for
11485 * @entry will make the interactive search code use the built-in popup
11491 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11494 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11495 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11497 if (tree_view->priv->search_custom_entry_set)
11499 if (tree_view->priv->search_entry_changed_id)
11501 g_signal_handler_disconnect (tree_view->priv->search_entry,
11502 tree_view->priv->search_entry_changed_id);
11503 tree_view->priv->search_entry_changed_id = 0;
11505 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11506 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11509 g_object_unref (tree_view->priv->search_entry);
11511 else if (tree_view->priv->search_window)
11513 gtk_widget_destroy (tree_view->priv->search_window);
11515 tree_view->priv->search_window = NULL;
11520 tree_view->priv->search_entry = g_object_ref (entry);
11521 tree_view->priv->search_custom_entry_set = TRUE;
11523 if (tree_view->priv->search_entry_changed_id == 0)
11525 tree_view->priv->search_entry_changed_id =
11526 g_signal_connect (tree_view->priv->search_entry, "changed",
11527 G_CALLBACK (pspp_sheet_view_search_init),
11531 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11532 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11535 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11539 tree_view->priv->search_entry = NULL;
11540 tree_view->priv->search_custom_entry_set = FALSE;
11545 * pspp_sheet_view_set_search_position_func:
11546 * @tree_view: A #PsppSheetView
11547 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11548 * to use the default search position function
11549 * @data: (allow-none): user data to pass to @func, or %NULL
11550 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11552 * Sets the function to use when positioning the search dialog.
11557 pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view,
11558 PsppSheetViewSearchPositionFunc func,
11559 gpointer user_data,
11560 GDestroyNotify destroy)
11562 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11564 if (tree_view->priv->search_position_destroy)
11565 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11567 tree_view->priv->search_position_func = func;
11568 tree_view->priv->search_position_user_data = user_data;
11569 tree_view->priv->search_position_destroy = destroy;
11570 if (tree_view->priv->search_position_func == NULL)
11571 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11575 * pspp_sheet_view_get_search_position_func:
11576 * @tree_view: A #PsppSheetView
11578 * Returns the positioning function currently in use.
11580 * Return value: the currently used function for positioning the search dialog.
11584 PsppSheetViewSearchPositionFunc
11585 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11587 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11589 return tree_view->priv->search_position_func;
11594 pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
11595 PsppSheetView *tree_view)
11597 if (tree_view->priv->disable_popdown)
11600 if (tree_view->priv->search_entry_changed_id)
11602 g_signal_handler_disconnect (tree_view->priv->search_entry,
11603 tree_view->priv->search_entry_changed_id);
11604 tree_view->priv->search_entry_changed_id = 0;
11606 if (tree_view->priv->typeselect_flush_timeout)
11608 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11609 tree_view->priv->typeselect_flush_timeout = 0;
11612 if (gtk_widget_get_visible (search_dialog))
11614 /* send focus-in event */
11615 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11616 gtk_widget_hide (search_dialog);
11617 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11618 send_focus_change (GTK_WIDGET (tree_view), TRUE);
11623 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11624 GtkWidget *search_dialog,
11625 gpointer user_data)
11628 gint tree_x, tree_y;
11629 gint tree_width, tree_height;
11630 GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
11631 GdkScreen *screen = gdk_window_get_screen (tree_window);
11632 GtkRequisition requisition;
11634 GdkRectangle monitor;
11636 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11637 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11639 gtk_widget_realize (search_dialog);
11641 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11642 tree_width = gdk_window_get_width (tree_window);
11643 tree_height = gdk_window_get_height (tree_window);
11645 gtk_widget_size_request (search_dialog, &requisition);
11647 if (tree_x + tree_width > gdk_screen_get_width (screen))
11648 x = gdk_screen_get_width (screen) - requisition.width;
11649 else if (tree_x + tree_width - requisition.width < 0)
11652 x = tree_x + tree_width - requisition.width;
11654 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11655 y = gdk_screen_get_height (screen) - requisition.height;
11656 else if (tree_y + tree_height < 0) /* isn't really possible ... */
11659 y = tree_y + tree_height;
11661 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11665 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11669 PsppSheetView *tree_view = (PsppSheetView *)data;
11671 tree_view->priv->disable_popdown = 1;
11672 g_signal_connect (menu, "hide",
11673 G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11676 #if GTK3_TRANSITION
11677 /* Because we're visible but offscreen, we just set a flag in the preedit
11681 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11682 PsppSheetView *tree_view)
11684 tree_view->priv->imcontext_changed = 1;
11685 if (tree_view->priv->typeselect_flush_timeout)
11687 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11688 tree_view->priv->typeselect_flush_timeout =
11689 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11690 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11698 pspp_sheet_view_search_activate (GtkEntry *entry,
11699 PsppSheetView *tree_view)
11704 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11707 /* If we have a row selected and it's the cursor row, we activate
11709 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11711 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11713 _pspp_sheet_view_find_node (tree_view, path, &node);
11715 if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11716 pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11718 gtk_tree_path_free (path);
11723 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11725 PsppSheetView *tree_view = (PsppSheetView *)data;
11727 tree_view->priv->disable_popdown = 0;
11733 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11736 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11740 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11741 GdkEventAny *event,
11742 PsppSheetView *tree_view)
11744 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11746 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11752 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11753 GdkEventButton *event,
11754 PsppSheetView *tree_view)
11756 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11758 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11760 if (event->window == tree_view->priv->bin_window)
11761 pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
11767 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
11768 GdkEventScroll *event,
11769 PsppSheetView *tree_view)
11771 gboolean retval = FALSE;
11773 if (event->direction == GDK_SCROLL_UP)
11775 pspp_sheet_view_search_move (widget, tree_view, TRUE);
11778 else if (event->direction == GDK_SCROLL_DOWN)
11780 pspp_sheet_view_search_move (widget, tree_view, FALSE);
11784 /* renew the flush timeout */
11785 if (retval && tree_view->priv->typeselect_flush_timeout
11786 && !tree_view->priv->search_custom_entry_set)
11788 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11789 tree_view->priv->typeselect_flush_timeout =
11790 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11791 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11799 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
11800 GdkEventKey *event,
11801 PsppSheetView *tree_view)
11803 gboolean retval = FALSE;
11805 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11806 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11808 /* close window and cancel the search */
11809 if (!tree_view->priv->search_custom_entry_set
11810 && (event->keyval == GDK_Escape ||
11811 event->keyval == GDK_Tab ||
11812 event->keyval == GDK_KP_Tab ||
11813 event->keyval == GDK_ISO_Left_Tab))
11815 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11819 /* select previous matching iter */
11820 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
11822 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11823 gtk_widget_error_bell (widget);
11828 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
11829 && (event->keyval == GDK_g || event->keyval == GDK_G))
11831 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11832 gtk_widget_error_bell (widget);
11837 /* select next matching iter */
11838 if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
11840 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11841 gtk_widget_error_bell (widget);
11846 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
11847 && (event->keyval == GDK_g || event->keyval == GDK_G))
11849 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11850 gtk_widget_error_bell (widget);
11855 /* renew the flush timeout */
11856 if (retval && tree_view->priv->typeselect_flush_timeout
11857 && !tree_view->priv->search_custom_entry_set)
11859 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11860 tree_view->priv->typeselect_flush_timeout =
11861 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11862 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11869 /* this function returns FALSE if there is a search string but
11870 * nothing was found, and TRUE otherwise.
11873 pspp_sheet_view_search_move (GtkWidget *window,
11874 PsppSheetView *tree_view,
11882 GtkTreeModel *model;
11883 PsppSheetSelection *selection;
11885 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
11887 g_return_val_if_fail (text != NULL, FALSE);
11889 len = strlen (text);
11891 if (up && tree_view->priv->selected_iter == 1)
11892 return strlen (text) < 1;
11894 len = strlen (text);
11899 model = pspp_sheet_view_get_model (tree_view);
11900 selection = pspp_sheet_view_get_selection (tree_view);
11903 pspp_sheet_selection_unselect_all (selection);
11904 if (!gtk_tree_model_get_iter_first (model, &iter))
11907 ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
11908 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
11913 tree_view->priv->selected_iter += up?(-1):(1);
11918 /* return to old iter */
11920 gtk_tree_model_get_iter_first (model, &iter);
11921 pspp_sheet_view_search_iter (model, selection,
11923 &count, tree_view->priv->selected_iter);
11929 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
11933 gpointer search_data)
11935 gboolean retval = TRUE;
11937 gchar *normalized_string;
11938 gchar *normalized_key;
11939 gchar *case_normalized_string = NULL;
11940 gchar *case_normalized_key = NULL;
11941 GValue value = {0,};
11942 GValue transformed = {0,};
11944 gtk_tree_model_get_value (model, iter, column, &value);
11946 g_value_init (&transformed, G_TYPE_STRING);
11948 if (!g_value_transform (&value, &transformed))
11950 g_value_unset (&value);
11954 g_value_unset (&value);
11956 str = g_value_get_string (&transformed);
11959 g_value_unset (&transformed);
11963 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
11964 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
11966 if (normalized_string && normalized_key)
11968 case_normalized_string = g_utf8_casefold (normalized_string, -1);
11969 case_normalized_key = g_utf8_casefold (normalized_key, -1);
11971 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
11975 g_value_unset (&transformed);
11976 g_free (normalized_key);
11977 g_free (normalized_string);
11978 g_free (case_normalized_key);
11979 g_free (case_normalized_string);
11985 pspp_sheet_view_search_iter (GtkTreeModel *model,
11986 PsppSheetSelection *selection,
11995 PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
11997 path = gtk_tree_model_get_path (model, iter);
11998 _pspp_sheet_view_find_node (tree_view, path, &node);
12002 gboolean done = FALSE;
12004 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
12009 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
12011 pspp_sheet_selection_select_iter (selection, iter);
12012 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12015 gtk_tree_path_free (path);
12024 node = pspp_sheet_view_node_next (tree_view, node);
12030 has_next = gtk_tree_model_iter_next (model, iter);
12033 gtk_tree_path_next (path);
12036 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12041 gtk_tree_path_free (path);
12043 /* we've run out of tree, done with this func */
12055 pspp_sheet_view_search_init (GtkWidget *entry,
12056 PsppSheetView *tree_view)
12062 GtkTreeModel *model;
12063 PsppSheetSelection *selection;
12065 g_return_if_fail (GTK_IS_ENTRY (entry));
12066 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12068 text = gtk_entry_get_text (GTK_ENTRY (entry));
12070 model = pspp_sheet_view_get_model (tree_view);
12071 selection = pspp_sheet_view_get_selection (tree_view);
12074 pspp_sheet_selection_unselect_all (selection);
12075 if (tree_view->priv->typeselect_flush_timeout
12076 && !tree_view->priv->search_custom_entry_set)
12078 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12079 tree_view->priv->typeselect_flush_timeout =
12080 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12081 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12088 if (!gtk_tree_model_get_iter_first (model, &iter))
12091 ret = pspp_sheet_view_search_iter (model, selection,
12096 tree_view->priv->selected_iter = 1;
12100 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12101 PsppSheetView *tree_view)
12103 if (tree_view->priv->edited_column == NULL)
12106 _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12107 tree_view->priv->edited_column = NULL;
12109 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12110 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12112 g_signal_handlers_disconnect_by_func (cell_editable,
12113 pspp_sheet_view_remove_widget,
12115 g_signal_handlers_disconnect_by_func (cell_editable,
12116 pspp_sheet_view_editable_button_press_event,
12118 g_signal_handlers_disconnect_by_func (cell_editable,
12119 pspp_sheet_view_editable_clicked,
12122 gtk_container_remove (GTK_CONTAINER (tree_view),
12123 GTK_WIDGET (cell_editable));
12125 /* FIXME should only redraw a single node */
12126 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12130 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12131 GtkTreePath *cursor_path)
12134 GdkRectangle background_area;
12135 GdkRectangle cell_area;
12136 GtkCellEditable *editable_widget = NULL;
12137 gchar *path_string;
12138 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12139 gint retval = FALSE;
12142 g_assert (tree_view->priv->focus_column);
12144 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12147 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12148 if (cursor_node < 0)
12151 path_string = gtk_tree_path_to_string (cursor_path);
12152 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12154 pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12155 tree_view->priv->model,
12157 pspp_sheet_view_get_background_area (tree_view,
12159 tree_view->priv->focus_column,
12161 pspp_sheet_view_get_cell_area (tree_view,
12163 tree_view->priv->focus_column,
12166 if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12175 if (editable_widget != NULL)
12179 GtkCellRenderer *cell;
12182 cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12184 _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12187 area.width -= right + left;
12189 pspp_sheet_view_real_start_editing (tree_view,
12190 tree_view->priv->focus_column,
12199 g_free (path_string);
12204 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12205 GdkEventButton *event,
12206 PsppSheetView *sheet_view)
12210 node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12211 "pspp-sheet-view-node"));
12212 return pspp_sheet_view_row_head_clicked (sheet_view,
12214 sheet_view->priv->edited_column,
12219 pspp_sheet_view_editable_clicked (GtkButton *button,
12220 PsppSheetView *sheet_view)
12222 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12227 is_all_selected (GtkWidget *widget)
12229 GtkEntryBuffer *buffer;
12230 gint start_pos, end_pos;
12232 if (!GTK_IS_ENTRY (widget))
12235 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12236 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12237 &start_pos, &end_pos)
12239 && end_pos == gtk_entry_buffer_get_length (buffer));
12243 is_at_left (GtkWidget *widget)
12245 return (GTK_IS_ENTRY (widget)
12246 && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12250 is_at_right (GtkWidget *widget)
12252 GtkEntryBuffer *buffer;
12255 if (!GTK_IS_ENTRY (widget))
12258 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12259 length = gtk_entry_buffer_get_length (buffer);
12260 return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12264 pspp_sheet_view_event (GtkWidget *widget,
12265 GdkEventKey *event,
12266 PsppSheetView *tree_view)
12268 PsppSheetViewColumn *column;
12275 /* Intercept only key press events.
12276 It would make sense to use "key-press-event" instead of "event", but
12277 GtkEntry attaches its own signal handler to "key-press-event" that runs
12278 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12280 if (event->type != GDK_KEY_PRESS)
12283 keyval = event->keyval;
12285 switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12288 switch (event->keyval)
12290 case GDK_Left: case GDK_KP_Left:
12291 case GDK_Home: case GDK_KP_Home:
12292 if (!is_all_selected (widget) && !is_at_left (widget))
12296 case GDK_Right: case GDK_KP_Right:
12297 case GDK_End: case GDK_KP_End:
12298 if (!is_all_selected (widget) && !is_at_right (widget))
12302 case GDK_Up: case GDK_KP_Up:
12303 case GDK_Down: case GDK_KP_Down:
12306 case GDK_Page_Up: case GDK_KP_Page_Up:
12307 case GDK_Page_Down: case GDK_KP_Page_Down:
12318 case GDK_Tab: case GDK_KP_Tab:
12319 case GDK_ISO_Left_Tab:
12328 case GDK_SHIFT_MASK:
12329 switch (event->keyval)
12332 case GDK_ISO_Left_Tab:
12341 case GDK_CONTROL_MASK:
12342 switch (event->keyval)
12344 case GDK_Left: case GDK_KP_Left:
12345 if (!is_all_selected (widget) && !is_at_left (widget))
12349 case GDK_Right: case GDK_KP_Right:
12350 if (!is_all_selected (widget) && !is_at_right (widget))
12354 case GDK_Up: case GDK_KP_Up:
12355 case GDK_Down: case GDK_KP_Down:
12367 row = tree_view->priv->edited_row;
12368 column = tree_view->priv->edited_column;
12369 path = gtk_tree_path_new_from_indices (row, -1);
12371 pspp_sheet_view_stop_editing (tree_view, cancel);
12372 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12374 pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12375 gtk_tree_path_free (path);
12377 handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12378 G_OBJECT (tree_view));
12380 g_signal_stop_emission_by_name (widget, "event");
12382 pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12383 pspp_sheet_view_start_editing (tree_view, path);
12384 gtk_tree_path_free (path);
12390 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12393 PsppSheetView *sheet_view = data;
12395 g_signal_connect (widget, "event",
12396 G_CALLBACK (pspp_sheet_view_event),
12399 if (GTK_IS_CONTAINER (widget))
12400 gtk_container_foreach (GTK_CONTAINER (widget),
12401 pspp_sheet_view_override_cell_keypresses,
12406 pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
12407 PsppSheetViewColumn *column,
12409 GtkCellEditable *cell_editable,
12410 GdkRectangle *cell_area,
12414 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12415 gint pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
12418 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12420 tree_view->priv->edited_column = column;
12421 _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12423 row = gtk_tree_path_get_indices (path)[0];
12424 tree_view->priv->edited_row = row;
12425 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12426 cell_area->y += pre_val - (int)gtk_adjustment_get_value (tree_view->priv->vadjustment);
12428 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12429 pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12430 tree_view->priv->anchor_column = column;
12432 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12434 pspp_sheet_view_put (tree_view,
12435 GTK_WIDGET (cell_editable),
12439 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12440 (GdkEvent *)event);
12442 gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12443 g_signal_connect (cell_editable, "remove-widget",
12444 G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12445 if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12446 GTK_IS_BUTTON (cell_editable))
12448 g_signal_connect (cell_editable, "button-press-event",
12449 G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12451 g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12452 GINT_TO_POINTER (row));
12453 g_signal_connect (cell_editable, "clicked",
12454 G_CALLBACK (pspp_sheet_view_editable_clicked),
12458 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12463 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12464 gboolean cancel_editing)
12466 PsppSheetViewColumn *column;
12467 GtkCellRenderer *cell;
12469 if (tree_view->priv->edited_column == NULL)
12473 * This is very evil. We need to do this, because
12474 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12475 * later on. If pspp_sheet_view_row_changed notices
12476 * tree_view->priv->edited_column != NULL, it'll call
12477 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12479 * Please read that again if you intend to modify anything here.
12482 column = tree_view->priv->edited_column;
12483 tree_view->priv->edited_column = NULL;
12485 cell = _pspp_sheet_view_column_get_edited_cell (column);
12486 gtk_cell_renderer_stop_editing (cell, cancel_editing);
12488 if (!cancel_editing)
12489 gtk_cell_editable_editing_done (column->editable_widget);
12491 tree_view->priv->edited_column = column;
12493 gtk_cell_editable_remove_widget (column->editable_widget);
12498 * pspp_sheet_view_set_hover_selection:
12499 * @tree_view: a #PsppSheetView
12500 * @hover: %TRUE to enable hover selection mode
12502 * Enables of disables the hover selection mode of @tree_view.
12503 * Hover selection makes the selected row follow the pointer.
12504 * Currently, this works only for the selection modes
12505 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12510 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12513 hover = hover != FALSE;
12515 if (hover != tree_view->priv->hover_selection)
12517 tree_view->priv->hover_selection = hover;
12519 g_object_notify (G_OBJECT (tree_view), "hover-selection");
12524 * pspp_sheet_view_get_hover_selection:
12525 * @tree_view: a #PsppSheetView
12527 * Returns whether hover selection mode is turned on for @tree_view.
12529 * Return value: %TRUE if @tree_view is in hover selection mode
12534 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12536 return tree_view->priv->hover_selection;
12540 * pspp_sheet_view_set_rubber_banding:
12541 * @tree_view: a #PsppSheetView
12542 * @enable: %TRUE to enable rubber banding
12544 * Enables or disables rubber banding in @tree_view. If the selection mode is
12545 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12546 * banding will allow the user to select multiple rows by dragging the mouse.
12551 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12554 enable = enable != FALSE;
12556 if (enable != tree_view->priv->rubber_banding_enable)
12558 tree_view->priv->rubber_banding_enable = enable;
12560 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12565 * pspp_sheet_view_get_rubber_banding:
12566 * @tree_view: a #PsppSheetView
12568 * Returns whether rubber banding is turned on for @tree_view. If the
12569 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12570 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12571 * select multiple rows by dragging the mouse.
12573 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12578 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12580 return tree_view->priv->rubber_banding_enable;
12584 * pspp_sheet_view_is_rubber_banding_active:
12585 * @tree_view: a #PsppSheetView
12587 * Returns whether a rubber banding operation is currently being done
12590 * Return value: %TRUE if a rubber banding operation is currently being
12591 * done in @tree_view.
12596 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12598 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12600 if (tree_view->priv->rubber_banding_enable
12601 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12608 pspp_sheet_view_grab_notify (GtkWidget *widget,
12609 gboolean was_grabbed)
12611 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12613 tree_view->priv->in_grab = !was_grabbed;
12617 tree_view->priv->pressed_button = -1;
12619 if (tree_view->priv->rubber_band_status)
12620 pspp_sheet_view_stop_rubber_band (tree_view);
12625 pspp_sheet_view_state_changed (GtkWidget *widget,
12626 GtkStateType previous_state)
12628 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12630 if (gtk_widget_get_realized (widget))
12632 GtkStyle *style = gtk_widget_get_style (widget);
12633 gdk_window_set_background (tree_view->priv->bin_window, &style->base[gtk_widget_get_state (widget)]);
12636 gtk_widget_queue_draw (widget);
12640 * pspp_sheet_view_get_grid_lines:
12641 * @tree_view: a #PsppSheetView
12643 * Returns which grid lines are enabled in @tree_view.
12645 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12650 PsppSheetViewGridLines
12651 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12653 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12655 return tree_view->priv->grid_lines;
12659 * pspp_sheet_view_set_grid_lines:
12660 * @tree_view: a #PsppSheetView
12661 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12664 * Sets which grid lines to draw in @tree_view.
12669 pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view,
12670 PsppSheetViewGridLines grid_lines)
12672 PsppSheetViewPrivate *priv;
12673 PsppSheetViewGridLines old_grid_lines;
12675 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12677 priv = tree_view->priv;
12679 old_grid_lines = priv->grid_lines;
12680 priv->grid_lines = grid_lines;
12682 if (old_grid_lines != grid_lines)
12684 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12686 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12691 * pspp_sheet_view_get_special_cells:
12692 * @tree_view: a #PsppSheetView
12694 * Returns which grid lines are enabled in @tree_view.
12696 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12697 * the sheet view contain special cells.
12699 PsppSheetViewSpecialCells
12700 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12702 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12704 return tree_view->priv->special_cells;
12708 * pspp_sheet_view_set_special_cells:
12709 * @tree_view: a #PsppSheetView
12710 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12711 * the sheet view contain special cells.
12713 * Sets whether rows in the sheet view contain special cells, controlling the
12714 * rendering of row selections.
12717 pspp_sheet_view_set_special_cells (PsppSheetView *tree_view,
12718 PsppSheetViewSpecialCells special_cells)
12720 PsppSheetViewPrivate *priv;
12722 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12724 priv = tree_view->priv;
12726 if (priv->special_cells != special_cells)
12728 priv->special_cells = special_cells;
12729 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12730 g_object_notify (G_OBJECT (tree_view), "special-cells");
12735 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12737 /* XXX (re)calculate fixed_height if necessary */
12738 return tree_view->priv->fixed_height;
12742 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
12745 g_return_if_fail (fixed_height > 0);
12747 if (tree_view->priv->fixed_height != fixed_height)
12749 tree_view->priv->fixed_height = fixed_height;
12750 g_object_notify (G_OBJECT (tree_view), "fixed-height");
12752 if (!tree_view->priv->fixed_height_set)
12754 tree_view->priv->fixed_height_set = TRUE;
12755 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
12760 * pspp_sheet_view_set_tooltip_row:
12761 * @tree_view: a #PsppSheetView
12762 * @tooltip: a #GtkTooltip
12763 * @path: a #GtkTreePath
12765 * Sets the tip area of @tooltip to be the area covered by the row at @path.
12766 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12767 * See also gtk_tooltip_set_tip_area().
12772 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
12773 GtkTooltip *tooltip,
12776 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12777 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12779 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
12783 * pspp_sheet_view_set_tooltip_cell:
12784 * @tree_view: a #PsppSheetView
12785 * @tooltip: a #GtkTooltip
12786 * @path: (allow-none): a #GtkTreePath or %NULL
12787 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
12788 * @cell: (allow-none): a #GtkCellRenderer or %NULL
12790 * Sets the tip area of @tooltip to the area @path, @column and @cell have
12791 * in common. For example if @path is %NULL and @column is set, the tip
12792 * area will be set to the full area covered by @column. See also
12793 * gtk_tooltip_set_tip_area().
12795 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12800 pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view,
12801 GtkTooltip *tooltip,
12803 PsppSheetViewColumn *column,
12804 GtkCellRenderer *cell)
12808 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12809 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12810 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
12811 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
12813 /* Determine x values. */
12814 if (column && cell)
12819 pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
12820 pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
12822 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12825 rect.width = width;
12831 pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
12832 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12835 rect.width = tmp.width;
12839 GtkAllocation allocation;
12840 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
12842 rect.width = allocation.width;
12845 /* Determine y values. */
12850 pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
12851 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12854 rect.height = tmp.height;
12859 rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
12862 gtk_tooltip_set_tip_area (tooltip, &rect);
12866 * pspp_sheet_view_get_tooltip_context:
12867 * @tree_view: a #PsppSheetView
12868 * @x: the x coordinate (relative to widget coordinates)
12869 * @y: the y coordinate (relative to widget coordinates)
12870 * @keyboard_tip: whether this is a keyboard tooltip or not
12871 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
12872 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
12873 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
12875 * This function is supposed to be used in a #GtkWidget::query-tooltip
12876 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
12877 * which are received in the signal handler, should be passed to this
12878 * function without modification.
12880 * The return value indicates whether there is a tree view row at the given
12881 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
12882 * tooltips the row returned will be the cursor row. When %TRUE, then any of
12883 * @model, @path and @iter which have been provided will be set to point to
12884 * that row and the corresponding model. @x and @y will always be converted
12885 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
12887 * Return value: whether or not the given tooltip context points to a row.
12892 pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view,
12895 gboolean keyboard_tip,
12896 GtkTreeModel **model,
12897 GtkTreePath **path,
12900 GtkTreePath *tmppath = NULL;
12902 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12903 g_return_val_if_fail (x != NULL, FALSE);
12904 g_return_val_if_fail (y != NULL, FALSE);
12908 pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
12915 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
12918 if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
12919 &tmppath, NULL, NULL, NULL))
12924 *model = pspp_sheet_view_get_model (tree_view);
12927 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
12933 gtk_tree_path_free (tmppath);
12939 pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget,
12942 gboolean keyboard_tip,
12943 GtkTooltip *tooltip,
12946 GValue value = { 0, };
12947 GValue transformed = { 0, };
12950 GtkTreeModel *model;
12951 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12953 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
12956 &model, &path, &iter))
12959 gtk_tree_model_get_value (model, &iter,
12960 tree_view->priv->tooltip_column, &value);
12962 g_value_init (&transformed, G_TYPE_STRING);
12964 if (!g_value_transform (&value, &transformed))
12966 g_value_unset (&value);
12967 gtk_tree_path_free (path);
12972 g_value_unset (&value);
12974 if (!g_value_get_string (&transformed))
12976 g_value_unset (&transformed);
12977 gtk_tree_path_free (path);
12982 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
12983 pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
12985 gtk_tree_path_free (path);
12986 g_value_unset (&transformed);
12992 * pspp_sheet_view_set_tooltip_column:
12993 * @tree_view: a #PsppSheetView
12994 * @column: an integer, which is a valid column number for @tree_view's model
12996 * If you only plan to have simple (text-only) tooltips on full rows, you
12997 * can use this function to have #PsppSheetView handle these automatically
12998 * for you. @column should be set to the column in @tree_view's model
12999 * containing the tooltip texts, or -1 to disable this feature.
13001 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
13002 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
13004 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
13005 * so &, <, etc have to be escaped in the text.
13010 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
13013 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13015 if (column == tree_view->priv->tooltip_column)
13020 g_signal_handlers_disconnect_by_func (tree_view,
13021 pspp_sheet_view_set_tooltip_query_cb,
13023 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13027 if (tree_view->priv->tooltip_column == -1)
13029 g_signal_connect (tree_view, "query-tooltip",
13030 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13031 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13035 tree_view->priv->tooltip_column = column;
13036 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13040 * pspp_sheet_view_get_tooltip_column:
13041 * @tree_view: a #PsppSheetView
13043 * Returns the column of @tree_view's model which is being used for
13044 * displaying tooltips on @tree_view's rows.
13046 * Return value: the index of the tooltip column that is currently being
13047 * used, or -1 if this is disabled.
13052 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13054 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13056 return tree_view->priv->tooltip_column;
13060 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13061 GValue *return_accu,
13062 const GValue *handler_return,
13065 gboolean continue_emission;
13066 gboolean signal_handled;
13068 signal_handled = g_value_get_boolean (handler_return);
13069 g_value_set_boolean (return_accu, signal_handled);
13070 continue_emission = !signal_handled;
13072 return continue_emission;
13077 pspp_sheet_view_grid_lines_get_type (void)
13079 static GType etype = 0;
13080 if (G_UNLIKELY(etype == 0)) {
13081 static const GEnumValue values[] = {
13082 { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13083 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13084 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13085 { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13088 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13094 pspp_sheet_view_special_cells_get_type (void)
13096 static GType etype = 0;
13097 if (G_UNLIKELY(etype == 0)) {
13098 static const GEnumValue values[] = {
13099 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13100 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13101 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13104 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);