1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012 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>
45 #include "ui/gui/psppire-marshal.h"
46 #include "ui/gui/pspp-sheet-selection.h"
48 #define P_(STRING) STRING
49 #define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
50 #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
52 /* Many keyboard shortcuts for Mac are the same as for X
53 * except they use Command key instead of Control (e.g. Cut,
54 * Copy, Paste). This symbol is for those simple cases. */
55 #ifndef GDK_WINDOWING_QUARTZ
56 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_CONTROL_MASK
58 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_META_MASK
61 #define PSPP_SHEET_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
62 #define PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC (PSPP_SHEET_VIEW_PRIORITY_VALIDATE + 2)
63 #define PSPP_SHEET_VIEW_TIME_MS_PER_IDLE 30
64 #define SCROLL_EDGE_SIZE 15
65 #define EXPANDER_EXTRA_PADDING 4
66 #define PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT 5000
68 /* The "background" areas of all rows/cells add up to cover the entire tree.
69 * The background includes all inter-row and inter-cell spacing.
70 * The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
71 * i.e. just the cells, no spacing.
74 #define BACKGROUND_HEIGHT(tree_view) (tree_view->priv->fixed_height)
75 #define CELL_HEIGHT(tree_view, separator) ((BACKGROUND_HEIGHT (tree_view)) - (separator))
77 /* Translate from bin_window coordinates to rbtree (tree coordinates) and
80 #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->priv->dy)
81 #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->priv->dy)
83 /* This is in bin_window coordinates */
84 #define BACKGROUND_FIRST_PIXEL(tree_view,node) (RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, pspp_sheet_view_node_find_offset (tree_view, (node))))
85 #define CELL_FIRST_PIXEL(tree_view,node,separator) (BACKGROUND_FIRST_PIXEL (tree_view,node) + separator/2)
87 #define ROW_HEIGHT(tree_view) \
88 ((tree_view->priv->fixed_height > 0) ? (tree_view->priv->fixed_height) : (tree_view)->priv->expander_size)
91 typedef struct _PsppSheetViewChild PsppSheetViewChild;
92 struct _PsppSheetViewChild
102 typedef struct _TreeViewDragInfo TreeViewDragInfo;
103 struct _TreeViewDragInfo
105 GdkModifierType start_button_mask;
106 GtkTargetList *_unused_source_target_list;
107 GdkDragAction source_actions;
109 GtkTargetList *_unused_dest_target_list;
111 guint source_set : 1;
127 START_INTERACTIVE_SEARCH,
137 PROP_HEADERS_VISIBLE,
138 PROP_HEADERS_CLICKABLE,
143 PROP_HOVER_SELECTION,
145 PROP_ENABLE_GRID_LINES,
149 PROP_FIXED_HEIGHT_SET
153 static void pspp_sheet_view_finalize (GObject *object);
154 static void pspp_sheet_view_set_property (GObject *object,
158 static void pspp_sheet_view_get_property (GObject *object,
163 static void pspp_sheet_view_dispose (GObject *object);
165 /* gtkwidget signals */
166 static void pspp_sheet_view_realize (GtkWidget *widget);
167 static void pspp_sheet_view_unrealize (GtkWidget *widget);
168 static void pspp_sheet_view_map (GtkWidget *widget);
169 static void pspp_sheet_view_size_request (GtkWidget *widget,
170 GtkRequisition *requisition);
171 static void pspp_sheet_view_size_allocate (GtkWidget *widget,
172 GtkAllocation *allocation);
173 static gboolean pspp_sheet_view_expose (GtkWidget *widget,
174 GdkEventExpose *event);
175 static gboolean pspp_sheet_view_key_press (GtkWidget *widget,
177 static gboolean pspp_sheet_view_key_release (GtkWidget *widget,
179 static gboolean pspp_sheet_view_motion (GtkWidget *widget,
180 GdkEventMotion *event);
181 static gboolean pspp_sheet_view_enter_notify (GtkWidget *widget,
182 GdkEventCrossing *event);
183 static gboolean pspp_sheet_view_leave_notify (GtkWidget *widget,
184 GdkEventCrossing *event);
185 static gboolean pspp_sheet_view_button_press (GtkWidget *widget,
186 GdkEventButton *event);
187 static gboolean pspp_sheet_view_button_release (GtkWidget *widget,
188 GdkEventButton *event);
189 static gboolean pspp_sheet_view_grab_broken (GtkWidget *widget,
190 GdkEventGrabBroken *event);
192 static void pspp_sheet_view_set_focus_child (GtkContainer *container,
194 static gint pspp_sheet_view_focus_out (GtkWidget *widget,
195 GdkEventFocus *event);
196 static gint pspp_sheet_view_focus (GtkWidget *widget,
197 GtkDirectionType direction);
198 static void pspp_sheet_view_grab_focus (GtkWidget *widget);
199 static void pspp_sheet_view_style_set (GtkWidget *widget,
200 GtkStyle *previous_style);
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 static gboolean pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view);
261 static void pspp_sheet_view_row_changed (GtkTreeModel *model,
265 static void pspp_sheet_view_row_inserted (GtkTreeModel *model,
269 static void pspp_sheet_view_row_deleted (GtkTreeModel *model,
272 static void pspp_sheet_view_rows_reordered (GtkTreeModel *model,
278 /* Incremental reflow */
279 static gint validate_row (PsppSheetView *tree_view,
283 static void validate_visible_area (PsppSheetView *tree_view);
284 static gboolean validate_rows_handler (PsppSheetView *tree_view);
285 static gboolean presize_handler_callback (gpointer data);
286 static void install_presize_handler (PsppSheetView *tree_view);
287 static void install_scroll_sync_handler (PsppSheetView *tree_view);
288 static void pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
291 static void pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view);
292 static void pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view);
293 static void invalidate_empty_focus (PsppSheetView *tree_view);
295 /* Internal functions */
296 static void pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
299 gboolean add_shifted_binding,
300 GtkMovementStep step,
302 static void pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
304 const GdkRectangle *clip_rect);
305 static gint pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
308 static void pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
309 PsppSheetView *tree_view);
310 static void pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
312 static void pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
313 PsppSheetViewColumn *column,
314 gboolean focus_to_cell);
315 static gboolean pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
316 GdkEventMotion *event);
317 static void pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view);
318 static gboolean pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
320 static void pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
322 static void pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
324 static void pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
326 static void pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
328 static void pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
330 gboolean clear_and_select,
331 gboolean clamp_node);
332 static gboolean pspp_sheet_view_has_special_cell (PsppSheetView *tree_view);
333 static void pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view);
334 static void update_prelight (PsppSheetView *tree_view,
337 static void initialize_fixed_height_mode (PsppSheetView *tree_view);
339 /* interactive search */
340 static void pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view);
341 static void pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
342 PsppSheetView *tree_view);
343 static void pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
344 GtkWidget *search_dialog,
346 static void pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
349 static void pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
350 PsppSheetView *tree_view);
351 static void pspp_sheet_view_search_activate (GtkEntry *entry,
352 PsppSheetView *tree_view);
353 static gboolean pspp_sheet_view_real_search_enable_popdown(gpointer data);
354 static void pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
356 static gboolean pspp_sheet_view_search_delete_event (GtkWidget *widget,
358 PsppSheetView *tree_view);
359 static gboolean pspp_sheet_view_search_button_press_event (GtkWidget *widget,
360 GdkEventButton *event,
361 PsppSheetView *tree_view);
362 static gboolean pspp_sheet_view_search_scroll_event (GtkWidget *entry,
363 GdkEventScroll *event,
364 PsppSheetView *tree_view);
365 static gboolean pspp_sheet_view_search_key_press_event (GtkWidget *entry,
367 PsppSheetView *tree_view);
368 static gboolean pspp_sheet_view_search_move (GtkWidget *window,
369 PsppSheetView *tree_view,
371 static gboolean pspp_sheet_view_search_equal_func (GtkTreeModel *model,
375 gpointer search_data);
376 static gboolean pspp_sheet_view_search_iter (GtkTreeModel *model,
377 PsppSheetSelection *selection,
382 static void pspp_sheet_view_search_init (GtkWidget *entry,
383 PsppSheetView *tree_view);
384 static void pspp_sheet_view_put (PsppSheetView *tree_view,
385 GtkWidget *child_widget,
390 static gboolean pspp_sheet_view_start_editing (PsppSheetView *tree_view,
391 GtkTreePath *cursor_path);
392 static gboolean pspp_sheet_view_editable_button_press_event (GtkWidget *,
395 static void pspp_sheet_view_editable_clicked (GtkButton *, PsppSheetView *);
396 static void pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
397 PsppSheetViewColumn *column,
399 GtkCellEditable *cell_editable,
400 GdkRectangle *cell_area,
403 static gboolean pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
404 gboolean keybinding);
405 static gboolean pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view);
406 static PsppSheetViewColumn *pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
407 PsppSheetViewColumn *column,
410 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
411 PsppSheetViewColumn *column,
412 const GdkRectangle *background_area,
413 gboolean subtract_focus_rect,
414 GdkRectangle *cell_area);
415 static gint pspp_sheet_view_find_offset (PsppSheetView *tree_view,
420 static void pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
424 static void pspp_sheet_view_buildable_init (GtkBuildableIface *iface);
427 static gboolean scroll_row_timeout (gpointer data);
428 static void add_scroll_timeout (PsppSheetView *tree_view);
429 static void remove_scroll_timeout (PsppSheetView *tree_view);
431 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
433 static GtkBindingSet *edit_bindings;
440 G_DEFINE_TYPE_WITH_CODE (PsppSheetView, pspp_sheet_view, GTK_TYPE_CONTAINER,
441 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
442 pspp_sheet_view_buildable_init))
445 pspp_sheet_view_class_init (PsppSheetViewClass *class)
447 GObjectClass *o_class;
448 GtkWidgetClass *widget_class;
449 GtkContainerClass *container_class;
450 GtkBindingSet *binding_set[2];
453 binding_set[0] = gtk_binding_set_by_class (class);
455 binding_set[1] = gtk_binding_set_new ("PsppSheetViewEditing");
456 edit_bindings = binding_set[1];
458 o_class = (GObjectClass *) class;
459 widget_class = (GtkWidgetClass *) class;
460 container_class = (GtkContainerClass *) class;
462 /* GObject signals */
463 o_class->set_property = pspp_sheet_view_set_property;
464 o_class->get_property = pspp_sheet_view_get_property;
465 o_class->finalize = pspp_sheet_view_finalize;
466 o_class->dispose = pspp_sheet_view_dispose;
468 /* GtkWidget signals */
469 widget_class->map = pspp_sheet_view_map;
470 widget_class->realize = pspp_sheet_view_realize;
471 widget_class->unrealize = pspp_sheet_view_unrealize;
472 widget_class->size_request = pspp_sheet_view_size_request;
473 widget_class->size_allocate = pspp_sheet_view_size_allocate;
474 widget_class->button_press_event = pspp_sheet_view_button_press;
475 widget_class->button_release_event = pspp_sheet_view_button_release;
476 widget_class->grab_broken_event = pspp_sheet_view_grab_broken;
477 /*widget_class->configure_event = pspp_sheet_view_configure;*/
478 widget_class->motion_notify_event = pspp_sheet_view_motion;
479 widget_class->expose_event = pspp_sheet_view_expose;
480 widget_class->key_press_event = pspp_sheet_view_key_press;
481 widget_class->key_release_event = pspp_sheet_view_key_release;
482 widget_class->enter_notify_event = pspp_sheet_view_enter_notify;
483 widget_class->leave_notify_event = pspp_sheet_view_leave_notify;
484 widget_class->focus_out_event = pspp_sheet_view_focus_out;
485 widget_class->drag_begin = pspp_sheet_view_drag_begin;
486 widget_class->drag_end = pspp_sheet_view_drag_end;
487 widget_class->drag_data_get = pspp_sheet_view_drag_data_get;
488 widget_class->drag_data_delete = pspp_sheet_view_drag_data_delete;
489 widget_class->drag_leave = pspp_sheet_view_drag_leave;
490 widget_class->drag_motion = pspp_sheet_view_drag_motion;
491 widget_class->drag_drop = pspp_sheet_view_drag_drop;
492 widget_class->drag_data_received = pspp_sheet_view_drag_data_received;
493 widget_class->focus = pspp_sheet_view_focus;
494 widget_class->grab_focus = pspp_sheet_view_grab_focus;
495 widget_class->style_set = pspp_sheet_view_style_set;
496 widget_class->grab_notify = pspp_sheet_view_grab_notify;
497 widget_class->state_changed = pspp_sheet_view_state_changed;
499 /* GtkContainer signals */
500 container_class->remove = pspp_sheet_view_remove;
501 container_class->forall = pspp_sheet_view_forall;
502 container_class->set_focus_child = pspp_sheet_view_set_focus_child;
504 class->set_scroll_adjustments = pspp_sheet_view_set_adjustments;
505 class->move_cursor = pspp_sheet_view_real_move_cursor;
506 class->select_all = pspp_sheet_view_real_select_all;
507 class->unselect_all = pspp_sheet_view_real_unselect_all;
508 class->select_cursor_row = pspp_sheet_view_real_select_cursor_row;
509 class->toggle_cursor_row = pspp_sheet_view_real_toggle_cursor_row;
510 class->start_interactive_search = pspp_sheet_view_start_interactive_search;
514 g_object_class_install_property (o_class,
516 g_param_spec_object ("model",
517 P_("TreeView Model"),
518 P_("The model for the tree view"),
520 GTK_PARAM_READWRITE));
522 g_object_class_install_property (o_class,
524 g_param_spec_object ("hadjustment",
525 P_("Horizontal Adjustment"),
526 P_("Horizontal Adjustment for the widget"),
528 GTK_PARAM_READWRITE));
530 g_object_class_install_property (o_class,
532 g_param_spec_object ("vadjustment",
533 P_("Vertical Adjustment"),
534 P_("Vertical Adjustment for the widget"),
536 GTK_PARAM_READWRITE));
538 g_object_class_install_property (o_class,
539 PROP_HEADERS_VISIBLE,
540 g_param_spec_boolean ("headers-visible",
541 P_("Headers Visible"),
542 P_("Show the column header buttons"),
544 GTK_PARAM_READWRITE));
546 g_object_class_install_property (o_class,
547 PROP_HEADERS_CLICKABLE,
548 g_param_spec_boolean ("headers-clickable",
549 P_("Headers Clickable"),
550 P_("Column headers respond to click events"),
552 GTK_PARAM_READWRITE));
554 g_object_class_install_property (o_class,
556 g_param_spec_boolean ("reorderable",
558 P_("View is reorderable"),
560 GTK_PARAM_READWRITE));
562 g_object_class_install_property (o_class,
564 g_param_spec_boolean ("rules-hint",
566 P_("Set a hint to the theme engine to draw rows in alternating colors"),
568 GTK_PARAM_READWRITE));
570 g_object_class_install_property (o_class,
572 g_param_spec_boolean ("enable-search",
574 P_("View allows user to search through columns interactively"),
576 GTK_PARAM_READWRITE));
578 g_object_class_install_property (o_class,
580 g_param_spec_int ("search-column",
582 P_("Model column to search through during interactive search"),
586 GTK_PARAM_READWRITE));
589 * PsppSheetView:hover-selection:
591 * Enables of disables the hover selection mode of @tree_view.
592 * Hover selection makes the selected row follow the pointer.
593 * Currently, this works only for the selection modes
594 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
596 * This mode is primarily intended for treeviews in popups, e.g.
597 * in #GtkComboBox or #GtkEntryCompletion.
601 g_object_class_install_property (o_class,
602 PROP_HOVER_SELECTION,
603 g_param_spec_boolean ("hover-selection",
604 P_("Hover Selection"),
605 P_("Whether the selection should follow the pointer"),
607 GTK_PARAM_READWRITE));
609 g_object_class_install_property (o_class,
611 g_param_spec_boolean ("rubber-banding",
612 P_("Rubber Banding"),
613 P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
615 GTK_PARAM_READWRITE));
617 g_object_class_install_property (o_class,
618 PROP_ENABLE_GRID_LINES,
619 g_param_spec_enum ("enable-grid-lines",
620 P_("Enable Grid Lines"),
621 P_("Whether grid lines should be drawn in the tree view"),
622 PSPP_TYPE_SHEET_VIEW_GRID_LINES,
623 PSPP_SHEET_VIEW_GRID_LINES_NONE,
624 GTK_PARAM_READWRITE));
626 g_object_class_install_property (o_class,
628 g_param_spec_int ("tooltip-column",
629 P_("Tooltip Column"),
630 P_("The column in the model containing the tooltip texts for the rows"),
634 GTK_PARAM_READWRITE));
636 g_object_class_install_property (o_class,
638 g_param_spec_enum ("special-cells",
640 P_("Whether rows have special cells."),
641 PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS,
642 PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT,
643 GTK_PARAM_READWRITE));
645 g_object_class_install_property (o_class,
647 g_param_spec_int ("fixed-height",
649 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."),
653 GTK_PARAM_READWRITE));
655 g_object_class_install_property (o_class,
656 PROP_FIXED_HEIGHT_SET,
657 g_param_spec_boolean ("fixed-height-set",
658 P_("Fixed Height Set"),
659 P_("Whether fixed-height was set externally."),
661 GTK_PARAM_READWRITE));
663 /* Style properties */
664 #define _TREE_VIEW_EXPANDER_SIZE 12
665 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
666 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
668 gtk_widget_class_install_style_property (widget_class,
669 g_param_spec_int ("expander-size",
671 P_("Size of the expander arrow"),
674 _TREE_VIEW_EXPANDER_SIZE,
675 GTK_PARAM_READABLE));
677 gtk_widget_class_install_style_property (widget_class,
678 g_param_spec_int ("vertical-separator",
679 P_("Vertical Separator Width"),
680 P_("Vertical space between cells. Must be an even number"),
683 _TREE_VIEW_VERTICAL_SEPARATOR,
684 GTK_PARAM_READABLE));
686 gtk_widget_class_install_style_property (widget_class,
687 g_param_spec_int ("horizontal-separator",
688 P_("Horizontal Separator Width"),
689 P_("Horizontal space between cells. Must be an even number"),
692 _TREE_VIEW_HORIZONTAL_SEPARATOR,
693 GTK_PARAM_READABLE));
695 gtk_widget_class_install_style_property (widget_class,
696 g_param_spec_boolean ("allow-rules",
698 P_("Allow drawing of alternating color rows"),
700 GTK_PARAM_READABLE));
702 gtk_widget_class_install_style_property (widget_class,
703 g_param_spec_boxed ("even-row-color",
704 P_("Even Row Color"),
705 P_("Color to use for even rows"),
707 GTK_PARAM_READABLE));
709 gtk_widget_class_install_style_property (widget_class,
710 g_param_spec_boxed ("odd-row-color",
712 P_("Color to use for odd rows"),
714 GTK_PARAM_READABLE));
716 gtk_widget_class_install_style_property (widget_class,
717 g_param_spec_boolean ("row-ending-details",
718 P_("Row Ending details"),
719 P_("Enable extended row background theming"),
721 GTK_PARAM_READABLE));
723 gtk_widget_class_install_style_property (widget_class,
724 g_param_spec_int ("grid-line-width",
725 P_("Grid line width"),
726 P_("Width, in pixels, of the tree view grid lines"),
728 GTK_PARAM_READABLE));
730 gtk_widget_class_install_style_property (widget_class,
731 g_param_spec_int ("tree-line-width",
732 P_("Tree line width"),
733 P_("Width, in pixels, of the tree view lines"),
735 GTK_PARAM_READABLE));
737 gtk_widget_class_install_style_property (widget_class,
738 g_param_spec_string ("tree-line-pattern",
739 P_("Tree line pattern"),
740 P_("Dash pattern used to draw the tree view lines"),
742 GTK_PARAM_READABLE));
746 * PsppSheetView::set-scroll-adjustments
747 * @horizontal: the horizontal #GtkAdjustment
748 * @vertical: the vertical #GtkAdjustment
750 * Set the scroll adjustments for the tree view. Usually scrolled containers
751 * like #GtkScrolledWindow will emit this signal to connect two instances
752 * of #GtkScrollbar to the scroll directions of the #PsppSheetView.
754 widget_class->set_scroll_adjustments_signal =
755 g_signal_new ("set-scroll-adjustments",
756 G_TYPE_FROM_CLASS (o_class),
757 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
758 G_STRUCT_OFFSET (PsppSheetViewClass, set_scroll_adjustments),
760 psppire_marshal_VOID__OBJECT_OBJECT,
763 GTK_TYPE_ADJUSTMENT);
766 * PsppSheetView::row-activated:
767 * @tree_view: the object on which the signal is emitted
768 * @path: the #GtkTreePath for the activated row
769 * @column: the #PsppSheetViewColumn in which the activation occurred
771 * The "row-activated" signal is emitted when the method
772 * pspp_sheet_view_row_activated() is called or the user double clicks
773 * a treeview row. It is also emitted when a non-editable row is
774 * selected and one of the keys: Space, Shift+Space, Return or
777 * For selection handling refer to the <link linkend="TreeWidget">tree
778 * widget conceptual overview</link> as well as #PsppSheetSelection.
780 tree_view_signals[ROW_ACTIVATED] =
781 g_signal_new ("row-activated",
782 G_TYPE_FROM_CLASS (o_class),
783 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
784 G_STRUCT_OFFSET (PsppSheetViewClass, row_activated),
786 psppire_marshal_VOID__BOXED_OBJECT,
789 PSPP_TYPE_SHEET_VIEW_COLUMN);
792 * PsppSheetView::columns-changed:
793 * @tree_view: the object on which the signal is emitted
795 * The number of columns of the treeview has changed.
797 tree_view_signals[COLUMNS_CHANGED] =
798 g_signal_new ("columns-changed",
799 G_TYPE_FROM_CLASS (o_class),
801 G_STRUCT_OFFSET (PsppSheetViewClass, columns_changed),
803 g_cclosure_marshal_VOID__VOID,
807 * PsppSheetView::cursor-changed:
808 * @tree_view: the object on which the signal is emitted
810 * The position of the cursor (focused cell) has changed.
812 tree_view_signals[CURSOR_CHANGED] =
813 g_signal_new ("cursor-changed",
814 G_TYPE_FROM_CLASS (o_class),
816 G_STRUCT_OFFSET (PsppSheetViewClass, cursor_changed),
818 g_cclosure_marshal_VOID__VOID,
821 tree_view_signals[MOVE_CURSOR] =
822 g_signal_new ("move-cursor",
823 G_TYPE_FROM_CLASS (o_class),
824 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
825 G_STRUCT_OFFSET (PsppSheetViewClass, move_cursor),
827 psppire_marshal_BOOLEAN__ENUM_INT,
829 GTK_TYPE_MOVEMENT_STEP,
832 tree_view_signals[SELECT_ALL] =
833 g_signal_new ("select-all",
834 G_TYPE_FROM_CLASS (o_class),
835 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
836 G_STRUCT_OFFSET (PsppSheetViewClass, select_all),
838 psppire_marshal_BOOLEAN__VOID,
841 tree_view_signals[UNSELECT_ALL] =
842 g_signal_new ("unselect-all",
843 G_TYPE_FROM_CLASS (o_class),
844 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
845 G_STRUCT_OFFSET (PsppSheetViewClass, unselect_all),
847 psppire_marshal_BOOLEAN__VOID,
850 tree_view_signals[SELECT_CURSOR_ROW] =
851 g_signal_new ("select-cursor-row",
852 G_TYPE_FROM_CLASS (o_class),
853 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
854 G_STRUCT_OFFSET (PsppSheetViewClass, select_cursor_row),
856 psppire_marshal_BOOLEAN__BOOLEAN,
860 tree_view_signals[TOGGLE_CURSOR_ROW] =
861 g_signal_new ("toggle-cursor-row",
862 G_TYPE_FROM_CLASS (o_class),
863 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
864 G_STRUCT_OFFSET (PsppSheetViewClass, toggle_cursor_row),
866 psppire_marshal_BOOLEAN__VOID,
869 tree_view_signals[START_INTERACTIVE_SEARCH] =
870 g_signal_new ("start-interactive-search",
871 G_TYPE_FROM_CLASS (o_class),
872 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
873 G_STRUCT_OFFSET (PsppSheetViewClass, start_interactive_search),
875 psppire_marshal_BOOLEAN__VOID,
879 for (i = 0; i < 2; i++)
881 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Up, 0, TRUE,
882 GTK_MOVEMENT_DISPLAY_LINES, -1);
883 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Up, 0, TRUE,
884 GTK_MOVEMENT_DISPLAY_LINES, -1);
886 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Down, 0, TRUE,
887 GTK_MOVEMENT_DISPLAY_LINES, 1);
888 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Down, 0, TRUE,
889 GTK_MOVEMENT_DISPLAY_LINES, 1);
891 pspp_sheet_view_add_move_binding (binding_set[i], GDK_p, GDK_CONTROL_MASK, FALSE,
892 GTK_MOVEMENT_DISPLAY_LINES, -1);
894 pspp_sheet_view_add_move_binding (binding_set[i], GDK_n, GDK_CONTROL_MASK, FALSE,
895 GTK_MOVEMENT_DISPLAY_LINES, 1);
897 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Home, 0, TRUE,
898 GTK_MOVEMENT_BUFFER_ENDS, -1);
899 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Home, 0, TRUE,
900 GTK_MOVEMENT_BUFFER_ENDS, -1);
902 pspp_sheet_view_add_move_binding (binding_set[i], GDK_End, 0, TRUE,
903 GTK_MOVEMENT_BUFFER_ENDS, 1);
904 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_End, 0, TRUE,
905 GTK_MOVEMENT_BUFFER_ENDS, 1);
907 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Up, 0, TRUE,
908 GTK_MOVEMENT_PAGES, -1);
909 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Up, 0, TRUE,
910 GTK_MOVEMENT_PAGES, -1);
912 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Down, 0, TRUE,
913 GTK_MOVEMENT_PAGES, 1);
914 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Down, 0, TRUE,
915 GTK_MOVEMENT_PAGES, 1);
918 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, 0, "move-cursor", 2,
919 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
922 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, 0, "move-cursor", 2,
923 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
926 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, 0, "move-cursor", 2,
927 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
930 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, GDK_SHIFT_MASK, "move-cursor", 2,
931 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
934 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, 0, "move-cursor", 2,
935 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
938 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, 0, "move-cursor", 2,
939 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
942 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, GDK_CONTROL_MASK,
944 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
947 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, GDK_CONTROL_MASK,
949 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
952 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, GDK_CONTROL_MASK,
954 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
957 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, GDK_CONTROL_MASK,
959 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
962 gtk_binding_entry_add_signal (binding_set[i], GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
964 gtk_binding_entry_add_signal (binding_set[i], GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
967 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
968 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
970 gtk_binding_entry_add_signal (binding_set[0], GDK_a, GDK_CONTROL_MASK, "select-all", 0);
971 gtk_binding_entry_add_signal (binding_set[0], GDK_slash, GDK_CONTROL_MASK, "select-all", 0);
973 gtk_binding_entry_add_signal (binding_set[0], GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
974 gtk_binding_entry_add_signal (binding_set[0], GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0);
976 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1,
977 G_TYPE_BOOLEAN, TRUE);
978 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1,
979 G_TYPE_BOOLEAN, TRUE);
981 gtk_binding_entry_add_signal (binding_set[0], GDK_space, 0, "select-cursor-row", 1,
982 G_TYPE_BOOLEAN, TRUE);
983 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, 0, "select-cursor-row", 1,
984 G_TYPE_BOOLEAN, TRUE);
985 gtk_binding_entry_add_signal (binding_set[0], GDK_Return, 0, "select-cursor-row", 1,
986 G_TYPE_BOOLEAN, TRUE);
987 gtk_binding_entry_add_signal (binding_set[0], GDK_ISO_Enter, 0, "select-cursor-row", 1,
988 G_TYPE_BOOLEAN, TRUE);
989 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Enter, 0, "select-cursor-row", 1,
990 G_TYPE_BOOLEAN, TRUE);
992 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, 0, "select-cursor-parent", 0);
993 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0);
995 g_type_class_add_private (o_class, sizeof (PsppSheetViewPrivate));
999 pspp_sheet_view_buildable_init (GtkBuildableIface *iface)
1001 iface->add_child = pspp_sheet_view_buildable_add_child;
1005 pspp_sheet_view_init (PsppSheetView *tree_view)
1007 tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, PSPP_TYPE_SHEET_VIEW, PsppSheetViewPrivate);
1009 gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
1010 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE);
1012 tree_view->priv->flags = PSPP_SHEET_VIEW_DRAW_KEYFOCUS
1013 | PSPP_SHEET_VIEW_HEADERS_VISIBLE;
1015 /* We need some padding */
1016 tree_view->priv->selected = range_tower_create ();
1017 tree_view->priv->dy = 0;
1018 tree_view->priv->cursor_offset = 0;
1019 tree_view->priv->n_columns = 0;
1020 tree_view->priv->header_height = 1;
1021 tree_view->priv->x_drag = 0;
1022 tree_view->priv->drag_pos = -1;
1023 tree_view->priv->header_has_focus = FALSE;
1024 tree_view->priv->pressed_button = -1;
1025 tree_view->priv->press_start_x = -1;
1026 tree_view->priv->press_start_y = -1;
1027 tree_view->priv->reorderable = FALSE;
1028 tree_view->priv->presize_handler_timer = 0;
1029 tree_view->priv->scroll_sync_timer = 0;
1030 tree_view->priv->fixed_height = -1;
1031 tree_view->priv->fixed_height_set = FALSE;
1032 pspp_sheet_view_set_adjustments (tree_view, NULL, NULL);
1033 tree_view->priv->selection = _pspp_sheet_selection_new_with_tree_view (tree_view);
1034 tree_view->priv->enable_search = TRUE;
1035 tree_view->priv->search_column = -1;
1036 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
1037 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
1038 tree_view->priv->search_custom_entry_set = FALSE;
1039 tree_view->priv->typeselect_flush_timeout = 0;
1040 tree_view->priv->init_hadjust_value = TRUE;
1041 tree_view->priv->width = 0;
1043 tree_view->priv->hover_selection = FALSE;
1045 tree_view->priv->rubber_banding_enable = FALSE;
1047 tree_view->priv->grid_lines = PSPP_SHEET_VIEW_GRID_LINES_NONE;
1049 tree_view->priv->tooltip_column = -1;
1051 tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT;
1053 tree_view->priv->post_validation_flag = FALSE;
1055 tree_view->priv->last_button_x = -1;
1056 tree_view->priv->last_button_y = -1;
1058 tree_view->priv->event_last_x = -10000;
1059 tree_view->priv->event_last_y = -10000;
1061 tree_view->priv->prelight_node = -1;
1062 tree_view->priv->rubber_band_start_node = -1;
1063 tree_view->priv->rubber_band_end_node = -1;
1065 tree_view->priv->anchor_column = NULL;
1067 tree_view->priv->button_style = NULL;
1069 tree_view->dispose_has_run = FALSE;
1078 pspp_sheet_view_set_property (GObject *object,
1080 const GValue *value,
1083 PsppSheetView *tree_view;
1085 tree_view = PSPP_SHEET_VIEW (object);
1090 pspp_sheet_view_set_model (tree_view, g_value_get_object (value));
1092 case PROP_HADJUSTMENT:
1093 pspp_sheet_view_set_hadjustment (tree_view, g_value_get_object (value));
1095 case PROP_VADJUSTMENT:
1096 pspp_sheet_view_set_vadjustment (tree_view, g_value_get_object (value));
1098 case PROP_HEADERS_VISIBLE:
1099 pspp_sheet_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1101 case PROP_HEADERS_CLICKABLE:
1102 pspp_sheet_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1104 case PROP_REORDERABLE:
1105 pspp_sheet_view_set_reorderable (tree_view, g_value_get_boolean (value));
1107 case PROP_RULES_HINT:
1108 pspp_sheet_view_set_rules_hint (tree_view, g_value_get_boolean (value));
1110 case PROP_ENABLE_SEARCH:
1111 pspp_sheet_view_set_enable_search (tree_view, g_value_get_boolean (value));
1113 case PROP_SEARCH_COLUMN:
1114 pspp_sheet_view_set_search_column (tree_view, g_value_get_int (value));
1116 case PROP_HOVER_SELECTION:
1117 tree_view->priv->hover_selection = g_value_get_boolean (value);
1119 case PROP_RUBBER_BANDING:
1120 tree_view->priv->rubber_banding_enable = g_value_get_boolean (value);
1122 case PROP_ENABLE_GRID_LINES:
1123 pspp_sheet_view_set_grid_lines (tree_view, g_value_get_enum (value));
1125 case PROP_TOOLTIP_COLUMN:
1126 pspp_sheet_view_set_tooltip_column (tree_view, g_value_get_int (value));
1128 case PROP_SPECIAL_CELLS:
1129 pspp_sheet_view_set_special_cells (tree_view, g_value_get_enum (value));
1131 case PROP_FIXED_HEIGHT:
1132 pspp_sheet_view_set_fixed_height (tree_view, g_value_get_int (value));
1134 case PROP_FIXED_HEIGHT_SET:
1135 if (g_value_get_boolean (value))
1137 if (!tree_view->priv->fixed_height_set
1138 && tree_view->priv->fixed_height >= 0)
1140 tree_view->priv->fixed_height_set = true;
1141 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1146 if (tree_view->priv->fixed_height_set)
1148 tree_view->priv->fixed_height_set = false;
1149 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1150 install_presize_handler (tree_view);
1155 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1161 pspp_sheet_view_get_property (GObject *object,
1166 PsppSheetView *tree_view;
1168 tree_view = PSPP_SHEET_VIEW (object);
1173 g_value_set_object (value, tree_view->priv->model);
1175 case PROP_HADJUSTMENT:
1176 g_value_set_object (value, tree_view->priv->hadjustment);
1178 case PROP_VADJUSTMENT:
1179 g_value_set_object (value, tree_view->priv->vadjustment);
1181 case PROP_HEADERS_VISIBLE:
1182 g_value_set_boolean (value, pspp_sheet_view_get_headers_visible (tree_view));
1184 case PROP_HEADERS_CLICKABLE:
1185 g_value_set_boolean (value, pspp_sheet_view_get_headers_clickable (tree_view));
1187 case PROP_REORDERABLE:
1188 g_value_set_boolean (value, tree_view->priv->reorderable);
1190 case PROP_RULES_HINT:
1191 g_value_set_boolean (value, tree_view->priv->has_rules);
1193 case PROP_ENABLE_SEARCH:
1194 g_value_set_boolean (value, tree_view->priv->enable_search);
1196 case PROP_SEARCH_COLUMN:
1197 g_value_set_int (value, tree_view->priv->search_column);
1199 case PROP_HOVER_SELECTION:
1200 g_value_set_boolean (value, tree_view->priv->hover_selection);
1202 case PROP_RUBBER_BANDING:
1203 g_value_set_boolean (value, tree_view->priv->rubber_banding_enable);
1205 case PROP_ENABLE_GRID_LINES:
1206 g_value_set_enum (value, tree_view->priv->grid_lines);
1208 case PROP_TOOLTIP_COLUMN:
1209 g_value_set_int (value, tree_view->priv->tooltip_column);
1211 case PROP_SPECIAL_CELLS:
1212 g_value_set_enum (value, tree_view->priv->special_cells);
1214 case PROP_FIXED_HEIGHT:
1215 g_value_set_int (value, pspp_sheet_view_get_fixed_height (tree_view));
1217 case PROP_FIXED_HEIGHT_SET:
1218 g_value_set_boolean (value, tree_view->priv->fixed_height_set);
1221 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1227 pspp_sheet_view_dispose (GObject *object)
1229 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1231 if (tree_view->dispose_has_run)
1234 tree_view->dispose_has_run = TRUE;
1236 if (tree_view->priv->selection != NULL)
1238 _pspp_sheet_selection_set_tree_view (tree_view->priv->selection, NULL);
1239 g_object_unref (tree_view->priv->selection);
1240 tree_view->priv->selection = NULL;
1243 if (tree_view->priv->hadjustment)
1245 g_object_unref (tree_view->priv->hadjustment);
1246 tree_view->priv->hadjustment = NULL;
1248 if (tree_view->priv->vadjustment)
1250 g_object_unref (tree_view->priv->vadjustment);
1251 tree_view->priv->vadjustment = NULL;
1254 if (tree_view->priv->button_style)
1256 g_object_unref (tree_view->priv->button_style);
1257 tree_view->priv->button_style = NULL;
1261 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->dispose (object);
1267 pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
1268 GtkBuilder *builder,
1272 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), PSPP_SHEET_VIEW_COLUMN (child));
1276 pspp_sheet_view_finalize (GObject *object)
1278 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1280 pspp_sheet_view_stop_editing (tree_view, TRUE);
1282 if (tree_view->priv->selected != NULL)
1284 range_tower_destroy (tree_view->priv->selected);
1285 tree_view->priv->selected = NULL;
1289 tree_view->priv->prelight_node = -1;
1292 if (tree_view->priv->scroll_to_path != NULL)
1294 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
1295 tree_view->priv->scroll_to_path = NULL;
1298 if (tree_view->priv->drag_dest_row != NULL)
1300 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
1301 tree_view->priv->drag_dest_row = NULL;
1304 if (tree_view->priv->top_row != NULL)
1306 gtk_tree_row_reference_free (tree_view->priv->top_row);
1307 tree_view->priv->top_row = NULL;
1310 if (tree_view->priv->column_drop_func_data &&
1311 tree_view->priv->column_drop_func_data_destroy)
1313 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
1314 tree_view->priv->column_drop_func_data = NULL;
1317 if (tree_view->priv->destroy_count_destroy &&
1318 tree_view->priv->destroy_count_data)
1320 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
1321 tree_view->priv->destroy_count_data = NULL;
1324 gtk_tree_row_reference_free (tree_view->priv->cursor);
1325 tree_view->priv->cursor = NULL;
1327 gtk_tree_row_reference_free (tree_view->priv->anchor);
1328 tree_view->priv->anchor = NULL;
1330 /* destroy interactive search dialog */
1331 if (tree_view->priv->search_window)
1333 gtk_widget_destroy (tree_view->priv->search_window);
1334 tree_view->priv->search_window = NULL;
1335 tree_view->priv->search_entry = NULL;
1336 if (tree_view->priv->typeselect_flush_timeout)
1338 g_source_remove (tree_view->priv->typeselect_flush_timeout);
1339 tree_view->priv->typeselect_flush_timeout = 0;
1343 if (tree_view->priv->search_destroy && tree_view->priv->search_user_data)
1345 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
1346 tree_view->priv->search_user_data = NULL;
1349 if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data)
1351 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
1352 tree_view->priv->search_position_user_data = NULL;
1355 pspp_sheet_view_set_model (tree_view, NULL);
1358 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->finalize (object);
1363 /* GtkWidget Methods
1366 /* GtkWidget::map helper */
1368 pspp_sheet_view_map_buttons (PsppSheetView *tree_view)
1372 g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
1374 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
1376 PsppSheetViewColumn *column;
1378 for (list = tree_view->priv->columns; list; list = list->next)
1380 column = list->data;
1381 if (column->button != NULL &&
1382 gtk_widget_get_visible (column->button) &&
1383 !gtk_widget_get_mapped (column->button))
1384 gtk_widget_map (column->button);
1386 for (list = tree_view->priv->columns; list; list = list->next)
1388 column = list->data;
1389 if (column->visible == FALSE || column->window == NULL)
1391 if (column->resizable)
1393 gdk_window_raise (column->window);
1394 gdk_window_show (column->window);
1397 gdk_window_hide (column->window);
1399 gdk_window_show (tree_view->priv->header_window);
1404 pspp_sheet_view_map (GtkWidget *widget)
1406 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1409 gtk_widget_set_mapped (widget, TRUE);
1411 tmp_list = tree_view->priv->children;
1414 PsppSheetViewChild *child = tmp_list->data;
1415 tmp_list = tmp_list->next;
1417 if (gtk_widget_get_visible (child->widget))
1419 if (!gtk_widget_get_mapped (child->widget))
1420 gtk_widget_map (child->widget);
1423 gdk_window_show (tree_view->priv->bin_window);
1425 pspp_sheet_view_map_buttons (tree_view);
1427 gdk_window_show (widget->window);
1431 pspp_sheet_view_realize (GtkWidget *widget)
1434 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1436 GdkWindowAttr attributes;
1437 gint attributes_mask;
1439 gtk_widget_set_realized (widget, TRUE);
1441 /* Make the main, clipping window */
1442 attributes.window_type = GDK_WINDOW_CHILD;
1443 attributes.x = widget->allocation.x;
1444 attributes.y = widget->allocation.y;
1445 attributes.width = widget->allocation.width;
1446 attributes.height = widget->allocation.height;
1447 attributes.wclass = GDK_INPUT_OUTPUT;
1448 attributes.visual = gtk_widget_get_visual (widget);
1449 attributes.colormap = gtk_widget_get_colormap (widget);
1450 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1452 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1454 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1455 &attributes, attributes_mask);
1456 gdk_window_set_user_data (widget->window, widget);
1458 /* Make the window for the tree */
1460 attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
1461 attributes.width = MAX (tree_view->priv->width, widget->allocation.width);
1462 attributes.height = widget->allocation.height;
1463 attributes.event_mask = (GDK_EXPOSURE_MASK |
1465 GDK_POINTER_MOTION_MASK |
1466 GDK_ENTER_NOTIFY_MASK |
1467 GDK_LEAVE_NOTIFY_MASK |
1468 GDK_BUTTON_PRESS_MASK |
1469 GDK_BUTTON_RELEASE_MASK |
1470 gtk_widget_get_events (widget));
1472 tree_view->priv->bin_window = gdk_window_new (widget->window,
1473 &attributes, attributes_mask);
1474 gdk_window_set_user_data (tree_view->priv->bin_window, widget);
1476 /* Make the column header window */
1479 attributes.width = MAX (tree_view->priv->width, widget->allocation.width);
1480 attributes.height = tree_view->priv->header_height;
1481 attributes.event_mask = (GDK_EXPOSURE_MASK |
1483 GDK_BUTTON_PRESS_MASK |
1484 GDK_BUTTON_RELEASE_MASK |
1485 GDK_KEY_PRESS_MASK |
1486 GDK_KEY_RELEASE_MASK |
1487 gtk_widget_get_events (widget));
1489 tree_view->priv->header_window = gdk_window_new (widget->window,
1490 &attributes, attributes_mask);
1491 gdk_window_set_user_data (tree_view->priv->header_window, widget);
1493 /* Add them all up. */
1494 widget->style = gtk_style_attach (widget->style, widget->window);
1495 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
1496 gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
1497 gtk_style_set_background (widget->style, tree_view->priv->header_window, GTK_STATE_NORMAL);
1499 tmp_list = tree_view->priv->children;
1502 PsppSheetViewChild *child = tmp_list->data;
1503 tmp_list = tmp_list->next;
1505 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
1508 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
1509 _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list->data));
1511 /* Need to call those here, since they create GCs */
1512 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
1514 install_presize_handler (tree_view);
1516 for (i = 0; i < 5; ++i)
1518 tree_view->priv->grid_line_gc[i] = gdk_gc_new (widget->window);
1519 gdk_gc_copy (tree_view->priv->grid_line_gc[i], widget->style->text_aa_gc[i]);
1524 pspp_sheet_view_unrealize (GtkWidget *widget)
1527 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1528 PsppSheetViewPrivate *priv = tree_view->priv;
1531 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget);
1533 if (priv->scroll_timeout != 0)
1535 g_source_remove (priv->scroll_timeout);
1536 priv->scroll_timeout = 0;
1539 if (priv->open_dest_timeout != 0)
1541 g_source_remove (priv->open_dest_timeout);
1542 priv->open_dest_timeout = 0;
1545 if (priv->presize_handler_timer != 0)
1547 g_source_remove (priv->presize_handler_timer);
1548 priv->presize_handler_timer = 0;
1551 if (priv->validate_rows_timer != 0)
1553 g_source_remove (priv->validate_rows_timer);
1554 priv->validate_rows_timer = 0;
1557 if (priv->scroll_sync_timer != 0)
1559 g_source_remove (priv->scroll_sync_timer);
1560 priv->scroll_sync_timer = 0;
1563 if (priv->typeselect_flush_timeout)
1565 g_source_remove (priv->typeselect_flush_timeout);
1566 priv->typeselect_flush_timeout = 0;
1569 for (list = priv->columns; list; list = list->next)
1570 _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list->data));
1572 gdk_window_set_user_data (priv->bin_window, NULL);
1573 gdk_window_destroy (priv->bin_window);
1574 priv->bin_window = NULL;
1576 gdk_window_set_user_data (priv->header_window, NULL);
1577 gdk_window_destroy (priv->header_window);
1578 priv->header_window = NULL;
1580 if (priv->drag_window)
1582 gdk_window_set_user_data (priv->drag_window, NULL);
1583 gdk_window_destroy (priv->drag_window);
1584 priv->drag_window = NULL;
1587 if (priv->drag_highlight_window)
1589 gdk_window_set_user_data (priv->drag_highlight_window, NULL);
1590 gdk_window_destroy (priv->drag_highlight_window);
1591 priv->drag_highlight_window = NULL;
1594 for (x = 0 ; x < 5 ; ++x)
1595 g_object_unref (priv->grid_line_gc[x]);
1597 if (tree_view->priv->columns != NULL)
1599 list = tree_view->priv->columns;
1602 PsppSheetViewColumn *column;
1603 column = PSPP_SHEET_VIEW_COLUMN (list->data);
1605 pspp_sheet_view_remove_column (tree_view, column);
1607 tree_view->priv->columns = NULL;
1611 /* GtkWidget::size_request helper */
1613 pspp_sheet_view_size_request_columns (PsppSheetView *tree_view)
1617 tree_view->priv->header_height = 0;
1619 if (tree_view->priv->model)
1621 for (list = tree_view->priv->columns; list; list = list->next)
1623 GtkRequisition requisition;
1624 PsppSheetViewColumn *column = list->data;
1626 pspp_sheet_view_column_size_request (column, &requisition);
1627 column->button_request = requisition.width;
1628 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
1634 /* Called only by ::size_request */
1636 pspp_sheet_view_update_size (PsppSheetView *tree_view)
1639 PsppSheetViewColumn *column;
1642 if (tree_view->priv->model == NULL)
1644 tree_view->priv->width = 0;
1645 tree_view->priv->prev_width = 0;
1646 tree_view->priv->height = 0;
1650 tree_view->priv->prev_width = tree_view->priv->width;
1651 tree_view->priv->width = 0;
1653 /* keep this in sync with size_allocate below */
1654 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
1656 gint real_requested_width = 0;
1657 column = list->data;
1658 if (!column->visible)
1661 if (column->use_resized_width)
1663 real_requested_width = column->resized_width;
1667 real_requested_width = column->fixed_width;
1670 if (column->min_width != -1)
1671 real_requested_width = MAX (real_requested_width, column->min_width);
1672 if (column->max_width != -1)
1673 real_requested_width = MIN (real_requested_width, column->max_width);
1675 tree_view->priv->width += real_requested_width;
1678 tree_view->priv->height = tree_view->priv->fixed_height * tree_view->priv->row_count;
1682 pspp_sheet_view_size_request (GtkWidget *widget,
1683 GtkRequisition *requisition)
1685 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1688 /* we validate some rows initially just to make sure we have some size.
1689 * In practice, with a lot of static lists, this should get a good width.
1691 initialize_fixed_height_mode (tree_view);
1692 pspp_sheet_view_size_request_columns (tree_view);
1693 pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget));
1695 requisition->width = tree_view->priv->width;
1696 requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
1698 tmp_list = tree_view->priv->children;
1702 PsppSheetViewChild *child = tmp_list->data;
1703 GtkRequisition child_requisition;
1705 tmp_list = tmp_list->next;
1707 if (gtk_widget_get_visible (child->widget))
1708 gtk_widget_size_request (child->widget, &child_requisition);
1713 invalidate_column (PsppSheetView *tree_view,
1714 PsppSheetViewColumn *column)
1716 gint column_offset = 0;
1718 GtkWidget *widget = GTK_WIDGET (tree_view);
1721 if (!gtk_widget_get_realized (widget))
1724 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1725 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
1727 list = (rtl ? list->prev : list->next))
1729 PsppSheetViewColumn *tmpcolumn = list->data;
1730 if (tmpcolumn == column)
1732 GdkRectangle invalid_rect;
1734 invalid_rect.x = column_offset;
1736 invalid_rect.width = column->width;
1737 invalid_rect.height = widget->allocation.height;
1739 gdk_window_invalidate_rect (widget->window, &invalid_rect, TRUE);
1743 column_offset += tmpcolumn->width;
1748 invalidate_last_column (PsppSheetView *tree_view)
1753 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1755 for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
1757 last_column = (rtl ? last_column->next : last_column->prev))
1759 if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible)
1761 invalidate_column (tree_view, last_column->data);
1768 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_view,
1769 PsppSheetViewColumn *column)
1771 gint real_requested_width;
1773 if (column->use_resized_width)
1775 real_requested_width = column->resized_width;
1779 real_requested_width = column->fixed_width;
1782 if (column->min_width != -1)
1783 real_requested_width = MAX (real_requested_width, column->min_width);
1784 if (column->max_width != -1)
1785 real_requested_width = MIN (real_requested_width, column->max_width);
1787 return real_requested_width;
1791 span_intersects (int a0, int a_width,
1792 int b0, int b_width)
1794 int a1 = a0 + a_width;
1795 int b1 = b0 + b_width;
1796 return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1);
1799 /* GtkWidget::size_allocate helper */
1801 pspp_sheet_view_size_allocate_columns (GtkWidget *widget,
1802 gboolean *width_changed)
1804 PsppSheetView *tree_view;
1805 GList *list, *first_column, *last_column;
1806 PsppSheetViewColumn *column;
1807 GtkAllocation allocation;
1809 gint extra, extra_per_column;
1810 gint full_requested_width = 0;
1811 gint number_of_expand_columns = 0;
1812 gboolean column_changed = FALSE;
1815 tree_view = PSPP_SHEET_VIEW (widget);
1817 for (last_column = g_list_last (tree_view->priv->columns);
1818 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
1819 last_column = last_column->prev)
1821 if (last_column == NULL)
1824 for (first_column = g_list_first (tree_view->priv->columns);
1825 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
1826 first_column = first_column->next)
1830 allocation.height = tree_view->priv->header_height;
1832 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1834 /* find out how many extra space and expandable columns we have */
1835 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
1837 column = (PsppSheetViewColumn *)list->data;
1839 if (!column->visible)
1842 full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1845 number_of_expand_columns++;
1848 extra = MAX (widget->allocation.width - full_requested_width, 0);
1849 if (number_of_expand_columns > 0)
1850 extra_per_column = extra/number_of_expand_columns;
1852 extra_per_column = 0;
1854 for (list = (rtl ? last_column : first_column);
1855 list != (rtl ? first_column->prev : last_column->next);
1856 list = (rtl ? list->prev : list->next))
1858 gint real_requested_width = 0;
1861 column = list->data;
1862 old_width = column->width;
1864 if (!column->visible)
1867 /* We need to handle the dragged button specially.
1869 if (column == tree_view->priv->drag_column)
1871 GtkAllocation drag_allocation;
1872 gdk_drawable_get_size (tree_view->priv->drag_window,
1873 &(drag_allocation.width),
1874 &(drag_allocation.height));
1875 drag_allocation.x = 0;
1876 drag_allocation.y = 0;
1877 pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column,
1879 width += drag_allocation.width;
1883 real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1885 allocation.x = width;
1886 column->width = real_requested_width;
1890 if (number_of_expand_columns == 1)
1892 /* We add the remander to the last column as
1894 column->width += extra;
1898 column->width += extra_per_column;
1899 extra -= extra_per_column;
1900 number_of_expand_columns --;
1904 if (column->width != old_width)
1905 g_object_notify (G_OBJECT (column), "width");
1907 allocation.width = column->width;
1908 width += column->width;
1910 if (column->width > old_width)
1911 column_changed = TRUE;
1913 pspp_sheet_view_column_size_allocate (column, &allocation);
1915 if (span_intersects (allocation.x, allocation.width,
1916 tree_view->priv->hadjustment->value,
1917 widget->allocation.width)
1918 && gtk_widget_get_realized (widget))
1919 pspp_sheet_view_column_set_need_button (column, TRUE);
1922 gdk_window_move_resize (column->window,
1923 allocation.x + (rtl ? 0 : allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
1925 TREE_VIEW_DRAG_WIDTH, allocation.height);
1928 /* We change the width here. The user might have been resizing columns,
1929 * so the total width of the tree view changes.
1931 tree_view->priv->width = width;
1933 *width_changed = TRUE;
1936 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
1941 pspp_sheet_view_size_allocate (GtkWidget *widget,
1942 GtkAllocation *allocation)
1944 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1946 gboolean width_changed = FALSE;
1947 gint old_width = widget->allocation.width;
1949 if (allocation->width != widget->allocation.width)
1950 width_changed = TRUE;
1952 widget->allocation = *allocation;
1954 tmp_list = tree_view->priv->children;
1958 GtkAllocation allocation;
1960 PsppSheetViewChild *child = tmp_list->data;
1961 tmp_list = tmp_list->next;
1963 /* totally ignore our child's requisition */
1964 allocation.x = child->x;
1965 allocation.y = child->y;
1966 allocation.width = child->width;
1967 allocation.height = child->height;
1968 gtk_widget_size_allocate (child->widget, &allocation);
1971 /* We size-allocate the columns first because the width of the
1972 * tree view (used in updating the adjustments below) might change.
1974 pspp_sheet_view_size_allocate_columns (widget, &width_changed);
1976 tree_view->priv->hadjustment->page_size = allocation->width;
1977 tree_view->priv->hadjustment->page_increment = allocation->width * 0.9;
1978 tree_view->priv->hadjustment->step_increment = allocation->width * 0.1;
1979 tree_view->priv->hadjustment->lower = 0;
1980 tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->page_size, tree_view->priv->width);
1982 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
1984 if (allocation->width < tree_view->priv->width)
1986 if (tree_view->priv->init_hadjust_value)
1988 tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
1989 tree_view->priv->init_hadjust_value = FALSE;
1991 else if (allocation->width != old_width)
1993 tree_view->priv->hadjustment->value = CLAMP (tree_view->priv->hadjustment->value - allocation->width + old_width, 0, tree_view->priv->width - allocation->width);
1996 tree_view->priv->hadjustment->value = CLAMP (tree_view->priv->width - (tree_view->priv->prev_width - tree_view->priv->hadjustment->value), 0, tree_view->priv->width - allocation->width);
2000 tree_view->priv->hadjustment->value = 0;
2001 tree_view->priv->init_hadjust_value = TRUE;
2005 if (tree_view->priv->hadjustment->value + allocation->width > tree_view->priv->width)
2006 tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
2008 gtk_adjustment_changed (tree_view->priv->hadjustment);
2010 tree_view->priv->vadjustment->page_size = allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view);
2011 tree_view->priv->vadjustment->step_increment = tree_view->priv->vadjustment->page_size * 0.1;
2012 tree_view->priv->vadjustment->page_increment = tree_view->priv->vadjustment->page_size * 0.9;
2013 tree_view->priv->vadjustment->lower = 0;
2014 tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->page_size, tree_view->priv->height);
2016 gtk_adjustment_changed (tree_view->priv->vadjustment);
2018 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2019 if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size)
2020 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2021 else if (tree_view->priv->vadjustment->value + tree_view->priv->vadjustment->page_size > tree_view->priv->height)
2022 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2023 tree_view->priv->height - tree_view->priv->vadjustment->page_size);
2024 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2025 pspp_sheet_view_top_row_to_dy (tree_view);
2027 pspp_sheet_view_dy_to_top_row (tree_view);
2029 if (gtk_widget_get_realized (widget))
2031 gdk_window_move_resize (widget->window,
2032 allocation->x, allocation->y,
2033 allocation->width, allocation->height);
2034 gdk_window_move_resize (tree_view->priv->header_window,
2035 - (gint) tree_view->priv->hadjustment->value,
2037 MAX (tree_view->priv->width, allocation->width),
2038 tree_view->priv->header_height);
2039 gdk_window_move_resize (tree_view->priv->bin_window,
2040 - (gint) tree_view->priv->hadjustment->value,
2041 TREE_VIEW_HEADER_HEIGHT (tree_view),
2042 MAX (tree_view->priv->width, allocation->width),
2043 allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2046 if (tree_view->priv->row_count == 0)
2047 invalidate_empty_focus (tree_view);
2049 if (gtk_widget_get_realized (widget))
2051 gboolean has_expand_column = FALSE;
2052 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2054 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2056 has_expand_column = TRUE;
2061 /* This little hack only works if we have an LTR locale, and no column has the */
2064 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2065 ! has_expand_column)
2066 invalidate_last_column (tree_view);
2068 gtk_widget_queue_draw (widget);
2073 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2075 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2077 GtkWidget *widget = GTK_WIDGET (tree_view);
2079 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2080 gtk_widget_grab_focus (widget);
2081 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2085 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2088 return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2092 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2095 range_tower_set1 (tree_view->priv->selected, node, 1);
2099 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2102 range_tower_set0 (tree_view->priv->selected, node, 1);
2106 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2109 return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2113 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2116 return node > 0 ? node - 1 : -1;
2120 all_columns_selected (PsppSheetView *tree_view)
2124 for (list = tree_view->priv->columns; list; list = list->next)
2126 PsppSheetViewColumn *column = list->data;
2127 if (column->selectable && !column->selected)
2135 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2137 PsppSheetViewColumn *column,
2138 GdkEventButton *event)
2140 PsppSheetSelection *selection;
2141 PsppSheetSelectionMode mode;
2143 gboolean update_anchor;
2147 g_return_val_if_fail (tree_view != NULL, FALSE);
2148 g_return_val_if_fail (column != NULL, FALSE);
2150 selection = tree_view->priv->selection;
2151 mode = pspp_sheet_selection_get_mode (selection);
2152 if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2155 if (!column->row_head)
2160 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2161 if (event->type != GDK_BUTTON_PRESS
2162 || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2168 path = gtk_tree_path_new_from_indices (node, -1);
2171 pspp_sheet_selection_unselect_all (selection);
2172 pspp_sheet_selection_select_path (selection, path);
2173 pspp_sheet_selection_select_all_columns (selection);
2174 update_anchor = TRUE;
2177 else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2179 if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2180 || !all_columns_selected (tree_view))
2182 pspp_sheet_selection_unselect_all (selection);
2183 pspp_sheet_selection_select_path (selection, path);
2184 pspp_sheet_selection_select_all_columns (selection);
2185 update_anchor = TRUE;
2189 update_anchor = handled = FALSE;
2191 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2192 && modifiers == GDK_CONTROL_MASK)
2194 if (!all_columns_selected (tree_view))
2196 pspp_sheet_selection_unselect_all (selection);
2197 pspp_sheet_selection_select_all_columns (selection);
2200 if (pspp_sheet_selection_path_is_selected (selection, path))
2201 pspp_sheet_selection_unselect_path (selection, path);
2203 pspp_sheet_selection_select_path (selection, path);
2204 update_anchor = TRUE;
2207 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2208 && modifiers == GDK_SHIFT_MASK)
2210 GtkTreeRowReference *anchor = tree_view->priv->anchor;
2211 GtkTreePath *anchor_path;
2213 if (all_columns_selected (tree_view)
2214 && gtk_tree_row_reference_valid (anchor))
2216 update_anchor = FALSE;
2217 anchor_path = gtk_tree_row_reference_get_path (anchor);
2221 update_anchor = TRUE;
2222 anchor_path = gtk_tree_path_copy (path);
2225 pspp_sheet_selection_unselect_all (selection);
2226 pspp_sheet_selection_select_range (selection, anchor_path, path);
2227 pspp_sheet_selection_select_all_columns (selection);
2229 gtk_tree_path_free (anchor_path);
2234 update_anchor = handled = FALSE;
2238 if (tree_view->priv->anchor)
2239 gtk_tree_row_reference_free (tree_view->priv->anchor);
2240 tree_view->priv->anchor =
2241 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2242 tree_view->priv->model,
2246 gtk_tree_path_free (path);
2251 find_click (PsppSheetView *tree_view,
2254 PsppSheetViewColumn **column,
2255 GdkRectangle *background_area,
2256 GdkRectangle *cell_area)
2263 /* find the node that was clicked */
2264 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2267 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2272 background_area->y = y_offset + y;
2273 background_area->height = ROW_HEIGHT (tree_view);
2274 background_area->x = 0;
2276 /* Let the column have a chance at selecting it. */
2277 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2278 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2279 list; list = (rtl ? list->prev : list->next))
2281 PsppSheetViewColumn *candidate = list->data;
2283 if (!candidate->visible)
2286 background_area->width = candidate->width;
2287 if ((background_area->x > x) ||
2288 (background_area->x + background_area->width <= x))
2290 background_area->x += background_area->width;
2294 /* we found the focus column */
2296 pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2298 *column = candidate;
2306 pspp_sheet_view_button_press (GtkWidget *widget,
2307 GdkEventButton *event)
2309 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2311 PsppSheetViewColumn *column = NULL;
2313 GdkRectangle background_area;
2314 GdkRectangle cell_area;
2317 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2318 pspp_sheet_view_stop_editing (tree_view, FALSE);
2321 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2322 * we're done handling the button press.
2325 if (event->window == tree_view->priv->bin_window)
2330 gint pre_val, aft_val;
2331 PsppSheetViewColumn *column = NULL;
2332 GtkCellRenderer *focus_cell = NULL;
2333 gboolean row_double_click = FALSE;
2336 if (tree_view->priv->row_count == 0)
2338 grab_focus_and_unset_draw_keyfocus (tree_view);
2342 if (!find_click (tree_view, event->x, event->y, &node, &column,
2343 &background_area, &cell_area))
2345 grab_focus_and_unset_draw_keyfocus (tree_view);
2349 tree_view->priv->focus_column = column;
2351 if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2355 pre_val = tree_view->priv->vadjustment->value;
2357 path = _pspp_sheet_view_find_path (tree_view, node);
2359 /* we only handle selection modifications on the first button press
2361 if (event->type == GDK_BUTTON_PRESS)
2363 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2364 tree_view->priv->ctrl_pressed = TRUE;
2365 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2366 tree_view->priv->shift_pressed = TRUE;
2368 focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2370 pspp_sheet_view_column_focus_cell (column, focus_cell);
2372 if (event->state & GDK_CONTROL_MASK)
2374 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE);
2375 pspp_sheet_view_real_toggle_cursor_row (tree_view);
2377 else if (event->state & GDK_SHIFT_MASK)
2379 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE);
2380 pspp_sheet_view_real_select_cursor_row (tree_view, FALSE);
2384 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE);
2387 if (tree_view->priv->anchor_column == NULL ||
2388 !(event->state & GDK_SHIFT_MASK))
2389 tree_view->priv->anchor_column = column;
2390 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2391 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2392 tree_view->priv->anchor_column,
2395 tree_view->priv->ctrl_pressed = FALSE;
2396 tree_view->priv->shift_pressed = FALSE;
2399 /* the treeview may have been scrolled because of _set_cursor,
2403 aft_val = tree_view->priv->vadjustment->value;
2404 dval = pre_val - aft_val;
2406 cell_area.y += dval;
2407 background_area.y += dval;
2409 /* Save press to possibly begin a drag
2411 if (!tree_view->priv->in_grab &&
2412 tree_view->priv->pressed_button < 0)
2414 tree_view->priv->pressed_button = event->button;
2415 tree_view->priv->press_start_x = event->x;
2416 tree_view->priv->press_start_y = event->y;
2417 tree_view->priv->press_start_node = node;
2419 if (tree_view->priv->rubber_banding_enable
2420 && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2421 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2423 tree_view->priv->press_start_y += tree_view->priv->dy;
2424 tree_view->priv->rubber_band_x = event->x;
2425 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2426 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2428 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2429 tree_view->priv->rubber_band_ctrl = TRUE;
2430 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2431 tree_view->priv->rubber_band_shift = TRUE;
2436 /* Test if a double click happened on the same row. */
2437 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2439 int double_click_time, double_click_distance;
2441 g_object_get (gtk_settings_get_for_screen (
2442 gtk_widget_get_screen (widget)),
2443 "gtk-double-click-time", &double_click_time,
2444 "gtk-double-click-distance", &double_click_distance,
2447 /* Same conditions as _gdk_event_button_generate */
2448 if (tree_view->priv->last_button_x != -1 &&
2449 (event->time < tree_view->priv->last_button_time + double_click_time) &&
2450 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2451 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2453 /* We do no longer compare paths of this row and the
2454 * row clicked previously. We use the double click
2455 * distance to decide whether this is a valid click,
2456 * allowing the mouse to slightly move over another row.
2458 row_double_click = TRUE;
2460 tree_view->priv->last_button_time = 0;
2461 tree_view->priv->last_button_x = -1;
2462 tree_view->priv->last_button_y = -1;
2466 tree_view->priv->last_button_time = event->time;
2467 tree_view->priv->last_button_x = event->x;
2468 tree_view->priv->last_button_y = event->y;
2472 if (row_double_click)
2474 gtk_grab_remove (widget);
2475 pspp_sheet_view_row_activated (tree_view, path, column);
2477 if (tree_view->priv->pressed_button == event->button)
2478 tree_view->priv->pressed_button = -1;
2481 gtk_tree_path_free (path);
2483 /* If we activated the row through a double click we don't want to grab
2484 * focus back, as moving focus to another widget is pretty common.
2486 if (!row_double_click)
2487 grab_focus_and_unset_draw_keyfocus (tree_view);
2492 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2494 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2496 column = list->data;
2497 if (event->window == column->window &&
2498 column->resizable &&
2503 if (gdk_pointer_grab (column->window, FALSE,
2504 GDK_POINTER_MOTION_HINT_MASK |
2505 GDK_BUTTON1_MOTION_MASK |
2506 GDK_BUTTON_RELEASE_MASK,
2507 NULL, NULL, event->time))
2510 gtk_grab_add (widget);
2511 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2512 column->resized_width = column->width;
2514 /* block attached dnd signal handler */
2515 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2517 g_signal_handlers_block_matched (widget,
2518 G_SIGNAL_MATCH_DATA,
2522 tree_view->priv->drag_pos = i;
2523 tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2525 if (!gtk_widget_has_focus (widget))
2526 gtk_widget_grab_focus (widget);
2534 /* GtkWidget::button_release_event helper */
2536 pspp_sheet_view_button_release_drag_column (GtkWidget *widget,
2537 GdkEventButton *event)
2539 PsppSheetView *tree_view;
2543 tree_view = PSPP_SHEET_VIEW (widget);
2545 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2546 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2547 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2549 /* Move the button back */
2550 g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2552 g_object_ref (tree_view->priv->drag_column->button);
2553 gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2554 gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2555 gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2556 g_object_unref (tree_view->priv->drag_column->button);
2557 gtk_widget_queue_resize (widget);
2558 if (tree_view->priv->drag_column->resizable)
2560 gdk_window_raise (tree_view->priv->drag_column->window);
2561 gdk_window_show (tree_view->priv->drag_column->window);
2564 gdk_window_hide (tree_view->priv->drag_column->window);
2566 gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2570 if (tree_view->priv->cur_reorder &&
2571 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2572 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2573 tree_view->priv->cur_reorder->right_column);
2577 if (tree_view->priv->cur_reorder &&
2578 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2579 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2580 tree_view->priv->cur_reorder->left_column);
2582 tree_view->priv->drag_column = NULL;
2583 gdk_window_hide (tree_view->priv->drag_window);
2585 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2586 g_slice_free (PsppSheetViewColumnReorder, l->data);
2587 g_list_free (tree_view->priv->column_drag_info);
2588 tree_view->priv->column_drag_info = NULL;
2589 tree_view->priv->cur_reorder = NULL;
2591 if (tree_view->priv->drag_highlight_window)
2592 gdk_window_hide (tree_view->priv->drag_highlight_window);
2594 /* Reset our flags */
2595 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2596 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2601 /* GtkWidget::button_release_event helper */
2603 pspp_sheet_view_button_release_column_resize (GtkWidget *widget,
2604 GdkEventButton *event)
2606 PsppSheetView *tree_view;
2609 tree_view = PSPP_SHEET_VIEW (widget);
2611 tree_view->priv->drag_pos = -1;
2613 /* unblock attached dnd signal handler */
2614 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2616 g_signal_handlers_unblock_matched (widget,
2617 G_SIGNAL_MATCH_DATA,
2621 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2622 gtk_grab_remove (widget);
2623 gdk_display_pointer_ungrab (gdk_drawable_get_display (event->window),
2629 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2630 GdkEventButton *event)
2632 GtkCellEditable *cell_editable;
2637 PsppSheetViewColumn *column;
2638 GdkRectangle background_area;
2639 GdkRectangle cell_area;
2645 if (event->window != tree_view->priv->bin_window)
2648 /* Ignore a released button, if that button wasn't depressed */
2649 if (tree_view->priv->pressed_button != event->button)
2652 if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2656 /* decide if we edit */
2657 path = _pspp_sheet_view_find_path (tree_view, node);
2658 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2659 if (event->button != 1 || modifiers)
2662 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2663 pspp_sheet_view_column_cell_set_cell_data (column,
2664 tree_view->priv->model,
2667 if (!pspp_sheet_view_column_get_quick_edit (column)
2668 && _pspp_sheet_view_column_has_editable_cell (column))
2671 flags = 0; /* FIXME: get the right flags */
2672 path_string = gtk_tree_path_to_string (path);
2674 if (!_pspp_sheet_view_column_cell_event (column,
2682 if (cell_editable == NULL)
2685 pspp_sheet_view_real_set_cursor (tree_view, path,
2687 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2690 _pspp_sheet_view_column_get_neighbor_sizes (
2691 column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2694 area.width -= right + left;
2696 pspp_sheet_view_real_start_editing (tree_view,
2703 g_free (path_string);
2704 gtk_tree_path_free (path);
2709 pspp_sheet_view_button_release (GtkWidget *widget,
2710 GdkEventButton *event)
2712 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2714 pspp_sheet_view_stop_editing (tree_view, FALSE);
2715 if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2716 && pspp_sheet_view_button_release_edit (tree_view, event))
2718 if (tree_view->priv->pressed_button == event->button)
2719 tree_view->priv->pressed_button = -1;
2721 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2725 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2726 return pspp_sheet_view_button_release_drag_column (widget, event);
2728 if (tree_view->priv->rubber_band_status)
2729 pspp_sheet_view_stop_rubber_band (tree_view);
2731 if (tree_view->priv->pressed_button == event->button)
2732 tree_view->priv->pressed_button = -1;
2734 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2735 return pspp_sheet_view_button_release_column_resize (widget, event);
2741 pspp_sheet_view_grab_broken (GtkWidget *widget,
2742 GdkEventGrabBroken *event)
2744 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2746 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2747 pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2749 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2750 pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2755 /* GtkWidget::motion_event function set.
2759 do_prelight (PsppSheetView *tree_view,
2761 /* these are in bin_window coords */
2765 int prev_node = tree_view->priv->prelight_node;
2767 if (prev_node != node)
2769 tree_view->priv->prelight_node = node;
2772 _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2775 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2780 prelight_or_select (PsppSheetView *tree_view,
2782 /* these are in bin_window coords */
2786 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2788 if (tree_view->priv->hover_selection &&
2789 (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2790 !(tree_view->priv->edited_column &&
2791 tree_view->priv->edited_column->editable_widget))
2795 if (!pspp_sheet_view_node_is_selected (tree_view, node))
2799 path = _pspp_sheet_view_find_path (tree_view, node);
2800 pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2801 if (pspp_sheet_view_node_is_selected (tree_view, node))
2803 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2804 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE);
2806 gtk_tree_path_free (path);
2810 else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2811 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2814 do_prelight (tree_view, node, x, y);
2818 ensure_unprelighted (PsppSheetView *tree_view)
2820 do_prelight (tree_view,
2822 -1000, -1000); /* coords not possibly over an arrow */
2824 g_assert (tree_view->priv->prelight_node < 0);
2828 update_prelight (PsppSheetView *tree_view,
2835 if (tree_view->priv->row_count == 0)
2840 ensure_unprelighted (tree_view);
2844 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2848 pspp_sheet_view_find_offset (tree_view, new_y, &node);
2851 prelight_or_select (tree_view, node, x, y);
2857 /* Our motion arrow is either a box (in the case of the original spot)
2858 * or an arrow. It is expander_size wide.
2881 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2883 PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2884 GtkWidget *widget = GTK_WIDGET (tree_view);
2885 GdkBitmap *mask = NULL;
2890 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2891 GdkWindowAttr attributes;
2892 guint attributes_mask;
2895 reorder->left_column == tree_view->priv->drag_column ||
2896 reorder->right_column == tree_view->priv->drag_column)
2897 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2898 else if (reorder->left_column || reorder->right_column)
2900 GdkRectangle visible_rect;
2901 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2902 if (reorder->left_column)
2903 x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2905 x = reorder->right_column->allocation.x;
2907 if (x < visible_rect.x)
2908 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2909 else if (x > visible_rect.x + visible_rect.width)
2910 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2912 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2915 /* We want to draw the rectangle over the initial location. */
2916 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2921 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2923 if (tree_view->priv->drag_highlight_window)
2925 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2927 gdk_window_destroy (tree_view->priv->drag_highlight_window);
2930 attributes.window_type = GDK_WINDOW_CHILD;
2931 attributes.wclass = GDK_INPUT_OUTPUT;
2932 attributes.x = tree_view->priv->drag_column_x;
2934 width = attributes.width = tree_view->priv->drag_column->allocation.width;
2935 height = attributes.height = tree_view->priv->drag_column->allocation.height;
2936 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
2937 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
2938 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
2939 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
2940 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
2941 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
2943 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
2944 gc = gdk_gc_new (mask);
2946 gdk_gc_set_foreground (gc, &col);
2947 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
2949 gdk_gc_set_foreground(gc, &col);
2950 gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
2951 g_object_unref (gc);
2953 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
2955 if (mask) g_object_unref (mask);
2956 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2959 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
2965 width = tree_view->priv->expander_size;
2967 /* Get x, y, width, height of arrow */
2968 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
2969 if (reorder->left_column)
2971 x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
2972 height = reorder->left_column->allocation.height;
2976 x += reorder->right_column->allocation.x - width/2;
2977 height = reorder->right_column->allocation.height;
2979 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
2980 height += tree_view->priv->expander_size;
2982 /* Create the new window */
2983 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
2985 if (tree_view->priv->drag_highlight_window)
2987 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2989 gdk_window_destroy (tree_view->priv->drag_highlight_window);
2992 attributes.window_type = GDK_WINDOW_TEMP;
2993 attributes.wclass = GDK_INPUT_OUTPUT;
2994 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
2995 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
2996 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
2997 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3000 attributes.width = width;
3001 attributes.height = height;
3002 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3003 &attributes, attributes_mask);
3004 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3006 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3007 gc = gdk_gc_new (mask);
3009 gdk_gc_set_foreground (gc, &col);
3010 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3012 /* Draw the 2 arrows as per above */
3014 gdk_gc_set_foreground (gc, &col);
3015 for (i = 0; i < width; i ++)
3017 if (i == (width/2 - 1))
3019 gdk_draw_line (mask, gc, i, j, i, height - j);
3020 if (i < (width/2 - 1))
3025 g_object_unref (gc);
3026 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3028 if (mask) g_object_unref (mask);
3031 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3032 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3034 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3035 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3041 width = tree_view->priv->expander_size;
3043 /* Get x, y, width, height of arrow */
3044 width = width/2; /* remember, the arrow only takes half the available width */
3045 gdk_window_get_origin (widget->window, &x, &y);
3046 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3047 x += widget->allocation.width - width;
3049 if (reorder->left_column)
3050 height = reorder->left_column->allocation.height;
3052 height = reorder->right_column->allocation.height;
3054 y -= tree_view->priv->expander_size;
3055 height += 2*tree_view->priv->expander_size;
3057 /* Create the new window */
3058 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3059 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3061 if (tree_view->priv->drag_highlight_window)
3063 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3065 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3068 attributes.window_type = GDK_WINDOW_TEMP;
3069 attributes.wclass = GDK_INPUT_OUTPUT;
3070 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3071 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3072 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3073 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3076 attributes.width = width;
3077 attributes.height = height;
3078 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3079 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3081 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3082 gc = gdk_gc_new (mask);
3084 gdk_gc_set_foreground (gc, &col);
3085 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3087 /* Draw the 2 arrows as per above */
3089 gdk_gc_set_foreground (gc, &col);
3090 j = tree_view->priv->expander_size;
3091 for (i = 0; i < width; i ++)
3094 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3098 gdk_draw_line (mask, gc, k, j, k, height - j);
3099 gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3100 gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3103 g_object_unref (gc);
3104 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3106 if (mask) g_object_unref (mask);
3109 tree_view->priv->drag_column_window_state = arrow_type;
3110 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3114 g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3115 gdk_window_hide (tree_view->priv->drag_highlight_window);
3119 gdk_window_show (tree_view->priv->drag_highlight_window);
3120 gdk_window_raise (tree_view->priv->drag_highlight_window);
3124 pspp_sheet_view_motion_resize_column (GtkWidget *widget,
3125 GdkEventMotion *event)
3129 PsppSheetViewColumn *column;
3130 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3132 column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3134 if (event->is_hint || event->window != widget->window)
3135 gtk_widget_get_pointer (widget, &x, NULL);
3139 if (tree_view->priv->hadjustment)
3140 x += tree_view->priv->hadjustment->value;
3142 new_width = pspp_sheet_view_new_column_width (tree_view,
3143 tree_view->priv->drag_pos, &x);
3144 if (x != tree_view->priv->x_drag &&
3145 (new_width != column->fixed_width))
3147 column->use_resized_width = TRUE;
3148 column->resized_width = new_width;
3151 column->resized_width -= tree_view->priv->last_extra_space_per_column;
3153 gtk_widget_queue_resize (widget);
3161 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3163 PsppSheetViewColumnReorder *reorder = NULL;
3167 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3168 for (list = tree_view->priv->column_drag_info; list; list = list->next)
3170 reorder = (PsppSheetViewColumnReorder *) list->data;
3171 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3176 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3179 tree_view->priv->cur_reorder = reorder;
3180 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3184 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3186 GdkRectangle visible_rect;
3191 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3192 y += tree_view->priv->dy;
3194 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3196 /* see if we are near the edge. */
3197 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3200 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3205 value = CLAMP (tree_view->priv->vadjustment->value + offset, 0.0,
3206 tree_view->priv->vadjustment->upper - tree_view->priv->vadjustment->page_size);
3207 gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3211 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3213 GdkRectangle visible_rect;
3218 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3220 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3222 /* See if we are near the edge. */
3223 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3226 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3232 value = CLAMP (tree_view->priv->hadjustment->value + offset,
3233 0.0, tree_view->priv->hadjustment->upper - tree_view->priv->hadjustment->page_size);
3234 gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3241 pspp_sheet_view_motion_drag_column (GtkWidget *widget,
3242 GdkEventMotion *event)
3244 PsppSheetView *tree_view = (PsppSheetView *) widget;
3245 PsppSheetViewColumn *column = tree_view->priv->drag_column;
3249 if ((column == NULL) ||
3250 (event->window != tree_view->priv->drag_window))
3253 /* Handle moving the header */
3254 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3255 x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3256 MAX (tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.width) - column->allocation.width);
3257 gdk_window_move (tree_view->priv->drag_window, x, y);
3259 /* autoscroll, if needed */
3260 pspp_sheet_view_horizontal_autoscroll (tree_view);
3261 /* Update the current reorder position and arrow; */
3262 pspp_sheet_view_update_current_reorder (tree_view);
3268 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3270 remove_scroll_timeout (tree_view);
3271 gtk_grab_remove (GTK_WIDGET (tree_view));
3273 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3275 GtkTreePath *tmp_path;
3277 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3279 /* The anchor path should be set to the start path */
3280 tmp_path = _pspp_sheet_view_find_path (tree_view,
3281 tree_view->priv->rubber_band_start_node);
3283 if (tree_view->priv->anchor)
3284 gtk_tree_row_reference_free (tree_view->priv->anchor);
3286 tree_view->priv->anchor =
3287 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3288 tree_view->priv->model,
3291 gtk_tree_path_free (tmp_path);
3293 /* ... and the cursor to the end path */
3294 tmp_path = _pspp_sheet_view_find_path (tree_view,
3295 tree_view->priv->rubber_band_end_node);
3296 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE);
3297 gtk_tree_path_free (tmp_path);
3299 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3302 /* Clear status variables */
3303 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3304 tree_view->priv->rubber_band_shift = 0;
3305 tree_view->priv->rubber_band_ctrl = 0;
3307 tree_view->priv->rubber_band_start_node = -1;
3308 tree_view->priv->rubber_band_end_node = -1;
3312 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3316 gboolean skip_start,
3319 if (start_node == end_node)
3322 /* We skip the first node and jump inside the loop */
3328 /* Small optimization by assuming insensitive nodes are never
3333 if (tree_view->priv->rubber_band_shift)
3334 pspp_sheet_view_node_select (tree_view, start_node);
3335 else if (tree_view->priv->rubber_band_ctrl)
3337 /* Toggle the selection state */
3338 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3339 pspp_sheet_view_node_unselect (tree_view, start_node);
3341 pspp_sheet_view_node_select (tree_view, start_node);
3344 pspp_sheet_view_node_select (tree_view, start_node);
3348 /* Mirror the above */
3349 if (tree_view->priv->rubber_band_shift)
3350 pspp_sheet_view_node_unselect (tree_view, start_node);
3351 else if (tree_view->priv->rubber_band_ctrl)
3353 /* Toggle the selection state */
3354 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3355 pspp_sheet_view_node_unselect (tree_view, start_node);
3357 pspp_sheet_view_node_select (tree_view, start_node);
3360 pspp_sheet_view_node_unselect (tree_view, start_node);
3363 _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3365 if (start_node == end_node)
3370 start_node = pspp_sheet_view_node_next (tree_view, start_node);
3373 /* Ran out of tree */
3376 if (skip_end && start_node == end_node)
3383 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3386 return node * tree_view->priv->fixed_height;
3390 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3394 int fixed_height = tree_view->priv->fixed_height;
3395 if (fixed_height <= 0
3397 || height >= tree_view->priv->row_count * fixed_height)
3404 *new_node = height / fixed_height;
3405 return height % fixed_height;
3410 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3415 pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3416 pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3418 /* Handle the start area first */
3419 if (tree_view->priv->rubber_band_start_node < 0)
3421 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3428 else if (start_node < tree_view->priv->rubber_band_start_node)
3430 /* New node is above the old one; selection became bigger */
3431 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3433 tree_view->priv->rubber_band_start_node,
3438 else if (start_node > tree_view->priv->rubber_band_start_node)
3440 /* New node is below the old one; selection became smaller */
3441 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3442 tree_view->priv->rubber_band_start_node,
3449 tree_view->priv->rubber_band_start_node = start_node;
3451 /* Next, handle the end area */
3452 if (tree_view->priv->rubber_band_end_node < 0)
3454 /* In the event this happens, start_node was also -1; this case is
3458 else if (end_node < 0)
3460 /* Find the last node in the tree */
3461 pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3464 /* Selection reached end of the tree */
3465 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3466 tree_view->priv->rubber_band_end_node,
3472 else if (end_node > tree_view->priv->rubber_band_end_node)
3474 /* New node is below the old one; selection became bigger */
3475 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3476 tree_view->priv->rubber_band_end_node,
3482 else if (end_node < tree_view->priv->rubber_band_end_node)
3484 /* New node is above the old one; selection became smaller */
3485 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3487 tree_view->priv->rubber_band_end_node,
3493 tree_view->priv->rubber_band_end_node = end_node;
3497 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3500 GdkRectangle old_area;
3501 GdkRectangle new_area;
3502 GdkRectangle common;
3503 GdkRegion *invalid_region;
3504 PsppSheetViewColumn *column;
3506 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3507 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3508 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3509 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3511 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3514 y = MAX (y, 0) + tree_view->priv->dy;
3516 new_area.x = MIN (tree_view->priv->press_start_x, x);
3517 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3518 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3519 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3521 invalid_region = gdk_region_rectangle (&old_area);
3522 gdk_region_union_with_rect (invalid_region, &new_area);
3524 gdk_rectangle_intersect (&old_area, &new_area, &common);
3525 if (common.width > 2 && common.height > 2)
3527 GdkRegion *common_region;
3529 /* make sure the border is invalidated */
3535 common_region = gdk_region_rectangle (&common);
3537 gdk_region_subtract (invalid_region, common_region);
3538 gdk_region_destroy (common_region);
3541 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
3543 gdk_region_destroy (invalid_region);
3545 tree_view->priv->rubber_band_x = x;
3546 tree_view->priv->rubber_band_y = y;
3547 pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3549 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3550 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3551 tree_view->priv->anchor_column,
3554 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3556 pspp_sheet_view_update_rubber_band_selection (tree_view);
3560 pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view,
3565 GdkRectangle rubber_rect;
3568 rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3569 rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3570 rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3571 rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3573 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3576 cr = gdk_cairo_create (tree_view->priv->bin_window);
3577 cairo_set_line_width (cr, 1.0);
3579 cairo_set_source_rgba (cr,
3580 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].red / 65535.,
3581 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].green / 65535.,
3582 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].blue / 65535.,
3585 gdk_cairo_rectangle (cr, &rect);
3589 cairo_set_source_rgb (cr,
3590 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].red / 65535.,
3591 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].green / 65535.,
3592 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].blue / 65535.);
3594 cairo_rectangle (cr,
3595 rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3596 rubber_rect.width - 1, rubber_rect.height - 1);
3603 pspp_sheet_view_motion_bin_window (GtkWidget *widget,
3604 GdkEventMotion *event)
3606 PsppSheetView *tree_view;
3610 tree_view = (PsppSheetView *) widget;
3612 if (tree_view->priv->row_count == 0)
3615 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3617 GdkRectangle background_area, cell_area;
3618 PsppSheetViewColumn *column;
3620 if (find_click (tree_view, event->x, event->y, &node, &column,
3621 &background_area, &cell_area)
3622 && tree_view->priv->focus_column == column
3623 && tree_view->priv->press_start_node == node)
3626 gtk_grab_add (GTK_WIDGET (tree_view));
3627 pspp_sheet_view_update_rubber_band (tree_view);
3629 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3631 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3633 pspp_sheet_view_update_rubber_band (tree_view);
3635 add_scroll_timeout (tree_view);
3638 /* only check for an initiated drag when a button is pressed */
3639 if (tree_view->priv->pressed_button >= 0
3640 && !tree_view->priv->rubber_band_status)
3641 pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3643 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3647 pspp_sheet_view_find_offset (tree_view, new_y, &node);
3649 tree_view->priv->event_last_x = event->x;
3650 tree_view->priv->event_last_y = event->y;
3652 prelight_or_select (tree_view, node, event->x, event->y);
3658 pspp_sheet_view_motion (GtkWidget *widget,
3659 GdkEventMotion *event)
3661 PsppSheetView *tree_view;
3663 tree_view = (PsppSheetView *) widget;
3665 /* Resizing a column */
3666 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3667 return pspp_sheet_view_motion_resize_column (widget, event);
3670 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3671 return pspp_sheet_view_motion_drag_column (widget, event);
3673 /* Sanity check it */
3674 if (event->window == tree_view->priv->bin_window)
3675 return pspp_sheet_view_motion_bin_window (widget, event);
3680 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3681 * the tree is empty.
3684 invalidate_empty_focus (PsppSheetView *tree_view)
3688 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3693 gdk_drawable_get_size (tree_view->priv->bin_window, &area.width, &area.height);
3694 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3697 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3701 draw_empty_focus (PsppSheetView *tree_view, GdkRectangle *clip_area)
3703 GtkWidget *widget = GTK_WIDGET (tree_view);
3706 if (!gtk_widget_has_focus (widget))
3709 gdk_drawable_get_size (tree_view->priv->bin_window, &w, &h);
3715 gtk_paint_focus (gtk_widget_get_style (widget),
3716 tree_view->priv->bin_window,
3717 gtk_widget_get_state (widget),
3725 pspp_sheet_view_draw_grid_lines (PsppSheetView *tree_view,
3726 GdkEventExpose *event,
3727 gint n_visible_columns,
3731 GList *list = tree_view->priv->columns;
3736 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3737 && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3740 gdk_drawable_get_size (event->window, NULL, &height);
3742 /* Only draw the lines for visible rows and columns */
3743 for (list = tree_view->priv->columns; list; list = list->next, i++)
3745 PsppSheetViewColumn *column = list->data;
3747 /* We don't want a line for the last column */
3748 if (i == n_visible_columns - 1)
3751 if (! column->visible)
3754 current_x += column->width;
3756 if (current_x - 1 >= event->area.x
3757 && current_x - 1 < event->area.x + event->area.width)
3758 gdk_draw_line (event->window,
3759 tree_view->priv->grid_line_gc[GTK_WIDGET(tree_view)->state],
3760 current_x - 1, min_y,
3761 current_x - 1, max_y - min_y);
3765 /* Warning: Very scary function.
3766 * Modify at your own risk
3768 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3769 * FIXME: It's not...
3772 pspp_sheet_view_bin_expose (GtkWidget *widget,
3773 GdkEventExpose *event)
3775 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3780 int drag_highlight = -1;
3783 gint y_offset, cell_offset;
3785 GdkRectangle background_area;
3786 GdkRectangle cell_area;
3788 gint bin_window_width;
3789 gint bin_window_height;
3790 GtkTreePath *cursor_path;
3791 GtkTreePath *drag_dest_path;
3792 GList *first_column, *last_column;
3793 gint vertical_separator;
3794 gint horizontal_separator;
3795 gint focus_line_width;
3796 gboolean allow_rules;
3797 gboolean has_special_cell;
3799 gint n_visible_columns;
3800 gint grid_line_width;
3801 gboolean row_ending_details;
3802 gboolean draw_vgrid_lines, draw_hgrid_lines;
3805 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3807 gtk_widget_style_get (widget,
3808 "horizontal-separator", &horizontal_separator,
3809 "vertical-separator", &vertical_separator,
3810 "allow-rules", &allow_rules,
3811 "focus-line-width", &focus_line_width,
3812 "row-ending-details", &row_ending_details,
3815 if (tree_view->priv->row_count == 0)
3817 draw_empty_focus (tree_view, &event->area);
3821 /* clip event->area to the visible area */
3822 if (event->area.height < 0)
3825 validate_visible_area (tree_view);
3827 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, event->area.y);
3831 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3832 gdk_drawable_get_size (tree_view->priv->bin_window,
3833 &bin_window_width, &bin_window_height);
3835 if (tree_view->priv->height < bin_window_height)
3837 gtk_paint_flat_box (widget->style,
3844 0, tree_view->priv->height,
3846 bin_window_height - tree_view->priv->height);
3852 /* find the path for the node */
3853 path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3854 gtk_tree_model_get_iter (tree_view->priv->model,
3857 gtk_tree_path_free (path);
3860 drag_dest_path = NULL;
3862 if (tree_view->priv->cursor)
3863 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3866 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3868 if (tree_view->priv->drag_dest_row)
3869 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3872 _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3876 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3877 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3879 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
3880 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3882 if (draw_vgrid_lines || draw_hgrid_lines)
3883 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
3885 n_visible_columns = 0;
3886 for (list = tree_view->priv->columns; list; list = list->next)
3888 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
3890 n_visible_columns ++;
3893 /* Find the last column */
3894 for (last_column = g_list_last (tree_view->priv->columns);
3895 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
3896 last_column = last_column->prev)
3900 for (first_column = g_list_first (tree_view->priv->columns);
3901 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
3902 first_column = first_column->next)
3905 /* Actually process the expose event. To do this, we want to
3906 * start at the first node of the event, and walk the tree in
3907 * order, drawing each successive node.
3914 gboolean is_first = FALSE;
3915 gboolean is_last = FALSE;
3916 gboolean done = FALSE;
3919 max_height = ROW_HEIGHT (tree_view);
3923 background_area.y = y_offset + event->area.y;
3924 background_area.height = max_height;
3925 max_y = background_area.y + max_height;
3929 if (node == tree_view->priv->prelight_node)
3930 flags |= GTK_CELL_RENDERER_PRELIT;
3932 selected = pspp_sheet_view_node_is_selected (tree_view, node);
3936 if (tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
3938 /* we *need* to set cell data on all cells before the call
3939 * to _has_special_cell, else _has_special_cell() does not
3940 * return a correct value.
3942 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
3944 list = (rtl ? list->prev : list->next))
3946 PsppSheetViewColumn *column = list->data;
3947 pspp_sheet_view_column_cell_set_cell_data (column,
3948 tree_view->priv->model,
3952 has_special_cell = pspp_sheet_view_has_special_cell (tree_view);
3955 has_special_cell = tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
3957 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
3959 list = (rtl ? list->prev : list->next))
3961 PsppSheetViewColumn *column = list->data;
3962 const gchar *detail = NULL;
3963 gboolean selected_column;
3966 if (!column->visible)
3969 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
3970 selected_column = column->selected && column->selectable;
3972 selected_column = TRUE;
3974 if (cell_offset > event->area.x + event->area.width ||
3975 cell_offset + column->width < event->area.x)
3977 cell_offset += column->width;
3981 if (selected && selected_column)
3982 flags |= GTK_CELL_RENDERER_SELECTED;
3984 flags &= ~GTK_CELL_RENDERER_SELECTED;
3986 if (column->show_sort_indicator)
3987 flags |= GTK_CELL_RENDERER_SORTED;
3989 flags &= ~GTK_CELL_RENDERER_SORTED;
3992 flags |= GTK_CELL_RENDERER_FOCUSED;
3994 flags &= ~GTK_CELL_RENDERER_FOCUSED;
3996 background_area.x = cell_offset;
3997 background_area.width = column->width;
3999 cell_area = background_area;
4000 cell_area.y += vertical_separator / 2;
4001 cell_area.x += horizontal_separator / 2;
4002 cell_area.height -= vertical_separator;
4003 cell_area.width -= horizontal_separator;
4005 if (draw_vgrid_lines)
4007 if (list == first_column)
4009 cell_area.width -= grid_line_width / 2;
4011 else if (list == last_column)
4013 cell_area.x += grid_line_width / 2;
4014 cell_area.width -= grid_line_width / 2;
4018 cell_area.x += grid_line_width / 2;
4019 cell_area.width -= grid_line_width;
4023 if (draw_hgrid_lines)
4025 cell_area.y += grid_line_width / 2;
4026 cell_area.height -= grid_line_width;
4029 if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4031 cell_offset += column->width;
4035 pspp_sheet_view_column_cell_set_cell_data (column,
4036 tree_view->priv->model,
4039 /* Select the detail for drawing the cell. relevant
4040 * factors are parity, sortedness, and whether to
4043 if (allow_rules && tree_view->priv->has_rules)
4045 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4046 n_visible_columns >= 3)
4049 detail = "cell_odd_ruled_sorted";
4051 detail = "cell_even_ruled_sorted";
4056 detail = "cell_odd_ruled";
4058 detail = "cell_even_ruled";
4063 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4064 n_visible_columns >= 3)
4067 detail = "cell_odd_sorted";
4069 detail = "cell_even_sorted";
4074 detail = "cell_odd";
4076 detail = "cell_even";
4082 if (widget->state == GTK_STATE_INSENSITIVE)
4083 state = GTK_STATE_INSENSITIVE;
4084 else if (flags & GTK_CELL_RENDERER_SELECTED)
4085 state = GTK_STATE_SELECTED;
4087 state = GTK_STATE_NORMAL;
4089 /* Draw background */
4090 if (row_ending_details)
4092 char new_detail[128];
4094 is_first = (rtl ? !list->next : !list->prev);
4095 is_last = (rtl ? !list->prev : !list->next);
4097 /* (I don't like the snprintfs either, but couldn't find a
4100 if (is_first && is_last)
4101 g_snprintf (new_detail, 127, "%s", detail);
4103 g_snprintf (new_detail, 127, "%s_start", detail);
4105 g_snprintf (new_detail, 127, "%s_end", detail);
4107 g_snprintf (new_detail, 128, "%s_middle", detail);
4109 gtk_paint_flat_box (widget->style,
4118 background_area.width,
4119 background_area.height);
4123 gtk_paint_flat_box (widget->style,
4132 background_area.width,
4133 background_area.height);
4136 if (draw_hgrid_lines)
4138 if (background_area.y > 0)
4139 gdk_draw_line (event->window,
4140 tree_view->priv->grid_line_gc[widget->state],
4141 background_area.x, background_area.y,
4142 background_area.x + background_area.width,
4145 if (y_offset + max_height >= event->area.height)
4146 gdk_draw_line (event->window,
4147 tree_view->priv->grid_line_gc[widget->state],
4148 background_area.x, background_area.y + max_height,
4149 background_area.x + background_area.width,
4150 background_area.y + max_height);
4153 _pspp_sheet_view_column_cell_render (column,
4160 if (node == cursor && has_special_cell &&
4161 ((column == tree_view->priv->focus_column &&
4162 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4163 gtk_widget_has_focus (widget)) ||
4164 (column == tree_view->priv->edited_column)))
4166 _pspp_sheet_view_column_cell_draw_focus (column,
4174 cell_offset += column->width;
4177 if (cell_offset < event->area.x)
4179 gtk_paint_flat_box (widget->style,
4188 event->area.x - cell_offset,
4189 background_area.height);
4192 if (node == drag_highlight)
4194 /* Draw indicator for the drop
4196 gint highlight_y = -1;
4200 switch (tree_view->priv->drag_dest_pos)
4202 case PSPP_SHEET_VIEW_DROP_BEFORE:
4203 highlight_y = background_area.y - 1;
4204 if (highlight_y < 0)
4208 case PSPP_SHEET_VIEW_DROP_AFTER:
4209 highlight_y = background_area.y + background_area.height - 1;
4212 case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE:
4213 case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER:
4214 _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node);
4218 gdk_drawable_get_size (tree_view->priv->bin_window,
4221 if (row_ending_details)
4222 gtk_paint_focus (widget->style,
4223 tree_view->priv->bin_window,
4224 gtk_widget_get_state (widget),
4228 ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4229 : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4230 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4231 - focus_line_width / 2,
4232 width, ROW_HEIGHT (tree_view)
4233 - focus_line_width + 1);
4235 gtk_paint_focus (widget->style,
4236 tree_view->priv->bin_window,
4237 gtk_widget_get_state (widget),
4240 "treeview-drop-indicator",
4241 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4242 - focus_line_width / 2,
4243 width, ROW_HEIGHT (tree_view)
4244 - focus_line_width + 1);
4248 if (highlight_y >= 0)
4250 gdk_draw_line (event->window,
4251 widget->style->fg_gc[gtk_widget_get_state (widget)],
4254 rtl ? 0 : bin_window_width,
4259 /* draw the big row-spanning focus rectangle, if needed */
4260 if (!has_special_cell && node == cursor &&
4261 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4262 gtk_widget_has_focus (widget))
4264 gint tmp_y, tmp_height;
4266 GtkStateType focus_rect_state;
4269 flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
4270 (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
4271 (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE :
4274 gdk_drawable_get_size (tree_view->priv->bin_window,
4277 if (draw_hgrid_lines)
4279 tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node) + grid_line_width / 2;
4280 tmp_height = ROW_HEIGHT (tree_view) - grid_line_width;
4284 tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node);
4285 tmp_height = ROW_HEIGHT (tree_view);
4288 if (row_ending_details)
4289 gtk_paint_focus (widget->style,
4290 tree_view->priv->bin_window,
4295 ? (is_last ? "treeview" : "treeview-left" )
4296 : (is_last ? "treeview-right" : "treeview-middle" )),
4300 gtk_paint_focus (widget->style,
4301 tree_view->priv->bin_window,
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 (has_next, FALSE);
4328 while (y_offset < event->area.height);
4331 pspp_sheet_view_draw_grid_lines (tree_view, event, n_visible_columns,
4334 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4336 GdkRectangle *rectangles;
4339 gdk_region_get_rectangles (event->region,
4343 while (n_rectangles--)
4344 pspp_sheet_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
4346 g_free (rectangles);
4350 gtk_tree_path_free (cursor_path);
4353 gtk_tree_path_free (drag_dest_path);
4359 pspp_sheet_view_expose (GtkWidget *widget,
4360 GdkEventExpose *event)
4362 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4364 if (event->window == tree_view->priv->bin_window)
4369 retval = pspp_sheet_view_bin_expose (widget, event);
4371 /* We can't just chain up to Container::expose as it will try to send the
4372 * event to the headers, so we handle propagating it to our children
4373 * (eg. widgets being edited) ourselves.
4375 tmp_list = tree_view->priv->children;
4378 PsppSheetViewChild *child = tmp_list->data;
4379 tmp_list = tmp_list->next;
4381 gtk_container_propagate_expose (GTK_CONTAINER (tree_view), child->widget, event);
4387 else if (event->window == tree_view->priv->header_window)
4389 gint n_visible_columns;
4392 gtk_paint_flat_box (widget->style,
4402 event->area.height);
4404 for (list = tree_view->priv->columns; list != NULL; list = list->next)
4406 PsppSheetViewColumn *column = list->data;
4408 if (column == tree_view->priv->drag_column || !column->visible)
4411 if (span_intersects (column->allocation.x, column->allocation.width,
4412 event->area.x, event->area.width)
4413 && column->button != NULL)
4414 gtk_container_propagate_expose (GTK_CONTAINER (tree_view),
4415 column->button, event);
4418 n_visible_columns = 0;
4419 for (list = tree_view->priv->columns; list; list = list->next)
4421 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4423 n_visible_columns ++;
4425 pspp_sheet_view_draw_grid_lines (tree_view,
4429 event->area.height);
4431 else if (event->window == tree_view->priv->drag_window)
4433 gtk_container_propagate_expose (GTK_CONTAINER (tree_view),
4434 tree_view->priv->drag_column->button,
4448 /* returns 0x1 when no column has been found -- yes it's hackish */
4449 static PsppSheetViewColumn *
4450 pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
4451 PsppSheetViewColumn *column,
4454 PsppSheetViewColumn *left_column = NULL;
4455 PsppSheetViewColumn *cur_column = NULL;
4458 if (!column->reorderable)
4459 return (PsppSheetViewColumn *)0x1;
4461 switch (drop_position)
4464 /* find first column where we can drop */
4465 tmp_list = tree_view->priv->columns;
4466 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4467 return (PsppSheetViewColumn *)0x1;
4471 g_assert (tmp_list);
4473 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4474 tmp_list = tmp_list->next;
4476 if (left_column && left_column->visible == FALSE)
4479 if (!tree_view->priv->column_drop_func)
4482 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4484 left_column = cur_column;
4491 if (!tree_view->priv->column_drop_func)
4494 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4497 return (PsppSheetViewColumn *)0x1;
4501 /* find first column after column where we can drop */
4502 tmp_list = tree_view->priv->columns;
4504 for (; tmp_list; tmp_list = tmp_list->next)
4505 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4508 if (!tmp_list || !tmp_list->next)
4509 return (PsppSheetViewColumn *)0x1;
4511 tmp_list = tmp_list->next;
4512 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4513 tmp_list = tmp_list->next;
4517 g_assert (tmp_list);
4519 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4520 tmp_list = tmp_list->next;
4522 if (left_column && left_column->visible == FALSE)
4524 left_column = cur_column;
4526 tmp_list = tmp_list->next;
4530 if (!tree_view->priv->column_drop_func)
4533 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4535 left_column = cur_column;
4542 if (!tree_view->priv->column_drop_func)
4545 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4548 return (PsppSheetViewColumn *)0x1;
4552 /* find first column before column where we can drop */
4553 tmp_list = tree_view->priv->columns;
4555 for (; tmp_list; tmp_list = tmp_list->next)
4556 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4559 if (!tmp_list || !tmp_list->prev)
4560 return (PsppSheetViewColumn *)0x1;
4562 tmp_list = tmp_list->prev;
4563 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4564 tmp_list = tmp_list->prev;
4568 g_assert (tmp_list);
4570 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4572 if (left_column && !left_column->visible)
4574 /*if (!tmp_list->prev)
4575 return (PsppSheetViewColumn *)0x1;
4578 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4579 tmp_list = tmp_list->prev->prev;
4582 cur_column = left_column;
4584 tmp_list = tmp_list->prev;
4588 if (!tree_view->priv->column_drop_func)
4591 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4594 cur_column = left_column;
4595 tmp_list = tmp_list->prev;
4598 if (!tree_view->priv->column_drop_func)
4601 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4604 return (PsppSheetViewColumn *)0x1;
4608 /* same as DROP_HOME case, but doing it backwards */
4609 tmp_list = g_list_last (tree_view->priv->columns);
4612 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4613 return (PsppSheetViewColumn *)0x1;
4617 g_assert (tmp_list);
4619 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4621 if (left_column && !left_column->visible)
4623 cur_column = left_column;
4624 tmp_list = tmp_list->prev;
4627 if (!tree_view->priv->column_drop_func)
4630 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4633 cur_column = left_column;
4634 tmp_list = tmp_list->prev;
4637 if (!tree_view->priv->column_drop_func)
4640 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4643 return (PsppSheetViewColumn *)0x1;
4647 return (PsppSheetViewColumn *)0x1;
4651 pspp_sheet_view_key_press (GtkWidget *widget,
4654 PsppSheetView *tree_view = (PsppSheetView *) widget;
4656 if (tree_view->priv->rubber_band_status)
4658 if (event->keyval == GDK_Escape)
4659 pspp_sheet_view_stop_rubber_band (tree_view);
4664 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4666 if (event->keyval == GDK_Escape)
4668 tree_view->priv->cur_reorder = NULL;
4669 pspp_sheet_view_button_release_drag_column (widget, NULL);
4674 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4676 GList *focus_column;
4679 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4681 for (focus_column = tree_view->priv->columns;
4683 focus_column = focus_column->next)
4685 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4687 if (column->button && gtk_widget_has_focus (column->button))
4692 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4693 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4694 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4696 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4698 if (!column->resizable)
4700 gtk_widget_error_bell (widget);
4704 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4705 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4707 gint old_width = column->resized_width;
4709 column->resized_width = MAX (column->resized_width,
4711 column->resized_width -= 2;
4712 if (column->resized_width < 0)
4713 column->resized_width = 0;
4715 if (column->min_width == -1)
4716 column->resized_width = MAX (column->button_request,
4717 column->resized_width);
4719 column->resized_width = MAX (column->min_width,
4720 column->resized_width);
4722 if (column->max_width != -1)
4723 column->resized_width = MIN (column->resized_width,
4726 column->use_resized_width = TRUE;
4728 if (column->resized_width != old_width)
4729 gtk_widget_queue_resize (widget);
4731 gtk_widget_error_bell (widget);
4733 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4734 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4736 gint old_width = column->resized_width;
4738 column->resized_width = MAX (column->resized_width,
4740 column->resized_width += 2;
4742 if (column->max_width != -1)
4743 column->resized_width = MIN (column->resized_width,
4746 column->use_resized_width = TRUE;
4748 if (column->resized_width != old_width)
4749 gtk_widget_queue_resize (widget);
4751 gtk_widget_error_bell (widget);
4758 (event->state & GDK_MOD1_MASK) &&
4759 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4760 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4761 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4762 || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4764 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4766 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4767 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4769 PsppSheetViewColumn *col;
4770 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
4771 if (col != (PsppSheetViewColumn *)0x1)
4772 pspp_sheet_view_move_column_after (tree_view, column, col);
4774 gtk_widget_error_bell (widget);
4776 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4777 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4779 PsppSheetViewColumn *col;
4780 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4781 if (col != (PsppSheetViewColumn *)0x1)
4782 pspp_sheet_view_move_column_after (tree_view, column, col);
4784 gtk_widget_error_bell (widget);
4786 else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4788 PsppSheetViewColumn *col;
4789 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4790 if (col != (PsppSheetViewColumn *)0x1)
4791 pspp_sheet_view_move_column_after (tree_view, column, col);
4793 gtk_widget_error_bell (widget);
4795 else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4797 PsppSheetViewColumn *col;
4798 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4799 if (col != (PsppSheetViewColumn *)0x1)
4800 pspp_sheet_view_move_column_after (tree_view, column, col);
4802 gtk_widget_error_bell (widget);
4809 /* Chain up to the parent class. It handles the keybindings. */
4810 if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4813 if (tree_view->priv->search_entry_avoid_unhandled_binding)
4815 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4819 /* We pass the event to the search_entry. If its text changes, then we start
4820 * the typeahead find capabilities. */
4821 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4822 && tree_view->priv->enable_search
4823 && !tree_view->priv->search_custom_entry_set)
4825 GdkEvent *new_event;
4827 const char *new_text;
4830 gboolean text_modified;
4831 gulong popup_menu_id;
4833 pspp_sheet_view_ensure_interactive_directory (tree_view);
4835 /* Make a copy of the current text */
4836 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4837 new_event = gdk_event_copy ((GdkEvent *) event);
4838 g_object_unref (((GdkEventKey *) new_event)->window);
4839 ((GdkEventKey *) new_event)->window = g_object_ref (tree_view->priv->search_window->window);
4840 gtk_widget_realize (tree_view->priv->search_window);
4842 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
4843 "popup-menu", G_CALLBACK (gtk_true),
4846 /* Move the entry off screen */
4847 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4848 gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4849 gdk_screen_get_width (screen) + 1,
4850 gdk_screen_get_height (screen) + 1);
4851 gtk_widget_show (tree_view->priv->search_window);
4853 /* Send the event to the window. If the preedit_changed signal is emitted
4854 * during this event, we will set priv->imcontext_changed */
4855 tree_view->priv->imcontext_changed = FALSE;
4856 retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4857 gdk_event_free (new_event);
4858 gtk_widget_hide (tree_view->priv->search_window);
4860 g_signal_handler_disconnect (tree_view->priv->search_entry,
4863 /* We check to make sure that the entry tried to handle the text, and that
4864 * the text has changed.
4866 new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
4867 text_modified = strcmp (old_text, new_text) != 0;
4869 if (tree_view->priv->imcontext_changed || /* we're in a preedit */
4870 (retval && text_modified)) /* ...or the text was modified */
4872 if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
4874 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
4879 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
4889 pspp_sheet_view_key_release (GtkWidget *widget,
4892 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4894 if (tree_view->priv->rubber_band_status)
4897 return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
4900 /* FIXME Is this function necessary? Can I get an enter_notify event
4901 * w/o either an expose event or a mouse motion event?
4904 pspp_sheet_view_enter_notify (GtkWidget *widget,
4905 GdkEventCrossing *event)
4907 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4911 /* Sanity check it */
4912 if (event->window != tree_view->priv->bin_window)
4915 if (tree_view->priv->row_count == 0)
4918 if (event->mode == GDK_CROSSING_GRAB ||
4919 event->mode == GDK_CROSSING_GTK_GRAB ||
4920 event->mode == GDK_CROSSING_GTK_UNGRAB ||
4921 event->mode == GDK_CROSSING_STATE_CHANGED)
4924 /* find the node internally */
4925 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
4928 pspp_sheet_view_find_offset (tree_view, new_y, &node);
4930 tree_view->priv->event_last_x = event->x;
4931 tree_view->priv->event_last_y = event->y;
4933 prelight_or_select (tree_view, node, event->x, event->y);
4939 pspp_sheet_view_leave_notify (GtkWidget *widget,
4940 GdkEventCrossing *event)
4942 PsppSheetView *tree_view;
4944 if (event->mode == GDK_CROSSING_GRAB)
4947 tree_view = PSPP_SHEET_VIEW (widget);
4949 if (tree_view->priv->prelight_node >= 0)
4950 _pspp_sheet_view_queue_draw_node (tree_view,
4951 tree_view->priv->prelight_node,
4954 tree_view->priv->event_last_x = -10000;
4955 tree_view->priv->event_last_y = -10000;
4957 prelight_or_select (tree_view,
4959 -1000, -1000); /* coords not possibly over an arrow */
4966 pspp_sheet_view_focus_out (GtkWidget *widget,
4967 GdkEventFocus *event)
4969 PsppSheetView *tree_view;
4971 tree_view = PSPP_SHEET_VIEW (widget);
4973 gtk_widget_queue_draw (widget);
4975 /* destroy interactive search dialog */
4976 if (tree_view->priv->search_window)
4977 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
4983 /* Incremental Reflow
4987 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
4992 y = pspp_sheet_view_node_find_offset (tree_view, node)
4993 - tree_view->priv->vadjustment->value
4994 + TREE_VIEW_HEADER_HEIGHT (tree_view);
4996 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
4998 GTK_WIDGET (tree_view)->allocation.width,
4999 tree_view->priv->fixed_height);
5003 node_is_visible (PsppSheetView *tree_view,
5009 y = pspp_sheet_view_node_find_offset (tree_view, node);
5010 height = ROW_HEIGHT (tree_view);
5012 if (y >= tree_view->priv->vadjustment->value &&
5013 y + height <= (tree_view->priv->vadjustment->value
5014 + tree_view->priv->vadjustment->page_size))
5020 /* Returns the row height. */
5022 validate_row (PsppSheetView *tree_view,
5027 PsppSheetViewColumn *column;
5028 GList *list, *first_column, *last_column;
5030 gint horizontal_separator;
5031 gint vertical_separator;
5032 gint focus_line_width;
5033 gboolean draw_vgrid_lines, draw_hgrid_lines;
5035 gint grid_line_width;
5036 gboolean wide_separators;
5037 gint separator_height;
5039 gtk_widget_style_get (GTK_WIDGET (tree_view),
5040 "focus-padding", &focus_pad,
5041 "focus-line-width", &focus_line_width,
5042 "horizontal-separator", &horizontal_separator,
5043 "vertical-separator", &vertical_separator,
5044 "grid-line-width", &grid_line_width,
5045 "wide-separators", &wide_separators,
5046 "separator-height", &separator_height,
5050 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5051 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5053 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5054 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5056 for (last_column = g_list_last (tree_view->priv->columns);
5057 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5058 last_column = last_column->prev)
5061 for (first_column = g_list_first (tree_view->priv->columns);
5062 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5063 first_column = first_column->next)
5066 for (list = tree_view->priv->columns; list; list = list->next)
5071 column = list->data;
5073 if (! column->visible)
5076 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5077 pspp_sheet_view_column_cell_get_size (column,
5079 &tmp_width, &tmp_height);
5081 tmp_height += vertical_separator;
5082 height = MAX (height, tmp_height);
5084 tmp_width = tmp_width + horizontal_separator;
5086 if (draw_vgrid_lines)
5088 if (list->data == first_column || list->data == last_column)
5089 tmp_width += grid_line_width / 2.0;
5091 tmp_width += grid_line_width;
5094 if (tmp_width > column->requested_width)
5095 column->requested_width = tmp_width;
5098 if (draw_hgrid_lines)
5099 height += grid_line_width;
5101 tree_view->priv->post_validation_flag = TRUE;
5107 validate_visible_area (PsppSheetView *tree_view)
5109 GtkTreePath *path = NULL;
5110 GtkTreePath *above_path = NULL;
5113 gboolean size_changed = FALSE;
5115 gint area_above = 0;
5116 gint area_below = 0;
5118 if (tree_view->priv->row_count == 0)
5121 if (tree_view->priv->scroll_to_path == NULL)
5124 total_height = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5126 if (total_height == 0)
5129 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5132 /* we are going to scroll, and will update dy */
5133 _pspp_sheet_view_find_node (tree_view, path, &node);
5134 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5136 if (tree_view->priv->scroll_to_use_align)
5138 gint height = ROW_HEIGHT (tree_view);
5139 area_above = (total_height - height) *
5140 tree_view->priv->scroll_to_row_align;
5141 area_below = total_height - area_above - height;
5142 area_above = MAX (area_above, 0);
5143 area_below = MAX (area_below, 0);
5148 * 1) row not visible
5152 gint height = ROW_HEIGHT (tree_view);
5154 dy = pspp_sheet_view_node_find_offset (tree_view, node);
5156 if (dy >= tree_view->priv->vadjustment->value &&
5157 dy + height <= (tree_view->priv->vadjustment->value
5158 + tree_view->priv->vadjustment->page_size))
5160 /* row visible: keep the row at the same position */
5161 area_above = dy - tree_view->priv->vadjustment->value;
5162 area_below = (tree_view->priv->vadjustment->value +
5163 tree_view->priv->vadjustment->page_size)
5168 /* row not visible */
5170 && dy + height <= tree_view->priv->vadjustment->page_size)
5172 /* row at the beginning -- fixed */
5174 area_below = tree_view->priv->vadjustment->page_size
5175 - area_above - height;
5177 else if (dy >= (tree_view->priv->vadjustment->upper -
5178 tree_view->priv->vadjustment->page_size))
5180 /* row at the end -- fixed */
5181 area_above = dy - (tree_view->priv->vadjustment->upper -
5182 tree_view->priv->vadjustment->page_size);
5183 area_below = tree_view->priv->vadjustment->page_size -
5184 area_above - height;
5188 area_above = tree_view->priv->vadjustment->page_size - height;
5194 /* row somewhere in the middle, bring it to the top
5198 area_below = total_height - height;
5204 /* the scroll to isn't valid; ignore it.
5207 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5208 tree_view->priv->scroll_to_path = NULL;
5212 above_path = gtk_tree_path_copy (path);
5214 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5215 * backwards is much slower then forward, as there is no iter_prev function.
5216 * We go forwards first in case we run out of tree. Then we go backwards to
5219 while (node >= 0 && area_below > 0)
5221 gboolean done = FALSE;
5224 node = pspp_sheet_view_node_next (tree_view, node);
5227 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5229 gtk_tree_path_next (path);
5232 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5242 area_below -= ROW_HEIGHT (tree_view);
5244 gtk_tree_path_free (path);
5246 /* If we ran out of tree, and have extra area_below left, we need to add it
5249 area_above += area_below;
5251 _pspp_sheet_view_find_node (tree_view, above_path, &node);
5253 /* We walk backwards */
5254 while (area_above > 0)
5256 node = pspp_sheet_view_node_prev (tree_view, node);
5258 /* Always find the new path in the tree. We cannot just assume
5259 * a gtk_tree_path_prev() is enough here, as there might be children
5260 * in between this node and the previous sibling node. If this
5261 * appears to be a performance hotspot in profiles, we can look into
5262 * intrigate logic for keeping path, node and iter in sync like
5263 * we do for forward walks. (Which will be hard because of the lacking
5270 gtk_tree_path_free (above_path);
5271 above_path = _pspp_sheet_view_find_path (tree_view, node);
5273 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5275 area_above -= ROW_HEIGHT (tree_view);
5278 /* set the dy here to scroll to the path,
5279 * and sync the top row accordingly
5281 pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5282 pspp_sheet_view_top_row_to_dy (tree_view);
5284 /* update width/height and queue a resize */
5287 GtkRequisition requisition;
5289 /* We temporarily guess a size, under the assumption that it will be the
5290 * same when we get our next size_allocate. If we don't do this, we'll be
5291 * in an inconsistent state if we call top_row_to_dy. */
5293 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5294 tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width);
5295 tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height);
5296 gtk_adjustment_changed (tree_view->priv->hadjustment);
5297 gtk_adjustment_changed (tree_view->priv->vadjustment);
5298 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5301 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5302 tree_view->priv->scroll_to_path = NULL;
5305 gtk_tree_path_free (above_path);
5307 if (tree_view->priv->scroll_to_column)
5309 tree_view->priv->scroll_to_column = NULL;
5311 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5315 initialize_fixed_height_mode (PsppSheetView *tree_view)
5317 if (!tree_view->priv->row_count)
5320 if (tree_view->priv->fixed_height_set)
5323 if (tree_view->priv->fixed_height < 0)
5330 path = _pspp_sheet_view_find_path (tree_view, node);
5331 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5333 tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5335 gtk_tree_path_free (path);
5337 g_object_notify (G_OBJECT (tree_view), "fixed-height");
5341 /* Our strategy for finding nodes to validate is a little convoluted. We find
5342 * the left-most uninvalidated node. We then try walking right, validating
5343 * nodes. Once we find a valid node, we repeat the previous process of finding
5344 * the first invalid node.
5348 validate_rows_handler (PsppSheetView *tree_view)
5350 initialize_fixed_height_mode (tree_view);
5351 if (tree_view->priv->validate_rows_timer)
5353 g_source_remove (tree_view->priv->validate_rows_timer);
5354 tree_view->priv->validate_rows_timer = 0;
5361 do_presize_handler (PsppSheetView *tree_view)
5363 GtkRequisition requisition;
5365 validate_visible_area (tree_view);
5366 tree_view->priv->presize_handler_timer = 0;
5368 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5371 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5373 tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width);
5374 tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height);
5375 gtk_adjustment_changed (tree_view->priv->hadjustment);
5376 gtk_adjustment_changed (tree_view->priv->vadjustment);
5377 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5383 presize_handler_callback (gpointer data)
5385 do_presize_handler (PSPP_SHEET_VIEW (data));
5391 install_presize_handler (PsppSheetView *tree_view)
5393 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5396 if (! tree_view->priv->presize_handler_timer)
5398 tree_view->priv->presize_handler_timer =
5399 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5401 if (! tree_view->priv->validate_rows_timer)
5403 tree_view->priv->validate_rows_timer =
5404 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5409 scroll_sync_handler (PsppSheetView *tree_view)
5411 if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size)
5412 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5413 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5414 pspp_sheet_view_top_row_to_dy (tree_view);
5416 pspp_sheet_view_dy_to_top_row (tree_view);
5418 tree_view->priv->scroll_sync_timer = 0;
5424 install_scroll_sync_handler (PsppSheetView *tree_view)
5426 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5429 if (!tree_view->priv->scroll_sync_timer)
5431 tree_view->priv->scroll_sync_timer =
5432 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5437 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5441 gtk_tree_row_reference_free (tree_view->priv->top_row);
5445 tree_view->priv->top_row = NULL;
5446 tree_view->priv->top_row_dy = 0;
5450 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5451 tree_view->priv->top_row_dy = offset;
5455 /* Always call this iff dy is in the visible range. If the tree is empty, then
5456 * it's set to be NULL, and top_row_dy is 0;
5459 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5465 if (tree_view->priv->row_count == 0)
5467 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5471 offset = pspp_sheet_view_find_offset (tree_view,
5472 tree_view->priv->dy,
5477 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5481 path = _pspp_sheet_view_find_path (tree_view, node);
5482 pspp_sheet_view_set_top_row (tree_view, path, offset);
5483 gtk_tree_path_free (path);
5489 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5495 /* Avoid recursive calls */
5496 if (tree_view->priv->in_top_row_to_dy)
5499 if (tree_view->priv->top_row)
5500 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5507 _pspp_sheet_view_find_node (tree_view, path, &node);
5510 gtk_tree_path_free (path);
5514 /* keep dy and set new toprow */
5515 gtk_tree_row_reference_free (tree_view->priv->top_row);
5516 tree_view->priv->top_row = NULL;
5517 tree_view->priv->top_row_dy = 0;
5518 /* DO NOT install the idle handler */
5519 pspp_sheet_view_dy_to_top_row (tree_view);
5523 if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5525 /* new top row -- do NOT install the idle handler */
5526 pspp_sheet_view_dy_to_top_row (tree_view);
5530 new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5531 new_dy += tree_view->priv->top_row_dy;
5533 if (new_dy + tree_view->priv->vadjustment->page_size > tree_view->priv->height)
5534 new_dy = tree_view->priv->height - tree_view->priv->vadjustment->page_size;
5536 new_dy = MAX (0, new_dy);
5538 tree_view->priv->in_top_row_to_dy = TRUE;
5539 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5540 tree_view->priv->in_top_row_to_dy = FALSE;
5545 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5547 install_presize_handler (tree_view);
5553 set_source_row (GdkDragContext *context,
5554 GtkTreeModel *model,
5555 GtkTreePath *source_row)
5557 g_object_set_data_full (G_OBJECT (context),
5558 "gtk-tree-view-source-row",
5559 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5560 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5564 get_source_row (GdkDragContext *context)
5566 GtkTreeRowReference *ref =
5567 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5570 return gtk_tree_row_reference_get_path (ref);
5577 GtkTreeRowReference *dest_row;
5578 guint path_down_mode : 1;
5579 guint empty_view_drop : 1;
5580 guint drop_append_mode : 1;
5585 dest_row_free (gpointer data)
5587 DestRow *dr = (DestRow *)data;
5589 gtk_tree_row_reference_free (dr->dest_row);
5590 g_slice_free (DestRow, dr);
5594 set_dest_row (GdkDragContext *context,
5595 GtkTreeModel *model,
5596 GtkTreePath *dest_row,
5597 gboolean path_down_mode,
5598 gboolean empty_view_drop,
5599 gboolean drop_append_mode)
5605 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5610 dr = g_slice_new (DestRow);
5612 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5613 dr->path_down_mode = path_down_mode != FALSE;
5614 dr->empty_view_drop = empty_view_drop != FALSE;
5615 dr->drop_append_mode = drop_append_mode != FALSE;
5617 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5618 dr, (GDestroyNotify) dest_row_free);
5622 get_dest_row (GdkDragContext *context,
5623 gboolean *path_down_mode)
5626 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5630 GtkTreePath *path = NULL;
5633 *path_down_mode = dr->path_down_mode;
5636 path = gtk_tree_row_reference_get_path (dr->dest_row);
5637 else if (dr->empty_view_drop)
5638 path = gtk_tree_path_new_from_indices (0, -1);
5642 if (path && dr->drop_append_mode)
5643 gtk_tree_path_next (path);
5651 /* Get/set whether drag_motion requested the drag data and
5652 * drag_data_received should thus not actually insert the data,
5653 * since the data doesn't result from a drop.
5656 set_status_pending (GdkDragContext *context,
5657 GdkDragAction suggested_action)
5659 g_object_set_data (G_OBJECT (context),
5660 "gtk-tree-view-status-pending",
5661 GINT_TO_POINTER (suggested_action));
5664 static GdkDragAction
5665 get_status_pending (GdkDragContext *context)
5667 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5668 "gtk-tree-view-status-pending"));
5671 static TreeViewDragInfo*
5672 get_info (PsppSheetView *tree_view)
5674 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5678 destroy_info (TreeViewDragInfo *di)
5680 g_slice_free (TreeViewDragInfo, di);
5683 static TreeViewDragInfo*
5684 ensure_info (PsppSheetView *tree_view)
5686 TreeViewDragInfo *di;
5688 di = get_info (tree_view);
5692 di = g_slice_new0 (TreeViewDragInfo);
5694 g_object_set_data_full (G_OBJECT (tree_view),
5695 "gtk-tree-view-drag-info",
5697 (GDestroyNotify) destroy_info);
5704 remove_info (PsppSheetView *tree_view)
5706 g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5711 drag_scan_timeout (gpointer data)
5713 PsppSheetView *tree_view;
5715 GdkModifierType state;
5716 GtkTreePath *path = NULL;
5717 PsppSheetViewColumn *column = NULL;
5718 GdkRectangle visible_rect;
5720 GDK_THREADS_ENTER ();
5722 tree_view = PSPP_SHEET_VIEW (data);
5724 gdk_window_get_pointer (tree_view->priv->bin_window,
5727 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5729 /* See if we are near the edge. */
5730 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5731 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5732 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5733 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5735 pspp_sheet_view_get_path_at_pos (tree_view,
5736 tree_view->priv->bin_window,
5745 pspp_sheet_view_scroll_to_cell (tree_view,
5751 gtk_tree_path_free (path);
5755 GDK_THREADS_LEAVE ();
5762 add_scroll_timeout (PsppSheetView *tree_view)
5764 if (tree_view->priv->scroll_timeout == 0)
5766 tree_view->priv->scroll_timeout =
5767 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5772 remove_scroll_timeout (PsppSheetView *tree_view)
5774 if (tree_view->priv->scroll_timeout != 0)
5776 g_source_remove (tree_view->priv->scroll_timeout);
5777 tree_view->priv->scroll_timeout = 0;
5782 check_model_dnd (GtkTreeModel *model,
5783 GType required_iface,
5784 const gchar *signal)
5786 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5788 g_warning ("You must override the default '%s' handler "
5789 "on PsppSheetView when using models that don't support "
5790 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5791 "is to connect to '%s' and call "
5792 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5793 "the default handler from running. Look at the source code "
5794 "for the default handler in gtktreeview.c to get an idea what "
5795 "your handler should do. (gtktreeview.c is in the GTK source "
5796 "code.) If you're using GTK from a language other than C, "
5797 "there may be a more natural way to override default handlers, e.g. via derivation.",
5798 signal, g_type_name (required_iface), signal);
5806 scroll_row_timeout (gpointer data)
5808 PsppSheetView *tree_view = data;
5810 pspp_sheet_view_horizontal_autoscroll (tree_view);
5811 pspp_sheet_view_vertical_autoscroll (tree_view);
5813 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5814 pspp_sheet_view_update_rubber_band (tree_view);
5819 /* Returns TRUE if event should not be propagated to parent widgets */
5821 set_destination_row (PsppSheetView *tree_view,
5822 GdkDragContext *context,
5823 /* coordinates relative to the widget */
5826 GdkDragAction *suggested_action,
5829 GtkTreePath *path = NULL;
5830 PsppSheetViewDropPosition pos;
5831 PsppSheetViewDropPosition old_pos;
5832 TreeViewDragInfo *di;
5834 GtkTreePath *old_dest_path = NULL;
5835 gboolean can_drop = FALSE;
5837 *suggested_action = 0;
5840 widget = GTK_WIDGET (tree_view);
5842 di = get_info (tree_view);
5844 if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5846 /* someone unset us as a drag dest, note that if
5847 * we return FALSE drag_leave isn't called
5850 pspp_sheet_view_set_drag_dest_row (tree_view,
5852 PSPP_SHEET_VIEW_DROP_BEFORE);
5854 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5856 return FALSE; /* no longer a drop site */
5859 *target = gtk_drag_dest_find_target (widget, context,
5860 gtk_drag_dest_get_target_list (widget));
5861 if (*target == GDK_NONE)
5866 if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
5872 GtkTreeModel *model;
5874 /* the row got dropped on empty space, let's setup a special case
5878 gtk_tree_path_free (path);
5880 model = pspp_sheet_view_get_model (tree_view);
5882 n_children = gtk_tree_model_iter_n_children (model, NULL);
5885 pos = PSPP_SHEET_VIEW_DROP_AFTER;
5886 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
5890 pos = PSPP_SHEET_VIEW_DROP_BEFORE;
5891 path = gtk_tree_path_new_from_indices (0, -1);
5901 /* If we left the current row's "open" zone, unset the timeout for
5904 pspp_sheet_view_get_drag_dest_row (tree_view,
5909 gtk_tree_path_free (old_dest_path);
5911 if (TRUE /* FIXME if the location droppable predicate */)
5919 GtkWidget *source_widget;
5921 *suggested_action = context->suggested_action;
5922 source_widget = gtk_drag_get_source_widget (context);
5924 if (source_widget == widget)
5926 /* Default to MOVE, unless the user has
5927 * pressed ctrl or shift to affect available actions
5929 if ((context->actions & GDK_ACTION_MOVE) != 0)
5930 *suggested_action = GDK_ACTION_MOVE;
5933 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5938 /* can't drop here */
5939 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5941 PSPP_SHEET_VIEW_DROP_BEFORE);
5945 gtk_tree_path_free (path);
5951 get_logical_dest_row (PsppSheetView *tree_view,
5952 gboolean *path_down_mode,
5953 gboolean *drop_append_mode)
5955 /* adjust path to point to the row the drop goes in front of */
5956 GtkTreePath *path = NULL;
5957 PsppSheetViewDropPosition pos;
5959 g_return_val_if_fail (path_down_mode != NULL, NULL);
5960 g_return_val_if_fail (drop_append_mode != NULL, NULL);
5962 *path_down_mode = FALSE;
5963 *drop_append_mode = 0;
5965 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
5970 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
5972 else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
5973 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
5974 *path_down_mode = TRUE;
5978 GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
5980 g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
5982 if (!gtk_tree_model_get_iter (model, &iter, path) ||
5983 !gtk_tree_model_iter_next (model, &iter))
5984 *drop_append_mode = 1;
5987 *drop_append_mode = 0;
5988 gtk_tree_path_next (path);
5996 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
5997 GdkEventMotion *event)
5999 GtkWidget *widget = GTK_WIDGET (tree_view);
6000 GdkDragContext *context;
6001 TreeViewDragInfo *di;
6002 GtkTreePath *path = NULL;
6004 gint cell_x, cell_y;
6005 GtkTreeModel *model;
6006 gboolean retval = FALSE;
6008 di = get_info (tree_view);
6010 if (di == NULL || !di->source_set)
6013 if (tree_view->priv->pressed_button < 0)
6016 if (!gtk_drag_check_threshold (widget,
6017 tree_view->priv->press_start_x,
6018 tree_view->priv->press_start_y,
6019 event->x, event->y))
6022 model = pspp_sheet_view_get_model (tree_view);
6027 button = tree_view->priv->pressed_button;
6028 tree_view->priv->pressed_button = -1;
6030 pspp_sheet_view_get_path_at_pos (tree_view,
6031 tree_view->priv->press_start_x,
6032 tree_view->priv->press_start_y,
6041 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6042 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6046 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6049 /* Now we can begin the drag */
6053 context = gtk_drag_begin (widget,
6054 gtk_drag_source_get_target_list (widget),
6059 set_source_row (context, model, path);
6063 gtk_tree_path_free (path);
6070 pspp_sheet_view_drag_begin (GtkWidget *widget,
6071 GdkDragContext *context)
6073 PsppSheetView *tree_view;
6074 GtkTreePath *path = NULL;
6075 gint cell_x, cell_y;
6077 TreeViewDragInfo *di;
6079 tree_view = PSPP_SHEET_VIEW (widget);
6081 /* if the user uses a custom DND source impl, we don't set the icon here */
6082 di = get_info (tree_view);
6084 if (di == NULL || !di->source_set)
6087 pspp_sheet_view_get_path_at_pos (tree_view,
6088 tree_view->priv->press_start_x,
6089 tree_view->priv->press_start_y,
6095 g_return_if_fail (path != NULL);
6097 row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6100 gtk_drag_set_icon_pixmap (context,
6101 gdk_drawable_get_colormap (row_pix),
6104 /* the + 1 is for the black border in the icon */
6105 tree_view->priv->press_start_x + 1,
6108 g_object_unref (row_pix);
6109 gtk_tree_path_free (path);
6113 pspp_sheet_view_drag_end (GtkWidget *widget,
6114 GdkDragContext *context)
6119 /* Default signal implementations for the drag signals */
6121 pspp_sheet_view_drag_data_get (GtkWidget *widget,
6122 GdkDragContext *context,
6123 GtkSelectionData *selection_data,
6127 PsppSheetView *tree_view;
6128 GtkTreeModel *model;
6129 TreeViewDragInfo *di;
6130 GtkTreePath *source_row;
6132 tree_view = PSPP_SHEET_VIEW (widget);
6134 model = pspp_sheet_view_get_model (tree_view);
6139 di = get_info (PSPP_SHEET_VIEW (widget));
6144 source_row = get_source_row (context);
6146 if (source_row == NULL)
6149 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6150 * any model; for DragSource models there are some other targets
6154 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6155 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6160 /* If drag_data_get does nothing, try providing row data. */
6161 if (selection_data->target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6163 gtk_tree_set_row_drag_data (selection_data,
6169 gtk_tree_path_free (source_row);
6174 pspp_sheet_view_drag_data_delete (GtkWidget *widget,
6175 GdkDragContext *context)
6177 TreeViewDragInfo *di;
6178 GtkTreeModel *model;
6179 PsppSheetView *tree_view;
6180 GtkTreePath *source_row;
6182 tree_view = PSPP_SHEET_VIEW (widget);
6183 model = pspp_sheet_view_get_model (tree_view);
6185 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6188 di = get_info (tree_view);
6193 source_row = get_source_row (context);
6195 if (source_row == NULL)
6198 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6201 gtk_tree_path_free (source_row);
6203 set_source_row (context, NULL, NULL);
6207 pspp_sheet_view_drag_leave (GtkWidget *widget,
6208 GdkDragContext *context,
6211 /* unset any highlight row */
6212 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6214 PSPP_SHEET_VIEW_DROP_BEFORE);
6216 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6221 pspp_sheet_view_drag_motion (GtkWidget *widget,
6222 GdkDragContext *context,
6223 /* coordinates relative to the widget */
6229 GtkTreePath *path = NULL;
6230 PsppSheetViewDropPosition pos;
6231 PsppSheetView *tree_view;
6232 GdkDragAction suggested_action = 0;
6235 tree_view = PSPP_SHEET_VIEW (widget);
6237 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6240 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6242 /* we only know this *after* set_desination_row */
6243 empty = tree_view->priv->empty_view_drop;
6245 if (path == NULL && !empty)
6247 /* Can't drop here. */
6248 gdk_drag_status (context, 0, time);
6252 if (tree_view->priv->open_dest_timeout == 0 &&
6253 (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6254 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6260 add_scroll_timeout (tree_view);
6263 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6265 /* Request data so we can use the source row when
6266 * determining whether to accept the drop
6268 set_status_pending (context, suggested_action);
6269 gtk_drag_get_data (widget, context, target, time);
6273 set_status_pending (context, 0);
6274 gdk_drag_status (context, suggested_action, time);
6279 gtk_tree_path_free (path);
6286 pspp_sheet_view_drag_drop (GtkWidget *widget,
6287 GdkDragContext *context,
6288 /* coordinates relative to the widget */
6293 PsppSheetView *tree_view;
6295 GdkDragAction suggested_action = 0;
6296 GdkAtom target = GDK_NONE;
6297 TreeViewDragInfo *di;
6298 GtkTreeModel *model;
6299 gboolean path_down_mode;
6300 gboolean drop_append_mode;
6302 tree_view = PSPP_SHEET_VIEW (widget);
6304 model = pspp_sheet_view_get_model (tree_view);
6306 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6308 di = get_info (tree_view);
6313 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6316 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6319 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6321 if (target != GDK_NONE && path != NULL)
6323 /* in case a motion had requested drag data, change things so we
6324 * treat drag data receives as a drop.
6326 set_status_pending (context, 0);
6327 set_dest_row (context, model, path,
6328 path_down_mode, tree_view->priv->empty_view_drop,
6333 gtk_tree_path_free (path);
6335 /* Unset this thing */
6336 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6338 PSPP_SHEET_VIEW_DROP_BEFORE);
6340 if (target != GDK_NONE)
6342 gtk_drag_get_data (widget, context, target, time);
6350 pspp_sheet_view_drag_data_received (GtkWidget *widget,
6351 GdkDragContext *context,
6352 /* coordinates relative to the widget */
6355 GtkSelectionData *selection_data,
6360 TreeViewDragInfo *di;
6361 gboolean accepted = FALSE;
6362 GtkTreeModel *model;
6363 PsppSheetView *tree_view;
6364 GtkTreePath *dest_row;
6365 GdkDragAction suggested_action;
6366 gboolean path_down_mode;
6367 gboolean drop_append_mode;
6369 tree_view = PSPP_SHEET_VIEW (widget);
6371 model = pspp_sheet_view_get_model (tree_view);
6373 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6376 di = get_info (tree_view);
6381 suggested_action = get_status_pending (context);
6383 if (suggested_action)
6385 /* We are getting this data due to a request in drag_motion,
6386 * rather than due to a request in drag_drop, so we are just
6387 * supposed to call drag_status, not actually paste in the
6390 path = get_logical_dest_row (tree_view, &path_down_mode,
6394 suggested_action = 0;
6395 else if (path_down_mode)
6396 gtk_tree_path_down (path);
6398 if (suggested_action)
6400 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6406 path_down_mode = FALSE;
6407 gtk_tree_path_up (path);
6409 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6412 suggested_action = 0;
6415 suggested_action = 0;
6419 gdk_drag_status (context, suggested_action, time);
6422 gtk_tree_path_free (path);
6424 /* If you can't drop, remove user drop indicator until the next motion */
6425 if (suggested_action == 0)
6426 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6428 PSPP_SHEET_VIEW_DROP_BEFORE);
6433 dest_row = get_dest_row (context, &path_down_mode);
6435 if (dest_row == NULL)
6438 if (selection_data->length >= 0)
6442 gtk_tree_path_down (dest_row);
6443 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6444 dest_row, selection_data))
6445 gtk_tree_path_up (dest_row);
6449 if (selection_data->length >= 0)
6451 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6457 gtk_drag_finish (context,
6459 (context->action == GDK_ACTION_MOVE),
6462 if (gtk_tree_path_get_depth (dest_row) == 1
6463 && gtk_tree_path_get_indices (dest_row)[0] == 0)
6465 /* special special case drag to "0", scroll to first item */
6466 if (!tree_view->priv->scroll_to_path)
6467 pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6470 gtk_tree_path_free (dest_row);
6473 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6478 /* GtkContainer Methods
6483 pspp_sheet_view_remove (GtkContainer *container,
6486 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6487 PsppSheetViewChild *child = NULL;
6490 tmp_list = tree_view->priv->children;
6493 child = tmp_list->data;
6494 if (child->widget == widget)
6496 gtk_widget_unparent (widget);
6498 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6499 g_list_free_1 (tmp_list);
6500 g_slice_free (PsppSheetViewChild, child);
6504 tmp_list = tmp_list->next;
6507 tmp_list = tree_view->priv->columns;
6511 PsppSheetViewColumn *column;
6512 column = tmp_list->data;
6513 if (column->button == widget)
6515 gtk_widget_unparent (widget);
6518 tmp_list = tmp_list->next;
6523 pspp_sheet_view_forall (GtkContainer *container,
6524 gboolean include_internals,
6525 GtkCallback callback,
6526 gpointer callback_data)
6528 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6529 PsppSheetViewChild *child = NULL;
6530 PsppSheetViewColumn *column;
6533 tmp_list = tree_view->priv->children;
6536 child = tmp_list->data;
6537 tmp_list = tmp_list->next;
6539 (* callback) (child->widget, callback_data);
6541 if (include_internals == FALSE)
6544 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6546 column = tmp_list->data;
6549 (* callback) (column->button, callback_data);
6553 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6554 * cells. If so we draw one big row-spanning focus rectangle.
6557 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6561 if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6562 return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6564 for (list = tree_view->priv->columns; list; list = list->next)
6566 if (!((PsppSheetViewColumn *)list->data)->visible)
6568 if (_pspp_sheet_view_column_count_special_cells (list->data))
6576 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6577 PsppSheetViewColumn *focus_column,
6578 gboolean clamp_column_visible)
6580 g_return_if_fail (focus_column != NULL);
6582 tree_view->priv->focus_column = focus_column;
6583 if (!focus_column->button)
6585 pspp_sheet_view_column_set_need_button (focus_column, TRUE);
6586 g_return_if_fail (focus_column->button != NULL);
6589 if (GTK_CONTAINER (tree_view)->focus_child != focus_column->button)
6590 gtk_widget_grab_focus (focus_column->button);
6592 if (clamp_column_visible)
6593 pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6596 /* Returns TRUE if the focus is within the headers, after the focus operation is
6600 pspp_sheet_view_header_focus (PsppSheetView *tree_view,
6601 GtkDirectionType dir,
6602 gboolean clamp_column_visible)
6604 GtkWidget *focus_child;
6605 PsppSheetViewColumn *focus_column;
6606 GList *last_column, *first_column;
6610 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6613 focus_child = GTK_CONTAINER (tree_view)->focus_child;
6615 first_column = tree_view->priv->columns;
6616 while (first_column)
6618 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6620 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6622 first_column = first_column->next;
6625 /* No headers are visible, or are focusable. We can't focus in or out.
6627 if (first_column == NULL)
6630 last_column = g_list_last (tree_view->priv->columns);
6633 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6635 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6637 last_column = last_column->prev;
6641 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6645 case GTK_DIR_TAB_BACKWARD:
6646 case GTK_DIR_TAB_FORWARD:
6649 if (focus_child == NULL)
6651 if (tree_view->priv->focus_column != NULL &&
6652 pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6653 focus_column = tree_view->priv->focus_column;
6655 focus_column = first_column->data;
6656 pspp_sheet_view_focus_column (tree_view, focus_column,
6657 clamp_column_visible);
6664 if (focus_child == NULL)
6666 if (tree_view->priv->focus_column != NULL)
6667 focus_column = tree_view->priv->focus_column;
6668 else if (dir == GTK_DIR_LEFT)
6669 focus_column = last_column->data;
6671 focus_column = first_column->data;
6672 pspp_sheet_view_focus_column (tree_view, focus_column,
6673 clamp_column_visible);
6677 if (gtk_widget_child_focus (focus_child, dir))
6679 /* The focus moves inside the button. */
6680 /* This is probably a great example of bad UI */
6681 if (clamp_column_visible)
6682 pspp_sheet_view_clamp_column_visible (tree_view,
6683 tree_view->priv->focus_column,
6688 /* We need to move the focus among the row of buttons. */
6689 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6690 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6693 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6694 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6696 gtk_widget_error_bell (GTK_WIDGET (tree_view));
6702 PsppSheetViewColumn *column;
6704 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6705 tmp_list = tmp_list->next;
6707 tmp_list = tmp_list->prev;
6709 if (tmp_list == NULL)
6711 g_warning ("Internal button not found");
6714 column = tmp_list->data;
6715 if (column->button &&
6717 pspp_sheet_view_column_can_focus (column))
6719 pspp_sheet_view_focus_column (tree_view, column,
6720 clamp_column_visible);
6727 g_assert_not_reached ();
6734 /* This function returns in 'path' the first focusable path, if the given path
6735 * is already focusable, it's the returned one.
6739 search_first_focusable_path (PsppSheetView *tree_view,
6741 gboolean search_forward,
6744 /* XXX this function is trivial given that the sheetview doesn't support
6748 if (!path || !*path)
6751 _pspp_sheet_view_find_node (tree_view, *path, &node);
6759 return (*path != NULL);
6763 pspp_sheet_view_focus (GtkWidget *widget,
6764 GtkDirectionType direction)
6766 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6767 GtkContainer *container = GTK_CONTAINER (widget);
6768 GtkWidget *focus_child;
6770 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6773 focus_child = container->focus_child;
6775 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6776 /* Case 1. Headers currently have focus. */
6783 pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6785 case GTK_DIR_TAB_BACKWARD:
6788 case GTK_DIR_TAB_FORWARD:
6790 gtk_widget_grab_focus (widget);
6793 g_assert_not_reached ();
6798 /* Case 2. We don't have focus at all. */
6799 if (!gtk_widget_has_focus (widget))
6801 if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6802 gtk_widget_grab_focus (widget);
6806 /* Case 3. We have focus already. */
6807 if (direction == GTK_DIR_TAB_BACKWARD)
6808 return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6809 else if (direction == GTK_DIR_TAB_FORWARD)
6812 /* Other directions caught by the keybindings */
6813 gtk_widget_grab_focus (widget);
6818 pspp_sheet_view_grab_focus (GtkWidget *widget)
6820 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6822 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6826 pspp_sheet_view_style_set (GtkWidget *widget,
6827 GtkStyle *previous_style)
6829 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6831 PsppSheetViewColumn *column;
6833 if (gtk_widget_get_realized (widget))
6836 PsppSheetViewPrivate *priv = PSPP_SHEET_VIEW (widget)->priv;
6838 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
6839 gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
6840 gtk_style_set_background (widget->style, tree_view->priv->header_window, GTK_STATE_NORMAL);
6841 for (i = 0; i < 5 ; ++i)
6843 g_object_unref (priv->grid_line_gc[i]);
6844 priv->grid_line_gc[i] = gdk_gc_new (widget->window);
6845 gdk_gc_copy (priv->grid_line_gc[i], widget->style->text_aa_gc[i]);
6848 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6851 gtk_widget_style_get (widget,
6852 "expander-size", &tree_view->priv->expander_size,
6854 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6856 for (list = tree_view->priv->columns; list; list = list->next)
6858 column = list->data;
6859 _pspp_sheet_view_column_cell_set_dirty (column);
6862 tree_view->priv->fixed_height = -1;
6864 /* Invalidate cached button style. */
6865 if (tree_view->priv->button_style)
6867 g_object_unref (tree_view->priv->button_style);
6868 tree_view->priv->button_style = NULL;
6871 gtk_widget_queue_resize (widget);
6876 pspp_sheet_view_set_focus_child (GtkContainer *container,
6879 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6882 for (list = tree_view->priv->columns; list; list = list->next)
6884 if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
6886 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
6891 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
6895 pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
6896 GtkAdjustment *hadj,
6897 GtkAdjustment *vadj)
6899 gboolean need_adjust = FALSE;
6901 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
6904 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
6906 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6908 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
6910 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6912 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
6914 g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
6915 pspp_sheet_view_adjustment_changed,
6917 g_object_unref (tree_view->priv->hadjustment);
6920 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
6922 g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
6923 pspp_sheet_view_adjustment_changed,
6925 g_object_unref (tree_view->priv->vadjustment);
6928 if (tree_view->priv->hadjustment != hadj)
6930 tree_view->priv->hadjustment = hadj;
6931 g_object_ref_sink (tree_view->priv->hadjustment);
6933 g_signal_connect (tree_view->priv->hadjustment, "value-changed",
6934 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6939 if (tree_view->priv->vadjustment != vadj)
6941 tree_view->priv->vadjustment = vadj;
6942 g_object_ref_sink (tree_view->priv->vadjustment);
6944 g_signal_connect (tree_view->priv->vadjustment, "value-changed",
6945 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6951 pspp_sheet_view_adjustment_changed (NULL, tree_view);
6956 pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
6957 GtkMovementStep step,
6960 GdkModifierType state;
6962 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
6963 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
6964 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
6965 step == GTK_MOVEMENT_DISPLAY_LINES ||
6966 step == GTK_MOVEMENT_PAGES ||
6967 step == GTK_MOVEMENT_BUFFER_ENDS, FALSE);
6969 if (tree_view->priv->row_count == 0)
6971 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
6974 pspp_sheet_view_stop_editing (tree_view, FALSE);
6975 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
6976 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
6978 if (gtk_get_current_event_state (&state))
6980 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
6981 tree_view->priv->ctrl_pressed = TRUE;
6982 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
6983 tree_view->priv->shift_pressed = TRUE;
6985 /* else we assume not pressed */
6989 case GTK_MOVEMENT_LOGICAL_POSITIONS:
6990 pspp_sheet_view_move_cursor_tab (tree_view, count);
6992 case GTK_MOVEMENT_VISUAL_POSITIONS:
6993 pspp_sheet_view_move_cursor_left_right (tree_view, count);
6995 case GTK_MOVEMENT_DISPLAY_LINES:
6996 pspp_sheet_view_move_cursor_up_down (tree_view, count);
6998 case GTK_MOVEMENT_PAGES:
6999 pspp_sheet_view_move_cursor_page_up_down (tree_view, count);
7001 case GTK_MOVEMENT_BUFFER_ENDS:
7002 pspp_sheet_view_move_cursor_start_end (tree_view, count);
7005 g_assert_not_reached ();
7008 tree_view->priv->ctrl_pressed = FALSE;
7009 tree_view->priv->shift_pressed = FALSE;
7015 pspp_sheet_view_put (PsppSheetView *tree_view,
7016 GtkWidget *child_widget,
7017 /* in bin_window coordinates */
7023 PsppSheetViewChild *child;
7025 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7026 g_return_if_fail (GTK_IS_WIDGET (child_widget));
7028 child = g_slice_new (PsppSheetViewChild);
7030 child->widget = child_widget;
7033 child->width = width;
7034 child->height = height;
7036 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7038 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7039 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7041 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7045 _pspp_sheet_view_child_move_resize (PsppSheetView *tree_view,
7047 /* in tree coordinates */
7053 PsppSheetViewChild *child = NULL;
7055 GdkRectangle allocation;
7057 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7058 g_return_if_fail (GTK_IS_WIDGET (widget));
7060 for (list = tree_view->priv->children; list; list = list->next)
7062 if (((PsppSheetViewChild *)list->data)->widget == widget)
7071 allocation.x = child->x = x;
7072 allocation.y = child->y = y;
7073 allocation.width = child->width = width;
7074 allocation.height = child->height = height;
7076 if (gtk_widget_get_realized (widget))
7077 gtk_widget_size_allocate (widget, &allocation);
7081 /* TreeModel Callbacks
7085 pspp_sheet_view_row_changed (GtkTreeModel *model,
7090 PsppSheetView *tree_view = (PsppSheetView *)data;
7092 gboolean free_path = FALSE;
7093 GtkTreePath *cursor_path;
7095 g_return_if_fail (path != NULL || iter != NULL);
7097 if (tree_view->priv->cursor != NULL)
7098 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7102 if (tree_view->priv->edited_column &&
7103 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7104 pspp_sheet_view_stop_editing (tree_view, TRUE);
7106 if (cursor_path != NULL)
7107 gtk_tree_path_free (cursor_path);
7111 path = gtk_tree_model_get_path (model, iter);
7114 else if (iter == NULL)
7115 gtk_tree_model_get_iter (model, iter, path);
7117 _pspp_sheet_view_find_node (tree_view,
7123 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7124 pspp_sheet_view_node_queue_redraw (tree_view, node);
7128 gtk_tree_path_free (path);
7132 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7137 PsppSheetView *tree_view = (PsppSheetView *) data;
7140 gint height = tree_view->priv->fixed_height;
7141 gboolean free_path = FALSE;
7142 gboolean node_visible = TRUE;
7144 g_return_if_fail (path != NULL || iter != NULL);
7148 path = gtk_tree_model_get_path (model, iter);
7151 else if (iter == NULL)
7152 gtk_tree_model_get_iter (model, iter, path);
7154 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7156 /* Update all row-references */
7157 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7158 indices = gtk_tree_path_get_indices (path);
7159 tmpnode = indices[0];
7161 range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7165 if (node_visible && node_is_visible (tree_view, tmpnode))
7166 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7168 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7171 install_presize_handler (tree_view);
7173 gtk_tree_path_free (path);
7177 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7181 PsppSheetView *tree_view = (PsppSheetView *)data;
7184 g_return_if_fail (path != NULL);
7186 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7188 _pspp_sheet_view_find_node (tree_view, path, &node);
7193 range_tower_delete (tree_view->priv->selected, node, 1);
7195 /* Ensure we don't have a dangling pointer to a dead node */
7196 ensure_unprelighted (tree_view);
7198 /* Cancel editting if we've started */
7199 pspp_sheet_view_stop_editing (tree_view, TRUE);
7201 if (tree_view->priv->destroy_count_func)
7203 gint child_count = 0;
7204 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7207 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7209 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7211 gtk_tree_row_reference_free (tree_view->priv->top_row);
7212 tree_view->priv->top_row = NULL;
7215 install_scroll_sync_handler (tree_view);
7217 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7220 if (helper_data.changed)
7221 g_signal_emit_by_name (tree_view->priv->selection, "changed");
7226 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7227 GtkTreePath *parent,
7232 PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7235 /* XXX need to adjust selection */
7236 len = gtk_tree_model_iter_n_children (model, iter);
7241 gtk_tree_row_reference_reordered (G_OBJECT (data),
7246 if (gtk_tree_path_get_depth (parent) != 0)
7249 if (tree_view->priv->edited_column)
7250 pspp_sheet_view_stop_editing (tree_view, TRUE);
7252 /* we need to be unprelighted */
7253 ensure_unprelighted (tree_view);
7255 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7257 pspp_sheet_view_dy_to_top_row (tree_view);
7261 /* Internal tree functions
7266 pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view,
7267 PsppSheetViewColumn *column,
7271 PsppSheetViewColumn *tmp_column = NULL;
7282 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7285 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7287 list = (rtl ? list->prev : list->next))
7289 tmp_column = list->data;
7291 if (tmp_column == column)
7294 if (tmp_column->visible)
7295 total_width += tmp_column->width;
7298 if (tmp_column != column)
7300 g_warning (G_STRLOC": passed-in column isn't in the tree");
7309 if (column->visible)
7310 *x2 = total_width + column->width;
7312 *x2 = total_width; /* width of 0 */
7316 /* Make sure the node is visible vertically */
7318 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7321 gint node_dy, height;
7322 GtkTreePath *path = NULL;
7324 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7327 /* just return if the node is visible, avoiding a costly expose */
7328 node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7329 height = ROW_HEIGHT (tree_view);
7330 if (node_dy >= tree_view->priv->vadjustment->value
7331 && node_dy + height <= (tree_view->priv->vadjustment->value
7332 + tree_view->priv->vadjustment->page_size))
7335 path = _pspp_sheet_view_find_path (tree_view, node);
7338 /* We process updates because we want to clear old selected items when we scroll.
7339 * if this is removed, we get a "selection streak" at the bottom. */
7340 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7341 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7342 gtk_tree_path_free (path);
7347 pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
7348 PsppSheetViewColumn *column,
7349 gboolean focus_to_cell)
7356 x = column->allocation.x;
7357 width = column->allocation.width;
7359 if (width > tree_view->priv->hadjustment->page_size)
7361 /* The column is larger than the horizontal page size. If the
7362 * column has cells which can be focussed individually, then we make
7363 * sure the cell which gets focus is fully visible (if even the
7364 * focus cell is bigger than the page size, we make sure the
7365 * left-hand side of the cell is visible).
7367 * If the column does not have those so-called special cells, we
7368 * make sure the left-hand side of the column is visible.
7371 if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7373 GtkTreePath *cursor_path;
7374 GdkRectangle background_area, cell_area, focus_area;
7376 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7378 pspp_sheet_view_get_cell_area (tree_view,
7379 cursor_path, column, &cell_area);
7380 pspp_sheet_view_get_background_area (tree_view,
7381 cursor_path, column,
7384 gtk_tree_path_free (cursor_path);
7386 _pspp_sheet_view_column_get_focus_area (column,
7392 width = focus_area.width;
7394 if (width < tree_view->priv->hadjustment->page_size)
7396 if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) < (x + width))
7397 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7398 x + width - tree_view->priv->hadjustment->page_size);
7399 else if (tree_view->priv->hadjustment->value > x)
7400 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7404 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7406 tree_view->priv->hadjustment->lower,
7407 tree_view->priv->hadjustment->upper
7408 - tree_view->priv->hadjustment->page_size));
7412 if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) < (x + width))
7413 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7414 x + width - tree_view->priv->hadjustment->page_size);
7415 else if (tree_view->priv->hadjustment->value > x)
7416 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7421 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7426 path = gtk_tree_path_new ();
7428 gtk_tree_path_append_index (path, node);
7433 _pspp_sheet_view_find_node (PsppSheetView *tree_view,
7437 gint *indices = gtk_tree_path_get_indices (path);
7438 gint depth = gtk_tree_path_get_depth (path);
7441 if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7447 pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
7450 gboolean add_shifted_binding,
7451 GtkMovementStep step,
7455 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7460 if (add_shifted_binding)
7461 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7466 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7469 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7474 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7481 pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view,
7482 PsppSheetViewColumn *column)
7484 PsppSheetViewColumn *left_column;
7485 PsppSheetViewColumn *cur_column = NULL;
7486 PsppSheetViewColumnReorder *reorder;
7491 /* We want to precalculate the motion list such that we know what column slots
7495 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7497 /* First, identify all possible drop spots */
7499 tmp_list = g_list_last (tree_view->priv->columns);
7501 tmp_list = g_list_first (tree_view->priv->columns);
7505 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7506 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7508 if (cur_column->visible == FALSE)
7511 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7512 if (left_column != column && cur_column != column &&
7513 tree_view->priv->column_drop_func &&
7514 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7516 left_column = cur_column;
7519 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7520 reorder->left_column = left_column;
7521 left_column = reorder->right_column = cur_column;
7523 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7526 /* Add the last one */
7527 if (tree_view->priv->column_drop_func == NULL ||
7528 ((left_column != column) &&
7529 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7531 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7532 reorder->left_column = left_column;
7533 reorder->right_column = NULL;
7534 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7537 /* We quickly check to see if it even makes sense to reorder columns. */
7538 /* If there is nothing that can be moved, then we return */
7540 if (tree_view->priv->column_drag_info == NULL)
7543 /* We know there are always 2 slots possbile, as you can always return column. */
7544 /* If that's all there is, return */
7545 if (tree_view->priv->column_drag_info->next == NULL ||
7546 (tree_view->priv->column_drag_info->next->next == NULL &&
7547 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7548 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7550 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7551 g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7552 g_list_free (tree_view->priv->column_drag_info);
7553 tree_view->priv->column_drag_info = NULL;
7556 /* We fill in the ranges for the columns, now that we've isolated them */
7557 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7559 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7561 reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7563 reorder->left_align = left;
7564 if (tmp_list->next != NULL)
7566 g_assert (tmp_list->next->data);
7567 left = reorder->right_align = (reorder->right_column->allocation.x +
7568 reorder->right_column->allocation.width +
7569 ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7575 gdk_drawable_get_size (tree_view->priv->header_window, &width, NULL);
7576 reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7582 _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view,
7583 PsppSheetViewColumn *column)
7585 GdkEvent *send_event;
7586 GtkAllocation allocation;
7587 gint x, y, width, height;
7588 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7589 GdkDisplay *display = gdk_screen_get_display (screen);
7591 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7592 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7593 g_return_if_fail (column->button);
7595 pspp_sheet_view_set_column_drag_info (tree_view, column);
7597 if (tree_view->priv->column_drag_info == NULL)
7600 if (tree_view->priv->drag_window == NULL)
7602 GdkWindowAttr attributes;
7603 guint attributes_mask;
7605 attributes.window_type = GDK_WINDOW_CHILD;
7606 attributes.wclass = GDK_INPUT_OUTPUT;
7607 attributes.x = column->allocation.x;
7609 attributes.width = column->allocation.width;
7610 attributes.height = column->allocation.height;
7611 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7612 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
7613 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7614 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
7616 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7619 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7622 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7623 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7625 gtk_grab_remove (column->button);
7627 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7628 send_event->crossing.send_event = TRUE;
7629 send_event->crossing.window = g_object_ref (GTK_BUTTON (column->button)->event_window);
7630 send_event->crossing.subwindow = NULL;
7631 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7632 send_event->crossing.time = GDK_CURRENT_TIME;
7634 gtk_propagate_event (column->button, send_event);
7635 gdk_event_free (send_event);
7637 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7638 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7639 send_event->button.send_event = TRUE;
7640 send_event->button.time = GDK_CURRENT_TIME;
7641 send_event->button.x = -1;
7642 send_event->button.y = -1;
7643 send_event->button.axes = NULL;
7644 send_event->button.state = 0;
7645 send_event->button.button = 1;
7646 send_event->button.device = gdk_display_get_core_pointer (display);
7647 send_event->button.x_root = 0;
7648 send_event->button.y_root = 0;
7650 gtk_propagate_event (column->button, send_event);
7651 gdk_event_free (send_event);
7653 /* Kids, don't try this at home */
7654 g_object_ref (column->button);
7655 gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7656 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7657 gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7658 g_object_unref (column->button);
7660 tree_view->priv->drag_column_x = column->allocation.x;
7661 allocation = column->allocation;
7663 gtk_widget_size_allocate (column->button, &allocation);
7664 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7666 tree_view->priv->drag_column = column;
7667 gdk_window_show (tree_view->priv->drag_window);
7669 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7670 gdk_drawable_get_size (tree_view->priv->header_window, &width, &height);
7672 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7673 while (gtk_events_pending ())
7674 gtk_main_iteration ();
7676 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7677 gdk_pointer_grab (tree_view->priv->drag_window,
7679 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7680 NULL, NULL, GDK_CURRENT_TIME);
7681 gdk_keyboard_grab (tree_view->priv->drag_window,
7687 _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view,
7689 const GdkRectangle *clip_rect)
7693 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7697 rect.width = MAX (tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.width);
7699 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7700 rect.height = ROW_HEIGHT (tree_view);
7704 GdkRectangle new_rect;
7706 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7708 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7712 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7717 pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
7719 const GdkRectangle *clip_rect)
7723 _pspp_sheet_view_find_node (tree_view, path, &node);
7726 _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7730 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7733 GtkTreePath *cursor_path;
7735 if ((tree_view->priv->row_count == 0) ||
7736 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7740 if (tree_view->priv->cursor)
7741 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7743 if (cursor_path == NULL)
7745 /* There's no cursor. Move the cursor to the first selected row, if any
7746 * are selected, otherwise to the first row in the sheetview.
7748 GList *selected_rows;
7749 GtkTreeModel *model;
7750 PsppSheetSelection *selection;
7752 selection = pspp_sheet_view_get_selection (tree_view);
7753 selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7757 /* XXX we could avoid doing O(n) work to get this result */
7758 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7759 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7760 g_list_free (selected_rows);
7764 cursor_path = gtk_tree_path_new_first ();
7765 search_first_focusable_path (tree_view, &cursor_path,
7769 gtk_tree_row_reference_free (tree_view->priv->cursor);
7770 tree_view->priv->cursor = NULL;
7774 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7775 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7776 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE);
7778 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
7784 /* Now find a column for the cursor. */
7785 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7787 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7788 gtk_tree_path_free (cursor_path);
7790 if (tree_view->priv->focus_column == NULL)
7793 for (list = tree_view->priv->columns; list; list = list->next)
7795 if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7797 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7798 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7799 pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7809 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7812 gint selection_count;
7813 int cursor_node = -1;
7814 int new_cursor_node = -1;
7815 GtkTreePath *cursor_path = NULL;
7816 gboolean grab_focus = TRUE;
7818 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7822 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7823 /* FIXME: we lost the cursor; should we get the first? */
7826 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7827 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7829 if (cursor_node < 0)
7830 /* FIXME: we lost the cursor; should we get the first? */
7833 selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7835 if (selection_count == 0
7836 && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7837 && !tree_view->priv->ctrl_pressed)
7839 /* Don't move the cursor, but just select the current node */
7840 new_cursor_node = cursor_node;
7845 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7847 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7850 gtk_tree_path_free (cursor_path);
7852 if (new_cursor_node)
7854 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7856 search_first_focusable_path (tree_view, &cursor_path,
7861 gtk_tree_path_free (cursor_path);
7865 * If the list has only one item and multi-selection is set then select
7866 * the row (if not yet selected).
7868 if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7869 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7870 new_cursor_node < 0)
7873 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7875 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7877 if (new_cursor_node < 0
7878 && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
7880 new_cursor_node = cursor_node;
7884 new_cursor_node = -1;
7888 if (new_cursor_node >= 0)
7890 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7891 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE);
7892 gtk_tree_path_free (cursor_path);
7896 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7898 if (!tree_view->priv->shift_pressed)
7900 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
7902 GTK_DIR_UP : GTK_DIR_DOWN))
7904 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
7907 gtk_widget_child_focus (toplevel,
7909 GTK_DIR_TAB_BACKWARD :
7910 GTK_DIR_TAB_FORWARD);
7917 gtk_widget_error_bell (GTK_WIDGET (tree_view));
7922 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7924 return new_cursor_node >= 0;
7928 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
7931 int cursor_node = -1;
7932 GtkTreePath *old_cursor_path = NULL;
7933 GtkTreePath *cursor_path = NULL;
7934 int start_cursor_node = -1;
7937 gint vertical_separator;
7939 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7942 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
7943 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7945 /* This is sorta weird. Focus in should give us a cursor */
7948 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
7949 _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
7951 if (cursor_node < 0)
7953 /* FIXME: we lost the cursor. Should we try to get one? */
7954 gtk_tree_path_free (old_cursor_path);
7958 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7959 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
7960 y += tree_view->priv->cursor_offset;
7961 y += count * (int)tree_view->priv->vadjustment->page_increment;
7962 y = CLAMP (y, (gint)tree_view->priv->vadjustment->lower, (gint)tree_view->priv->vadjustment->upper - vertical_separator);
7964 if (y >= tree_view->priv->height)
7965 y = tree_view->priv->height - 1;
7967 tree_view->priv->cursor_offset =
7968 pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
7970 if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
7972 cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7973 tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
7976 y -= tree_view->priv->cursor_offset;
7977 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7979 start_cursor_node = cursor_node;
7981 if (! search_first_focusable_path (tree_view, &cursor_path,
7985 /* It looks like we reached the end of the view without finding
7986 * a focusable row. We will step backwards to find the last
7989 cursor_node = start_cursor_node;
7990 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7992 search_first_focusable_path (tree_view, &cursor_path,
8001 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8003 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
8006 pspp_sheet_view_scroll_to_point (tree_view, -1, y);
8007 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8008 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8010 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
8011 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8013 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8016 gtk_tree_path_free (old_cursor_path);
8017 gtk_tree_path_free (cursor_path);
8021 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8024 int cursor_node = -1;
8025 GtkTreePath *cursor_path = NULL;
8026 PsppSheetViewColumn *column;
8029 gboolean found_column = FALSE;
8032 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8034 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8037 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8038 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8042 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8043 if (cursor_node < 0)
8045 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8047 gtk_tree_path_free (cursor_path);
8050 gtk_tree_path_free (cursor_path);
8052 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8053 if (tree_view->priv->focus_column)
8055 for (; list; list = (rtl ? list->prev : list->next))
8057 if (list->data == tree_view->priv->focus_column)
8064 gboolean left, right;
8066 column = list->data;
8067 if (column->visible == FALSE || column->row_head)
8070 pspp_sheet_view_column_cell_set_cell_data (column,
8071 tree_view->priv->model,
8076 right = list->prev ? TRUE : FALSE;
8077 left = list->next ? TRUE : FALSE;
8081 left = list->prev ? TRUE : FALSE;
8082 right = list->next ? TRUE : FALSE;
8085 if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8087 tree_view->priv->focus_column = column;
8088 found_column = TRUE;
8093 list = rtl ? list->prev : list->next;
8095 list = rtl ? list->next : list->prev;
8100 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8101 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8102 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8106 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8109 pspp_sheet_view_clamp_column_visible (tree_view,
8110 tree_view->priv->focus_column, TRUE);
8114 try_move_cursor_tab (PsppSheetView *tree_view,
8115 gboolean start_at_focus_column,
8118 PsppSheetViewColumn *column;
8120 int cursor_node = -1;
8121 GtkTreePath *cursor_path = NULL;
8125 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8126 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8130 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8131 if (cursor_node < 0)
8133 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8135 gtk_tree_path_free (cursor_path);
8138 gtk_tree_path_free (cursor_path);
8140 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8141 if (start_at_focus_column)
8144 ? g_list_last (tree_view->priv->columns)
8145 : g_list_first (tree_view->priv->columns));
8146 if (tree_view->priv->focus_column)
8148 for (; list; list = (rtl ? list->prev : list->next))
8150 if (list->data == tree_view->priv->focus_column)
8157 list = (rtl ^ (count == 1)
8158 ? g_list_first (tree_view->priv->columns)
8159 : g_list_last (tree_view->priv->columns));
8164 gboolean left, right;
8166 column = list->data;
8167 if (column->visible == FALSE || !column->tabbable)
8170 pspp_sheet_view_column_cell_set_cell_data (column,
8171 tree_view->priv->model,
8176 right = list->prev ? TRUE : FALSE;
8177 left = list->next ? TRUE : FALSE;
8181 left = list->prev ? TRUE : FALSE;
8182 right = list->next ? TRUE : FALSE;
8185 if (column->tabbable
8186 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8188 tree_view->priv->focus_column = column;
8189 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8190 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8191 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8196 list = rtl ? list->prev : list->next;
8198 list = rtl ? list->next : list->prev;
8205 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8208 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8211 if (!try_move_cursor_tab (tree_view, TRUE, count))
8213 /* Shift+Tab goes backward, but Shift isn't supposed to act as Shift does
8214 for other movement commands, that is, it shouldn't cause the selection
8215 to be extended, so we need to act as though it is off. */
8216 tree_view->priv->shift_pressed = FALSE;
8218 if (pspp_sheet_view_move_cursor_up_down (tree_view, count)
8219 && !try_move_cursor_tab (tree_view, FALSE, count))
8220 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8223 pspp_sheet_view_clamp_column_visible (tree_view,
8224 tree_view->priv->focus_column, TRUE);
8228 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8233 GtkTreePath *old_path;
8235 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8238 g_return_if_fail (tree_view->priv->row_count > 0);
8240 pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8244 /* Now go forward to find the first focusable row. */
8245 path = _pspp_sheet_view_find_path (tree_view, 0);
8246 search_first_focusable_path (tree_view, &path,
8247 TRUE, &cursor_node);
8251 /* Now go backwards to find last focusable row. */
8252 path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8253 search_first_focusable_path (tree_view, &path,
8254 FALSE, &cursor_node);
8260 if (gtk_tree_path_compare (old_path, path))
8262 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE);
8263 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8267 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8271 gtk_tree_path_free (old_path);
8272 gtk_tree_path_free (path);
8276 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8278 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8281 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8282 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8285 pspp_sheet_selection_select_all (tree_view->priv->selection);
8291 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8293 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8296 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8297 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8300 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8306 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8307 gboolean start_editing)
8310 int cursor_node = -1;
8311 GtkTreePath *cursor_path = NULL;
8312 GtkTreeSelectMode mode = 0;
8314 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8317 if (tree_view->priv->cursor)
8318 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8320 if (cursor_path == NULL)
8323 _pspp_sheet_view_find_node (tree_view, cursor_path,
8326 if (cursor_node < 0)
8328 gtk_tree_path_free (cursor_path);
8332 if (!tree_view->priv->shift_pressed && start_editing &&
8333 tree_view->priv->focus_column)
8335 if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8337 gtk_tree_path_free (cursor_path);
8342 if (tree_view->priv->ctrl_pressed)
8343 mode |= GTK_TREE_SELECT_MODE_TOGGLE;
8344 if (tree_view->priv->shift_pressed)
8345 mode |= GTK_TREE_SELECT_MODE_EXTEND;
8347 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8353 /* We bail out if the original (tree, node) don't exist anymore after
8354 * handling the selection-changed callback. We do return TRUE because
8355 * the key press has been handled at this point.
8357 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8359 if (cursor_node != new_node)
8362 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8364 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8365 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8367 if (!tree_view->priv->shift_pressed)
8368 pspp_sheet_view_row_activated (tree_view, cursor_path,
8369 tree_view->priv->focus_column);
8371 gtk_tree_path_free (cursor_path);
8377 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8380 int cursor_node = -1;
8381 GtkTreePath *cursor_path = NULL;
8383 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8387 if (tree_view->priv->cursor)
8388 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8390 if (cursor_path == NULL)
8393 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8394 if (cursor_node < 0)
8396 gtk_tree_path_free (cursor_path);
8400 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8403 GTK_TREE_SELECT_MODE_TOGGLE,
8406 /* We bail out if the original (tree, node) don't exist anymore after
8407 * handling the selection-changed callback. We do return TRUE because
8408 * the key press has been handled at this point.
8410 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8412 if (cursor_node != new_node)
8415 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8417 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8418 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8419 gtk_tree_path_free (cursor_path);
8425 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8427 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8428 tree_view->priv->typeselect_flush_timeout = 0;
8433 /* Cut and paste from gtkwindow.c */
8435 send_focus_change (GtkWidget *widget,
8438 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8440 g_object_ref (widget);
8443 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
8445 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
8447 fevent->focus_change.type = GDK_FOCUS_CHANGE;
8448 fevent->focus_change.window = g_object_ref (widget->window);
8449 fevent->focus_change.in = in;
8451 gtk_widget_event (widget, fevent);
8453 g_object_notify (G_OBJECT (widget), "has-focus");
8455 g_object_unref (widget);
8456 gdk_event_free (fevent);
8460 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8462 GtkWidget *frame, *vbox, *toplevel;
8465 if (tree_view->priv->search_custom_entry_set)
8468 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8469 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8471 if (tree_view->priv->search_window != NULL)
8473 if (GTK_WINDOW (toplevel)->group)
8474 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
8475 GTK_WINDOW (tree_view->priv->search_window));
8476 else if (GTK_WINDOW (tree_view->priv->search_window)->group)
8477 gtk_window_group_remove_window (GTK_WINDOW (tree_view->priv->search_window)->group,
8478 GTK_WINDOW (tree_view->priv->search_window));
8479 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8483 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8484 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8486 if (GTK_WINDOW (toplevel)->group)
8487 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
8488 GTK_WINDOW (tree_view->priv->search_window));
8490 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8491 GDK_WINDOW_TYPE_HINT_UTILITY);
8492 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8493 g_signal_connect (tree_view->priv->search_window, "delete-event",
8494 G_CALLBACK (pspp_sheet_view_search_delete_event),
8496 g_signal_connect (tree_view->priv->search_window, "key-press-event",
8497 G_CALLBACK (pspp_sheet_view_search_key_press_event),
8499 g_signal_connect (tree_view->priv->search_window, "button-press-event",
8500 G_CALLBACK (pspp_sheet_view_search_button_press_event),
8502 g_signal_connect (tree_view->priv->search_window, "scroll-event",
8503 G_CALLBACK (pspp_sheet_view_search_scroll_event),
8506 frame = gtk_frame_new (NULL);
8507 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8508 gtk_widget_show (frame);
8509 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8511 vbox = gtk_vbox_new (FALSE, 0);
8512 gtk_widget_show (vbox);
8513 gtk_container_add (GTK_CONTAINER (frame), vbox);
8514 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8517 tree_view->priv->search_entry = gtk_entry_new ();
8518 gtk_widget_show (tree_view->priv->search_entry);
8519 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8520 G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8522 g_signal_connect (tree_view->priv->search_entry,
8523 "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8525 g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8527 G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8529 gtk_container_add (GTK_CONTAINER (vbox),
8530 tree_view->priv->search_entry);
8532 gtk_widget_realize (tree_view->priv->search_entry);
8535 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8536 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8539 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8540 gboolean keybinding)
8542 /* We only start interactive search if we have focus or the columns
8543 * have focus. If one of our children have focus, we don't want to
8547 gboolean found_focus = FALSE;
8548 GtkWidgetClass *entry_parent_class;
8550 if (!tree_view->priv->enable_search && !keybinding)
8553 if (tree_view->priv->search_custom_entry_set)
8556 if (tree_view->priv->search_window != NULL &&
8557 gtk_widget_get_visible (tree_view->priv->search_window))
8560 for (list = tree_view->priv->columns; list; list = list->next)
8562 PsppSheetViewColumn *column;
8564 column = list->data;
8565 if (! column->visible)
8568 if (column->button && gtk_widget_has_focus (column->button))
8575 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8581 if (tree_view->priv->search_column < 0)
8584 pspp_sheet_view_ensure_interactive_directory (tree_view);
8587 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8590 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8591 gtk_widget_show (tree_view->priv->search_window);
8592 if (tree_view->priv->search_entry_changed_id == 0)
8594 tree_view->priv->search_entry_changed_id =
8595 g_signal_connect (tree_view->priv->search_entry, "changed",
8596 G_CALLBACK (pspp_sheet_view_search_init),
8600 tree_view->priv->typeselect_flush_timeout =
8601 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8602 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8605 /* Grab focus will select all the text. We don't want that to happen, so we
8606 * call the parent instance and bypass the selection change. This is probably
8607 * really non-kosher. */
8608 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8609 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8611 /* send focus-in event */
8612 send_focus_change (tree_view->priv->search_entry, TRUE);
8614 /* search first matching iter */
8615 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8621 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8623 return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8626 /* this function returns the new width of the column being resized given
8627 * the column and x position of the cursor; the x cursor position is passed
8628 * in as a pointer and automagicly corrected if it's beyond min/max limits
8631 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8635 PsppSheetViewColumn *column;
8639 /* first translate the x position from widget->window
8640 * to clist->clist_window
8642 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8643 column = g_list_nth (tree_view->priv->columns, i)->data;
8644 width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8646 /* Clamp down the value */
8647 if (column->min_width == -1)
8648 width = MAX (column->button_request, width);
8650 width = MAX (column->min_width, width);
8651 if (column->max_width != -1)
8652 width = MIN (width, column->max_width);
8654 *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8660 /* FIXME this adjust_allocation is a big cut-and-paste from
8661 * GtkCList, needs to be some "official" way to do this
8671 /* The window to which widget->window is relative */
8672 #define ALLOCATION_WINDOW(widget) \
8673 (!gtk_widget_get_has_window (widget) ? \
8674 (widget)->window : \
8675 gdk_window_get_parent ((widget)->window))
8678 adjust_allocation_recurse (GtkWidget *widget,
8681 ScrollData *scroll_data = data;
8683 /* Need to really size allocate instead of just poking
8684 * into widget->allocation if the widget is not realized.
8685 * FIXME someone figure out why this was.
8687 if (!gtk_widget_get_realized (widget))
8689 if (gtk_widget_get_visible (widget))
8691 GdkRectangle tmp_rectangle = widget->allocation;
8692 tmp_rectangle.x += scroll_data->dx;
8693 tmp_rectangle.y += scroll_data->dy;
8695 gtk_widget_size_allocate (widget, &tmp_rectangle);
8700 if (ALLOCATION_WINDOW (widget) == scroll_data->window)
8702 widget->allocation.x += scroll_data->dx;
8703 widget->allocation.y += scroll_data->dy;
8705 if (GTK_IS_CONTAINER (widget))
8706 gtk_container_forall (GTK_CONTAINER (widget),
8707 adjust_allocation_recurse,
8714 adjust_allocation (GtkWidget *widget,
8718 ScrollData scroll_data;
8720 if (gtk_widget_get_realized (widget))
8721 scroll_data.window = ALLOCATION_WINDOW (widget);
8723 scroll_data.window = NULL;
8725 scroll_data.dx = dx;
8726 scroll_data.dy = dy;
8728 adjust_allocation_recurse (widget, &scroll_data);
8732 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8736 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8737 PsppSheetView *tree_view)
8739 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8744 gdk_window_move (tree_view->priv->bin_window,
8745 - tree_view->priv->hadjustment->value,
8746 TREE_VIEW_HEADER_HEIGHT (tree_view));
8747 gdk_window_move (tree_view->priv->header_window,
8748 - tree_view->priv->hadjustment->value,
8750 dy = tree_view->priv->dy - (int) tree_view->priv->vadjustment->value;
8753 update_prelight (tree_view,
8754 tree_view->priv->event_last_x,
8755 tree_view->priv->event_last_y - dy);
8757 if (tree_view->priv->edited_column &&
8758 GTK_IS_WIDGET (tree_view->priv->edited_column->editable_widget))
8762 PsppSheetViewChild *child = NULL;
8764 widget = GTK_WIDGET (tree_view->priv->edited_column->editable_widget);
8765 adjust_allocation (widget, 0, dy);
8767 for (list = tree_view->priv->children; list; list = list->next)
8769 child = (PsppSheetViewChild *)list->data;
8770 if (child->widget == widget)
8778 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8780 if (tree_view->priv->dy != (int) tree_view->priv->vadjustment->value)
8782 /* update our dy and top_row */
8783 tree_view->priv->dy = (int) tree_view->priv->vadjustment->value;
8785 if (!tree_view->priv->in_top_row_to_dy)
8786 pspp_sheet_view_dy_to_top_row (tree_view);
8789 for (list = tree_view->priv->columns; list; list = list->next)
8791 PsppSheetViewColumn *column = list->data;
8792 GtkAllocation *allocation = &column->allocation;
8794 if (span_intersects (allocation->x, allocation->width,
8795 tree_view->priv->hadjustment->value,
8796 GTK_WIDGET (tree_view)->allocation.width))
8798 pspp_sheet_view_column_set_need_button (column, TRUE);
8799 if (!column->button)
8800 pspp_sheet_view_column_update_button (column);
8812 * pspp_sheet_view_new:
8814 * Creates a new #PsppSheetView widget.
8816 * Return value: A newly created #PsppSheetView widget.
8819 pspp_sheet_view_new (void)
8821 return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
8825 * pspp_sheet_view_new_with_model:
8826 * @model: the model.
8828 * Creates a new #PsppSheetView widget with the model initialized to @model.
8830 * Return value: A newly created #PsppSheetView widget.
8833 pspp_sheet_view_new_with_model (GtkTreeModel *model)
8835 return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
8842 * pspp_sheet_view_get_model:
8843 * @tree_view: a #PsppSheetView
8845 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
8848 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
8851 pspp_sheet_view_get_model (PsppSheetView *tree_view)
8853 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8855 return tree_view->priv->model;
8859 * pspp_sheet_view_set_model:
8860 * @tree_view: A #GtkTreeNode.
8861 * @model: (allow-none): The model.
8863 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
8864 * set, it will remove it before setting the new model. If @model is %NULL,
8865 * then it will unset the old model.
8868 pspp_sheet_view_set_model (PsppSheetView *tree_view,
8869 GtkTreeModel *model)
8871 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8872 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
8874 if (model == tree_view->priv->model)
8877 if (tree_view->priv->scroll_to_path)
8879 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8880 tree_view->priv->scroll_to_path = NULL;
8883 if (tree_view->priv->model)
8885 GList *tmplist = tree_view->priv->columns;
8887 if (tree_view->priv->selected)
8888 range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
8889 pspp_sheet_view_stop_editing (tree_view, TRUE);
8891 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8892 pspp_sheet_view_row_changed,
8894 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8895 pspp_sheet_view_row_inserted,
8897 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8898 pspp_sheet_view_row_deleted,
8900 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8901 pspp_sheet_view_rows_reordered,
8904 for (; tmplist; tmplist = tmplist->next)
8905 _pspp_sheet_view_column_unset_model (tmplist->data,
8906 tree_view->priv->model);
8908 tree_view->priv->prelight_node = -1;
8910 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
8911 tree_view->priv->drag_dest_row = NULL;
8912 gtk_tree_row_reference_free (tree_view->priv->cursor);
8913 tree_view->priv->cursor = NULL;
8914 gtk_tree_row_reference_free (tree_view->priv->anchor);
8915 tree_view->priv->anchor = NULL;
8916 gtk_tree_row_reference_free (tree_view->priv->top_row);
8917 tree_view->priv->top_row = NULL;
8918 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8919 tree_view->priv->scroll_to_path = NULL;
8921 tree_view->priv->scroll_to_column = NULL;
8923 g_object_unref (tree_view->priv->model);
8925 tree_view->priv->search_column = -1;
8926 tree_view->priv->fixed_height = -1;
8927 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
8928 tree_view->priv->last_button_x = -1;
8929 tree_view->priv->last_button_y = -1;
8932 tree_view->priv->model = model;
8934 if (tree_view->priv->model)
8938 if (tree_view->priv->search_column == -1)
8940 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
8942 GType type = gtk_tree_model_get_column_type (model, i);
8944 if (g_value_type_transformable (type, G_TYPE_STRING))
8946 tree_view->priv->search_column = i;
8952 g_object_ref (tree_view->priv->model);
8953 g_signal_connect (tree_view->priv->model,
8955 G_CALLBACK (pspp_sheet_view_row_changed),
8957 g_signal_connect (tree_view->priv->model,
8959 G_CALLBACK (pspp_sheet_view_row_inserted),
8961 g_signal_connect (tree_view->priv->model,
8963 G_CALLBACK (pspp_sheet_view_row_deleted),
8965 g_signal_connect (tree_view->priv->model,
8967 G_CALLBACK (pspp_sheet_view_rows_reordered),
8970 tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
8972 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
8973 install_presize_handler (tree_view);
8976 g_object_notify (G_OBJECT (tree_view), "model");
8978 if (tree_view->priv->selection)
8979 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
8981 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8982 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8986 * pspp_sheet_view_get_selection:
8987 * @tree_view: A #PsppSheetView.
8989 * Gets the #PsppSheetSelection associated with @tree_view.
8991 * Return value: A #PsppSheetSelection object.
8993 PsppSheetSelection *
8994 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
8996 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8998 return tree_view->priv->selection;
9002 * pspp_sheet_view_get_hadjustment:
9003 * @tree_view: A #PsppSheetView
9005 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
9007 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9011 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
9013 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9015 if (tree_view->priv->hadjustment == NULL)
9016 pspp_sheet_view_set_hadjustment (tree_view, NULL);
9018 return tree_view->priv->hadjustment;
9022 * pspp_sheet_view_set_hadjustment:
9023 * @tree_view: A #PsppSheetView
9024 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9026 * Sets the #GtkAdjustment for the current horizontal aspect.
9029 pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view,
9030 GtkAdjustment *adjustment)
9032 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9034 pspp_sheet_view_set_adjustments (tree_view,
9036 tree_view->priv->vadjustment);
9038 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9042 * pspp_sheet_view_get_vadjustment:
9043 * @tree_view: A #PsppSheetView
9045 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9047 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9051 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9053 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9055 if (tree_view->priv->vadjustment == NULL)
9056 pspp_sheet_view_set_vadjustment (tree_view, NULL);
9058 return tree_view->priv->vadjustment;
9062 * pspp_sheet_view_set_vadjustment:
9063 * @tree_view: A #PsppSheetView
9064 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9066 * Sets the #GtkAdjustment for the current vertical aspect.
9069 pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view,
9070 GtkAdjustment *adjustment)
9072 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9074 pspp_sheet_view_set_adjustments (tree_view,
9075 tree_view->priv->hadjustment,
9078 g_object_notify (G_OBJECT (tree_view), "vadjustment");
9081 /* Column and header operations */
9084 * pspp_sheet_view_get_headers_visible:
9085 * @tree_view: A #PsppSheetView.
9087 * Returns %TRUE if the headers on the @tree_view are visible.
9089 * Return value: Whether the headers are visible or not.
9092 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9094 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9096 return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9100 * pspp_sheet_view_set_headers_visible:
9101 * @tree_view: A #PsppSheetView.
9102 * @headers_visible: %TRUE if the headers are visible
9104 * Sets the visibility state of the headers.
9107 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9108 gboolean headers_visible)
9112 PsppSheetViewColumn *column;
9114 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9116 headers_visible = !! headers_visible;
9118 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9121 if (headers_visible)
9122 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9124 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9126 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9128 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9129 if (headers_visible)
9131 gdk_window_move_resize (tree_view->priv->bin_window, x, y + TREE_VIEW_HEADER_HEIGHT (tree_view), tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view));
9133 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9134 pspp_sheet_view_map_buttons (tree_view);
9138 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9140 for (list = tree_view->priv->columns; list; list = list->next)
9142 column = list->data;
9144 gtk_widget_unmap (column->button);
9146 gdk_window_hide (tree_view->priv->header_window);
9150 tree_view->priv->vadjustment->page_size = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
9151 tree_view->priv->vadjustment->page_increment = (GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2;
9152 tree_view->priv->vadjustment->lower = 0;
9153 tree_view->priv->vadjustment->upper = tree_view->priv->height;
9154 gtk_adjustment_changed (tree_view->priv->vadjustment);
9156 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9158 g_object_notify (G_OBJECT (tree_view), "headers-visible");
9162 * pspp_sheet_view_columns_autosize:
9163 * @tree_view: A #PsppSheetView.
9165 * Resizes all columns to their optimal width. Only works after the
9166 * treeview has been realized.
9169 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9171 gboolean dirty = FALSE;
9173 PsppSheetViewColumn *column;
9175 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9177 for (list = tree_view->priv->columns; list; list = list->next)
9179 column = list->data;
9180 _pspp_sheet_view_column_cell_set_dirty (column);
9185 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9189 * pspp_sheet_view_set_headers_clickable:
9190 * @tree_view: A #PsppSheetView.
9191 * @setting: %TRUE if the columns are clickable.
9193 * Allow the column title buttons to be clicked.
9196 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9201 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9203 for (list = tree_view->priv->columns; list; list = list->next)
9204 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9206 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9211 * pspp_sheet_view_get_headers_clickable:
9212 * @tree_view: A #PsppSheetView.
9214 * Returns whether all header columns are clickable.
9216 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9221 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9225 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9227 for (list = tree_view->priv->columns; list; list = list->next)
9228 if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9235 * pspp_sheet_view_set_rules_hint
9236 * @tree_view: a #PsppSheetView
9237 * @setting: %TRUE if the tree requires reading across rows
9239 * This function tells GTK+ that the user interface for your
9240 * application requires users to read across tree rows and associate
9241 * cells with one another. By default, GTK+ will then render the tree
9242 * with alternating row colors. Do <emphasis>not</emphasis> use it
9243 * just because you prefer the appearance of the ruled tree; that's a
9244 * question for the theme. Some themes will draw tree rows in
9245 * alternating colors even when rules are turned off, and users who
9246 * prefer that appearance all the time can choose those themes. You
9247 * should call this function only as a <emphasis>semantic</emphasis>
9248 * hint to the theme engine that your tree makes alternating colors
9249 * useful from a functional standpoint (since it has lots of columns,
9254 pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view,
9257 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9259 setting = setting != FALSE;
9261 if (tree_view->priv->has_rules != setting)
9263 tree_view->priv->has_rules = setting;
9264 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9267 g_object_notify (G_OBJECT (tree_view), "rules-hint");
9271 * pspp_sheet_view_get_rules_hint
9272 * @tree_view: a #PsppSheetView
9274 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9276 * Return value: %TRUE if rules are useful for the user of this tree
9279 pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view)
9281 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9283 return tree_view->priv->has_rules;
9286 /* Public Column functions
9290 * pspp_sheet_view_append_column:
9291 * @tree_view: A #PsppSheetView.
9292 * @column: The #PsppSheetViewColumn to add.
9294 * Appends @column to the list of columns.
9296 * Return value: The number of columns in @tree_view after appending.
9299 pspp_sheet_view_append_column (PsppSheetView *tree_view,
9300 PsppSheetViewColumn *column)
9302 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9303 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9304 g_return_val_if_fail (column->tree_view == NULL, -1);
9306 return pspp_sheet_view_insert_column (tree_view, column, -1);
9311 * pspp_sheet_view_remove_column:
9312 * @tree_view: A #PsppSheetView.
9313 * @column: The #PsppSheetViewColumn to remove.
9315 * Removes @column from @tree_view.
9317 * Return value: The number of columns in @tree_view after removing.
9320 pspp_sheet_view_remove_column (PsppSheetView *tree_view,
9321 PsppSheetViewColumn *column)
9323 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9324 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9325 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9327 if (tree_view->priv->focus_column == column)
9328 tree_view->priv->focus_column = NULL;
9330 if (tree_view->priv->edited_column == column)
9332 pspp_sheet_view_stop_editing (tree_view, TRUE);
9334 /* no need to, but just to be sure ... */
9335 tree_view->priv->edited_column = NULL;
9338 _pspp_sheet_view_column_unset_tree_view (column);
9340 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9341 tree_view->priv->n_columns--;
9343 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9347 _pspp_sheet_view_column_unrealize_button (column);
9348 for (list = tree_view->priv->columns; list; list = list->next)
9350 PsppSheetViewColumn *tmp_column;
9352 tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9353 if (tmp_column->visible)
9354 _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9357 if (tree_view->priv->n_columns == 0 &&
9358 pspp_sheet_view_get_headers_visible (tree_view) &&
9359 tree_view->priv->header_window)
9360 gdk_window_hide (tree_view->priv->header_window);
9362 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9365 g_object_unref (column);
9366 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9368 return tree_view->priv->n_columns;
9372 * pspp_sheet_view_insert_column:
9373 * @tree_view: A #PsppSheetView.
9374 * @column: The #PsppSheetViewColumn to be inserted.
9375 * @position: The position to insert @column in.
9377 * This inserts the @column into the @tree_view at @position. If @position is
9378 * -1, then the column is inserted at the end.
9380 * Return value: The number of columns in @tree_view after insertion.
9383 pspp_sheet_view_insert_column (PsppSheetView *tree_view,
9384 PsppSheetViewColumn *column,
9387 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9388 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9389 g_return_val_if_fail (column->tree_view == NULL, -1);
9391 g_object_ref_sink (column);
9393 if (tree_view->priv->n_columns == 0 &&
9394 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9395 pspp_sheet_view_get_headers_visible (tree_view))
9397 gdk_window_show (tree_view->priv->header_window);
9400 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9402 tree_view->priv->n_columns++;
9404 _pspp_sheet_view_column_set_tree_view (column, tree_view);
9406 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9410 _pspp_sheet_view_column_realize_button (column);
9412 for (list = tree_view->priv->columns; list; list = list->next)
9414 column = PSPP_SHEET_VIEW_COLUMN (list->data);
9415 if (column->visible)
9416 _pspp_sheet_view_column_cell_set_dirty (column);
9418 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9421 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9423 return tree_view->priv->n_columns;
9427 * pspp_sheet_view_insert_column_with_attributes:
9428 * @tree_view: A #PsppSheetView
9429 * @position: The position to insert the new column in.
9430 * @title: The title to set the header to.
9431 * @cell: The #GtkCellRenderer.
9432 * @Varargs: A %NULL-terminated list of attributes.
9434 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9435 * @position. If @position is -1, then the newly created column is inserted at
9436 * the end. The column is initialized with the attributes given.
9438 * Return value: The number of columns in @tree_view after insertion.
9441 pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view,
9444 GtkCellRenderer *cell,
9447 PsppSheetViewColumn *column;
9452 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9454 column = pspp_sheet_view_column_new ();
9455 pspp_sheet_view_column_set_title (column, title);
9456 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9458 va_start (args, cell);
9460 attribute = va_arg (args, gchar *);
9462 while (attribute != NULL)
9464 column_id = va_arg (args, gint);
9465 pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9466 attribute = va_arg (args, gchar *);
9471 pspp_sheet_view_insert_column (tree_view, column, position);
9473 return tree_view->priv->n_columns;
9477 * pspp_sheet_view_insert_column_with_data_func:
9478 * @tree_view: a #PsppSheetView
9479 * @position: Position to insert, -1 for append
9480 * @title: column title
9481 * @cell: cell renderer for column
9482 * @func: function to set attributes of cell renderer
9483 * @data: data for @func
9484 * @dnotify: destroy notifier for @data
9486 * Convenience function that inserts a new column into the #PsppSheetView
9487 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9488 * attributes (normally using data from the model). See also
9489 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9491 * Return value: number of columns in the tree view post-insert
9494 pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view,
9497 GtkCellRenderer *cell,
9498 PsppSheetCellDataFunc func,
9500 GDestroyNotify dnotify)
9502 PsppSheetViewColumn *column;
9504 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9506 column = pspp_sheet_view_column_new ();
9507 pspp_sheet_view_column_set_title (column, title);
9508 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9509 pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9511 pspp_sheet_view_insert_column (tree_view, column, position);
9513 return tree_view->priv->n_columns;
9517 * pspp_sheet_view_get_column:
9518 * @tree_view: A #PsppSheetView.
9519 * @n: The position of the column, counting from 0.
9521 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9523 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9526 PsppSheetViewColumn *
9527 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9530 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9532 if (n < 0 || n >= tree_view->priv->n_columns)
9535 if (tree_view->priv->columns == NULL)
9538 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9542 * pspp_sheet_view_get_columns:
9543 * @tree_view: A #PsppSheetView
9545 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9546 * The returned list must be freed with g_list_free ().
9548 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9551 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9553 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9555 return g_list_copy (tree_view->priv->columns);
9559 * pspp_sheet_view_move_column_after:
9560 * @tree_view: A #PsppSheetView
9561 * @column: The #PsppSheetViewColumn to be moved.
9562 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9564 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9565 * @column is placed in the first position.
9568 pspp_sheet_view_move_column_after (PsppSheetView *tree_view,
9569 PsppSheetViewColumn *column,
9570 PsppSheetViewColumn *base_column)
9572 GList *column_list_el, *base_el = NULL;
9574 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9576 column_list_el = g_list_find (tree_view->priv->columns, column);
9577 g_return_if_fail (column_list_el != NULL);
9581 base_el = g_list_find (tree_view->priv->columns, base_column);
9582 g_return_if_fail (base_el != NULL);
9585 if (column_list_el->prev == base_el)
9588 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9589 if (base_el == NULL)
9591 column_list_el->prev = NULL;
9592 column_list_el->next = tree_view->priv->columns;
9593 if (column_list_el->next)
9594 column_list_el->next->prev = column_list_el;
9595 tree_view->priv->columns = column_list_el;
9599 column_list_el->prev = base_el;
9600 column_list_el->next = base_el->next;
9601 if (column_list_el->next)
9602 column_list_el->next->prev = column_list_el;
9603 base_el->next = column_list_el;
9606 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9608 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9609 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9612 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9616 * pspp_sheet_view_set_column_drag_function:
9617 * @tree_view: A #PsppSheetView.
9618 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9619 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9620 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9622 * Sets a user function for determining where a column may be dropped when
9623 * dragged. This function is called on every column pair in turn at the
9624 * beginning of a column drag to determine where a drop can take place. The
9625 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9626 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9627 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9628 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9629 * @tree_view reverts to the default behavior of allowing all columns to be
9630 * dropped everywhere.
9633 pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view,
9634 PsppSheetViewColumnDropFunc func,
9636 GDestroyNotify destroy)
9638 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9640 if (tree_view->priv->column_drop_func_data_destroy)
9641 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9643 tree_view->priv->column_drop_func = func;
9644 tree_view->priv->column_drop_func_data = user_data;
9645 tree_view->priv->column_drop_func_data_destroy = destroy;
9649 * pspp_sheet_view_scroll_to_point:
9650 * @tree_view: a #PsppSheetView
9651 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9652 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9654 * Scrolls the tree view such that the top-left corner of the visible
9655 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9656 * in tree coordinates. The @tree_view must be realized before
9657 * this function is called. If it isn't, you probably want to be
9658 * using pspp_sheet_view_scroll_to_cell().
9660 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9663 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9667 GtkAdjustment *hadj;
9668 GtkAdjustment *vadj;
9670 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9671 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9673 hadj = tree_view->priv->hadjustment;
9674 vadj = tree_view->priv->vadjustment;
9677 gtk_adjustment_set_value (hadj, CLAMP (tree_x, hadj->lower, hadj->upper - hadj->page_size));
9679 gtk_adjustment_set_value (vadj, CLAMP (tree_y, vadj->lower, vadj->upper - vadj->page_size));
9683 * pspp_sheet_view_scroll_to_cell:
9684 * @tree_view: A #PsppSheetView.
9685 * @path: (allow-none): The path of the row to move to, or %NULL.
9686 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9687 * @use_align: whether to use alignment arguments, or %FALSE.
9688 * @row_align: The vertical alignment of the row specified by @path.
9689 * @col_align: The horizontal alignment of the column specified by @column.
9691 * Moves the alignments of @tree_view to the position specified by @column and
9692 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9693 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9694 * or @path need to be non-%NULL. @row_align determines where the row is
9695 * placed, and @col_align determines where @column is placed. Both are expected
9696 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9697 * right/bottom alignment, 0.5 means center.
9699 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9700 * tree does the minimum amount of work to scroll the cell onto the screen.
9701 * This means that the cell will be scrolled to the edge closest to its current
9702 * position. If the cell is currently visible on the screen, nothing is done.
9704 * This function only works if the model is set, and @path is a valid row on the
9705 * model. If the model changes before the @tree_view is realized, the centered
9706 * path will be modified to reflect this change.
9709 pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view,
9711 PsppSheetViewColumn *column,
9716 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9717 g_return_if_fail (tree_view->priv->model != NULL);
9718 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9719 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9720 g_return_if_fail (path != NULL || column != NULL);
9723 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9724 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9726 row_align = CLAMP (row_align, 0.0, 1.0);
9727 col_align = CLAMP (col_align, 0.0, 1.0);
9730 /* Note: Despite the benefits that come from having one code path for the
9731 * scrolling code, we short-circuit validate_visible_area's immplementation as
9732 * it is much slower than just going to the point.
9734 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9735 !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9736 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9738 if (tree_view->priv->scroll_to_path)
9739 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9741 tree_view->priv->scroll_to_path = NULL;
9742 tree_view->priv->scroll_to_column = NULL;
9745 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
9747 tree_view->priv->scroll_to_column = column;
9748 tree_view->priv->scroll_to_use_align = use_align;
9749 tree_view->priv->scroll_to_row_align = row_align;
9750 tree_view->priv->scroll_to_col_align = col_align;
9752 install_presize_handler (tree_view);
9756 GdkRectangle cell_rect;
9757 GdkRectangle vis_rect;
9758 gint dest_x, dest_y;
9760 pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
9761 pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
9763 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
9765 dest_x = vis_rect.x;
9766 dest_y = vis_rect.y;
9772 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
9776 if (cell_rect.x < vis_rect.x)
9777 dest_x = cell_rect.x;
9778 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
9779 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
9787 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
9788 dest_y = MAX (dest_y, 0);
9792 if (cell_rect.y < vis_rect.y)
9793 dest_y = cell_rect.y;
9794 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
9795 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
9799 pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
9804 * pspp_sheet_view_row_activated:
9805 * @tree_view: A #PsppSheetView
9806 * @path: The #GtkTreePath to be activated.
9807 * @column: The #PsppSheetViewColumn to be activated.
9809 * Activates the cell determined by @path and @column.
9812 pspp_sheet_view_row_activated (PsppSheetView *tree_view,
9814 PsppSheetViewColumn *column)
9816 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9818 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
9823 * pspp_sheet_view_get_reorderable:
9824 * @tree_view: a #PsppSheetView
9826 * Retrieves whether the user can reorder the tree via drag-and-drop. See
9827 * pspp_sheet_view_set_reorderable().
9829 * Return value: %TRUE if the tree can be reordered.
9832 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
9834 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9836 return tree_view->priv->reorderable;
9840 * pspp_sheet_view_set_reorderable:
9841 * @tree_view: A #PsppSheetView.
9842 * @reorderable: %TRUE, if the tree can be reordered.
9844 * This function is a convenience function to allow you to reorder
9845 * models that support the #GtkDragSourceIface and the
9846 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
9847 * these. If @reorderable is %TRUE, then the user can reorder the
9848 * model by dragging and dropping rows. The developer can listen to
9849 * these changes by connecting to the model's row_inserted and
9850 * row_deleted signals. The reordering is implemented by setting up
9851 * the tree view as a drag source and destination. Therefore, drag and
9852 * drop can not be used in a reorderable view for any other purpose.
9854 * This function does not give you any degree of control over the order -- any
9855 * reordering is allowed. If more control is needed, you should probably
9856 * handle drag and drop manually.
9859 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
9860 gboolean reorderable)
9862 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9864 reorderable = reorderable != FALSE;
9866 if (tree_view->priv->reorderable == reorderable)
9871 const GtkTargetEntry row_targets[] = {
9872 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
9875 pspp_sheet_view_enable_model_drag_source (tree_view,
9878 G_N_ELEMENTS (row_targets),
9880 pspp_sheet_view_enable_model_drag_dest (tree_view,
9882 G_N_ELEMENTS (row_targets),
9887 pspp_sheet_view_unset_rows_drag_source (tree_view);
9888 pspp_sheet_view_unset_rows_drag_dest (tree_view);
9891 tree_view->priv->reorderable = reorderable;
9893 g_object_notify (G_OBJECT (tree_view), "reorderable");
9896 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
9897 is pressed, other rows will be unselected.
9899 If CLAMP_NODE is true, then the sheetview will scroll to make the row
9902 pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
9904 gboolean clear_and_select,
9905 gboolean clamp_node)
9909 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9911 GtkTreePath *cursor_path;
9912 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9913 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
9914 gtk_tree_path_free (cursor_path);
9917 gtk_tree_row_reference_free (tree_view->priv->cursor);
9918 tree_view->priv->cursor = NULL;
9920 _pspp_sheet_view_find_node (tree_view, path, &node);
9921 tree_view->priv->cursor =
9922 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
9923 tree_view->priv->model,
9926 if (tree_view->priv->row_count > 0)
9930 if (clear_and_select && !tree_view->priv->ctrl_pressed)
9932 GtkTreeSelectMode mode = 0;
9934 if (tree_view->priv->ctrl_pressed)
9935 mode |= GTK_TREE_SELECT_MODE_TOGGLE;
9936 if (tree_view->priv->shift_pressed)
9937 mode |= GTK_TREE_SELECT_MODE_EXTEND;
9939 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
9944 /* We have to re-find tree and node here again, somebody might have
9945 * cleared the node or the whole tree in the PsppSheetSelection::changed
9946 * callback. If the nodes differ we bail out here.
9948 _pspp_sheet_view_find_node (tree_view, path, &new_node);
9950 if (node != new_node)
9955 pspp_sheet_view_clamp_node_visible (tree_view, node);
9956 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
9960 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
9964 * pspp_sheet_view_get_cursor:
9965 * @tree_view: A #PsppSheetView
9966 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
9967 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
9969 * Fills in @path and @focus_column with the current path and focus column. If
9970 * the cursor isn't currently set, then *@path will be %NULL. If no column
9971 * currently has focus, then *@focus_column will be %NULL.
9973 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
9974 * you are done with it.
9977 pspp_sheet_view_get_cursor (PsppSheetView *tree_view,
9979 PsppSheetViewColumn **focus_column)
9981 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9985 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9986 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9993 *focus_column = tree_view->priv->focus_column;
9998 * pspp_sheet_view_set_cursor:
9999 * @tree_view: A #PsppSheetView
10000 * @path: A #GtkTreePath
10001 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10002 * @start_editing: %TRUE if the specified cell should start being edited.
10004 * Sets the current keyboard focus to be at @path, and selects it. This is
10005 * useful when you want to focus the user's attention on a particular row. If
10006 * @focus_column is not %NULL, then focus is given to the column specified by
10007 * it. Additionally, if @focus_column is specified, and @start_editing is
10008 * %TRUE, then editing should be started in the specified cell.
10009 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10010 * in order to give keyboard focus to the widget. Please note that editing
10011 * can only happen when the widget is realized.
10013 * If @path is invalid for @model, the current cursor (if any) will be unset
10014 * and the function will return without failing.
10017 pspp_sheet_view_set_cursor (PsppSheetView *tree_view,
10019 PsppSheetViewColumn *focus_column,
10020 gboolean start_editing)
10022 pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10023 NULL, start_editing);
10027 * pspp_sheet_view_set_cursor_on_cell:
10028 * @tree_view: A #PsppSheetView
10029 * @path: A #GtkTreePath
10030 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10031 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10032 * @start_editing: %TRUE if the specified cell should start being edited.
10034 * Sets the current keyboard focus to be at @path, and selects it. This is
10035 * useful when you want to focus the user's attention on a particular row. If
10036 * @focus_column is not %NULL, then focus is given to the column specified by
10037 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10038 * contains 2 or more editable or activatable cells, then focus is given to
10039 * the cell specified by @focus_cell. Additionally, if @focus_column is
10040 * specified, and @start_editing is %TRUE, then editing should be started in
10041 * the specified cell. This function is often followed by
10042 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10043 * widget. Please note that editing can only happen when the widget is
10046 * If @path is invalid for @model, the current cursor (if any) will be unset
10047 * and the function will return without failing.
10052 pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view,
10054 PsppSheetViewColumn *focus_column,
10055 GtkCellRenderer *focus_cell,
10056 gboolean start_editing)
10058 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10059 g_return_if_fail (path != NULL);
10060 g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10062 if (!tree_view->priv->model)
10067 g_return_if_fail (focus_column);
10068 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10071 /* cancel the current editing, if it exists */
10072 if (tree_view->priv->edited_column &&
10073 tree_view->priv->edited_column->editable_widget)
10074 pspp_sheet_view_stop_editing (tree_view, TRUE);
10076 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE);
10078 if (focus_column && focus_column->visible)
10081 gboolean column_in_tree = FALSE;
10083 for (list = tree_view->priv->columns; list; list = list->next)
10084 if (list->data == focus_column)
10086 column_in_tree = TRUE;
10089 g_return_if_fail (column_in_tree);
10090 tree_view->priv->focus_column = focus_column;
10092 pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10094 pspp_sheet_view_start_editing (tree_view, path);
10096 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10097 pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10103 * pspp_sheet_view_get_bin_window:
10104 * @tree_view: A #PsppSheetView
10106 * Returns the window that @tree_view renders to. This is used primarily to
10107 * compare to <literal>event->window</literal> to confirm that the event on
10108 * @tree_view is on the right window.
10110 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10113 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10115 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10117 return tree_view->priv->bin_window;
10121 * pspp_sheet_view_get_path_at_pos:
10122 * @tree_view: A #PsppSheetView.
10123 * @x: The x position to be identified (relative to bin_window).
10124 * @y: The y position to be identified (relative to bin_window).
10125 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10126 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10127 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10128 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10130 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10131 * (please see pspp_sheet_view_get_bin_window()).
10132 * That is, @x and @y are relative to an events coordinates. @x and @y must
10133 * come from an event on the @tree_view only where <literal>event->window ==
10134 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10135 * things like popup menus. If @path is non-%NULL, then it will be filled
10136 * with the #GtkTreePath at that point. This path should be freed with
10137 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10138 * with the column at that point. @cell_x and @cell_y return the coordinates
10139 * relative to the cell background (i.e. the @background_area passed to
10140 * gtk_cell_renderer_render()). This function is only meaningful if
10141 * @tree_view is realized. Therefore this function will always return %FALSE
10142 * if @tree_view is not realized or does not have a model.
10144 * For converting widget coordinates (eg. the ones you get from
10145 * GtkWidget::query-tooltip), please see
10146 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10148 * Return value: %TRUE if a row exists at that coordinate.
10151 pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view,
10154 GtkTreePath **path,
10155 PsppSheetViewColumn **column,
10162 g_return_val_if_fail (tree_view != NULL, FALSE);
10169 if (tree_view->priv->bin_window == NULL)
10172 if (tree_view->priv->row_count == 0)
10175 if (x > tree_view->priv->hadjustment->upper)
10178 if (x < 0 || y < 0)
10181 if (column || cell_x)
10183 PsppSheetViewColumn *tmp_column;
10184 PsppSheetViewColumn *last_column = NULL;
10186 gint remaining_x = x;
10187 gboolean found = FALSE;
10190 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10191 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10193 list = (rtl ? list->prev : list->next))
10195 tmp_column = list->data;
10197 if (tmp_column->visible == FALSE)
10200 last_column = tmp_column;
10201 if (remaining_x <= tmp_column->width)
10206 *column = tmp_column;
10209 *cell_x = remaining_x;
10213 remaining_x -= tmp_column->width;
10216 /* If found is FALSE and there is a last_column, then it the remainder
10217 * space is in that area
10224 *column = last_column;
10227 *cell_x = last_column->width + remaining_x;
10236 y_offset = pspp_sheet_view_find_offset (tree_view,
10237 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10244 *cell_y = y_offset;
10247 *path = _pspp_sheet_view_find_path (tree_view, node);
10252 /* Computes 'cell_area' from 'background_area', which must be the background
10253 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10254 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10255 the cell area as passed to _pspp_sheet_view_column_cell_render().
10257 'column' is required to properly adjust 'cell_area->x' and
10258 'cell_area->width'. It may be set to NULL if these values are not of
10259 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10262 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
10263 PsppSheetViewColumn *column,
10264 const GdkRectangle *background_area,
10265 gboolean subtract_focus_rect,
10266 GdkRectangle *cell_area)
10268 gint vertical_separator;
10269 gint horizontal_separator;
10271 *cell_area = *background_area;
10273 gtk_widget_style_get (GTK_WIDGET (tree_view),
10274 "vertical-separator", &vertical_separator,
10275 "horizontal-separator", &horizontal_separator,
10277 cell_area->x += horizontal_separator / 2;
10278 cell_area->y += vertical_separator / 2;
10279 cell_area->width -= horizontal_separator;
10280 cell_area->height -= vertical_separator;
10282 if (subtract_focus_rect)
10284 int focus_line_width;
10286 gtk_widget_style_get (GTK_WIDGET (tree_view),
10287 "focus-line-width", &focus_line_width,
10289 cell_area->x += focus_line_width;
10290 cell_area->y += focus_line_width;
10291 cell_area->width -= 2 * focus_line_width;
10292 cell_area->height -= 2 * focus_line_width;
10295 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10297 gint grid_line_width;
10298 gtk_widget_style_get (GTK_WIDGET (tree_view),
10299 "grid-line-width", &grid_line_width,
10302 if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10303 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10306 PsppSheetViewColumn *first_column, *last_column;
10309 /* Find the last visible column. */
10310 last_column = NULL;
10311 for (list = g_list_last (tree_view->priv->columns);
10315 PsppSheetViewColumn *c = list->data;
10323 /* Find the first visible column. */
10324 first_column = NULL;
10325 for (list = g_list_first (tree_view->priv->columns);
10329 PsppSheetViewColumn *c = list->data;
10337 if (column == first_column)
10339 cell_area->width -= grid_line_width / 2;
10341 else if (column == last_column)
10343 cell_area->x += grid_line_width / 2;
10344 cell_area->width -= grid_line_width / 2;
10348 cell_area->x += grid_line_width / 2;
10349 cell_area->width -= grid_line_width;
10353 if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10354 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10356 cell_area->y += grid_line_width / 2;
10357 cell_area->height -= grid_line_width;
10361 if (column == NULL)
10364 cell_area->width = 0;
10369 * pspp_sheet_view_get_cell_area:
10370 * @tree_view: a #PsppSheetView
10371 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10372 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10373 * @rect: rectangle to fill with cell rect
10375 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10376 * row specified by @path and the column specified by @column. If @path is
10377 * %NULL, or points to a path not currently displayed, the @y and @height fields
10378 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10379 * fields will be filled with 0. The sum of all cell rects does not cover the
10380 * entire tree; there are extra pixels in between rows, for example. The
10381 * returned rectangle is equivalent to the @cell_area passed to
10382 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10386 pspp_sheet_view_get_cell_area (PsppSheetView *tree_view,
10388 PsppSheetViewColumn *column,
10389 GdkRectangle *rect)
10391 GdkRectangle background_area;
10393 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10394 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10395 g_return_if_fail (rect != NULL);
10396 g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10397 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10399 pspp_sheet_view_get_background_area (tree_view, path, column,
10401 pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10406 * pspp_sheet_view_get_background_area:
10407 * @tree_view: a #PsppSheetView
10408 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10409 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10410 * @rect: rectangle to fill with cell background rect
10412 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10413 * row specified by @path and the column specified by @column. If @path is
10414 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10415 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10416 * fields will be filled with 0. The returned rectangle is equivalent to the
10417 * @background_area passed to gtk_cell_renderer_render(). These background
10418 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10419 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10420 * itself, excluding surrounding borders.
10424 pspp_sheet_view_get_background_area (PsppSheetView *tree_view,
10426 PsppSheetViewColumn *column,
10427 GdkRectangle *rect)
10431 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10432 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10433 g_return_if_fail (rect != NULL);
10442 /* Get vertical coords */
10444 _pspp_sheet_view_find_node (tree_view, path, &node);
10448 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10450 rect->height = ROW_HEIGHT (tree_view);
10457 pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10458 rect->width = x2 - rect->x;
10463 * pspp_sheet_view_get_visible_rect:
10464 * @tree_view: a #PsppSheetView
10465 * @visible_rect: rectangle to fill
10467 * Fills @visible_rect with the currently-visible region of the
10468 * buffer, in tree coordinates. Convert to bin_window coordinates with
10469 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10470 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10471 * scrollable area of the tree.
10474 pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view,
10475 GdkRectangle *visible_rect)
10479 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10481 widget = GTK_WIDGET (tree_view);
10485 visible_rect->x = tree_view->priv->hadjustment->value;
10486 visible_rect->y = tree_view->priv->vadjustment->value;
10487 visible_rect->width = widget->allocation.width;
10488 visible_rect->height = widget->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10493 * pspp_sheet_view_widget_to_tree_coords:
10494 * @tree_view: a #PsppSheetView
10495 * @wx: X coordinate relative to bin_window
10496 * @wy: Y coordinate relative to bin_window
10497 * @tx: return location for tree X coordinate
10498 * @ty: return location for tree Y coordinate
10500 * Converts bin_window coordinates to coordinates for the
10501 * tree (the full scrollable area of the tree).
10503 * Deprecated: 2.12: Due to historial reasons the name of this function is
10504 * incorrect. For converting coordinates relative to the widget to
10505 * bin_window coordinates, please see
10506 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10510 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10516 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10519 *tx = wx + tree_view->priv->hadjustment->value;
10521 *ty = wy + tree_view->priv->dy;
10525 * pspp_sheet_view_tree_to_widget_coords:
10526 * @tree_view: a #PsppSheetView
10527 * @tx: tree X coordinate
10528 * @ty: tree Y coordinate
10529 * @wx: return location for X coordinate relative to bin_window
10530 * @wy: return location for Y coordinate relative to bin_window
10532 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10533 * to bin_window coordinates.
10535 * Deprecated: 2.12: Due to historial reasons the name of this function is
10536 * incorrect. For converting bin_window coordinates to coordinates relative
10537 * to bin_window, please see
10538 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10542 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10548 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10551 *wx = tx - tree_view->priv->hadjustment->value;
10553 *wy = ty - tree_view->priv->dy;
10558 * pspp_sheet_view_convert_widget_to_tree_coords:
10559 * @tree_view: a #PsppSheetView
10560 * @wx: X coordinate relative to the widget
10561 * @wy: Y coordinate relative to the widget
10562 * @tx: return location for tree X coordinate
10563 * @ty: return location for tree Y coordinate
10565 * Converts widget coordinates to coordinates for the
10566 * tree (the full scrollable area of the tree).
10571 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10579 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10581 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10584 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10590 * pspp_sheet_view_convert_tree_to_widget_coords:
10591 * @tree_view: a #PsppSheetView
10592 * @tx: X coordinate relative to the tree
10593 * @ty: Y coordinate relative to the tree
10594 * @wx: return location for widget X coordinate
10595 * @wy: return location for widget Y coordinate
10597 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10598 * to widget coordinates.
10603 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10611 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10613 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10616 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10622 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10623 * @tree_view: a #PsppSheetView
10624 * @wx: X coordinate relative to the widget
10625 * @wy: Y coordinate relative to the widget
10626 * @bx: return location for bin_window X coordinate
10627 * @by: return location for bin_window Y coordinate
10629 * Converts widget coordinates to coordinates for the bin_window
10630 * (see pspp_sheet_view_get_bin_window()).
10635 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10641 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10644 *bx = wx + tree_view->priv->hadjustment->value;
10646 *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10650 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10651 * @tree_view: a #PsppSheetView
10652 * @bx: bin_window X coordinate
10653 * @by: bin_window Y coordinate
10654 * @wx: return location for widget X coordinate
10655 * @wy: return location for widget Y coordinate
10657 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10658 * to widget relative coordinates.
10663 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10669 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10672 *wx = bx - tree_view->priv->hadjustment->value;
10674 *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10678 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10679 * @tree_view: a #PsppSheetView
10680 * @tx: tree X coordinate
10681 * @ty: tree Y coordinate
10682 * @bx: return location for X coordinate relative to bin_window
10683 * @by: return location for Y coordinate relative to bin_window
10685 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10686 * to bin_window coordinates.
10691 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10697 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10702 *by = ty - tree_view->priv->dy;
10706 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10707 * @tree_view: a #PsppSheetView
10708 * @bx: X coordinate relative to bin_window
10709 * @by: Y coordinate relative to bin_window
10710 * @tx: return location for tree X coordinate
10711 * @ty: return location for tree Y coordinate
10713 * Converts bin_window coordinates to coordinates for the
10714 * tree (the full scrollable area of the tree).
10719 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10725 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10730 *ty = by + tree_view->priv->dy;
10736 * pspp_sheet_view_get_visible_range:
10737 * @tree_view: A #PsppSheetView
10738 * @start_path: (allow-none): Return location for start of region, or %NULL.
10739 * @end_path: (allow-none): Return location for end of region, or %NULL.
10741 * Sets @start_path and @end_path to be the first and last visible path.
10742 * Note that there may be invisible paths in between.
10744 * The paths should be freed with gtk_tree_path_free() after use.
10746 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
10751 pspp_sheet_view_get_visible_range (PsppSheetView *tree_view,
10752 GtkTreePath **start_path,
10753 GtkTreePath **end_path)
10758 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10760 if (!tree_view->priv->row_count)
10767 pspp_sheet_view_find_offset (tree_view,
10768 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
10771 *start_path = _pspp_sheet_view_find_path (tree_view, node);
10780 if (tree_view->priv->height < tree_view->priv->vadjustment->page_size)
10781 y = tree_view->priv->height - 1;
10783 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, tree_view->priv->vadjustment->page_size) - 1;
10785 pspp_sheet_view_find_offset (tree_view, y, &node);
10787 *end_path = _pspp_sheet_view_find_path (tree_view, node);
10796 unset_reorderable (PsppSheetView *tree_view)
10798 if (tree_view->priv->reorderable)
10800 tree_view->priv->reorderable = FALSE;
10801 g_object_notify (G_OBJECT (tree_view), "reorderable");
10806 * pspp_sheet_view_enable_model_drag_source:
10807 * @tree_view: a #PsppSheetView
10808 * @start_button_mask: Mask of allowed buttons to start drag
10809 * @targets: the table of targets that the drag will support
10810 * @n_targets: the number of items in @targets
10811 * @actions: the bitmask of possible actions for a drag from this
10814 * Turns @tree_view into a drag source for automatic DND. Calling this
10815 * method sets #PsppSheetView:reorderable to %FALSE.
10818 pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view,
10819 GdkModifierType start_button_mask,
10820 const GtkTargetEntry *targets,
10822 GdkDragAction actions)
10824 TreeViewDragInfo *di;
10826 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10828 gtk_drag_source_set (GTK_WIDGET (tree_view),
10834 di = ensure_info (tree_view);
10836 di->start_button_mask = start_button_mask;
10837 di->source_actions = actions;
10838 di->source_set = TRUE;
10840 unset_reorderable (tree_view);
10844 * pspp_sheet_view_enable_model_drag_dest:
10845 * @tree_view: a #PsppSheetView
10846 * @targets: the table of targets that the drag will support
10847 * @n_targets: the number of items in @targets
10848 * @actions: the bitmask of possible actions for a drag from this
10851 * Turns @tree_view into a drop destination for automatic DND. Calling
10852 * this method sets #PsppSheetView:reorderable to %FALSE.
10855 pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view,
10856 const GtkTargetEntry *targets,
10858 GdkDragAction actions)
10860 TreeViewDragInfo *di;
10862 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10864 gtk_drag_dest_set (GTK_WIDGET (tree_view),
10870 di = ensure_info (tree_view);
10871 di->dest_set = TRUE;
10873 unset_reorderable (tree_view);
10877 * pspp_sheet_view_unset_rows_drag_source:
10878 * @tree_view: a #PsppSheetView
10880 * Undoes the effect of
10881 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
10882 * #PsppSheetView:reorderable to %FALSE.
10885 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
10887 TreeViewDragInfo *di;
10889 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10891 di = get_info (tree_view);
10895 if (di->source_set)
10897 gtk_drag_source_unset (GTK_WIDGET (tree_view));
10898 di->source_set = FALSE;
10901 if (!di->dest_set && !di->source_set)
10902 remove_info (tree_view);
10905 unset_reorderable (tree_view);
10909 * pspp_sheet_view_unset_rows_drag_dest:
10910 * @tree_view: a #PsppSheetView
10912 * Undoes the effect of
10913 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
10914 * #PsppSheetView:reorderable to %FALSE.
10917 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
10919 TreeViewDragInfo *di;
10921 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10923 di = get_info (tree_view);
10929 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
10930 di->dest_set = FALSE;
10933 if (!di->dest_set && !di->source_set)
10934 remove_info (tree_view);
10937 unset_reorderable (tree_view);
10941 * pspp_sheet_view_set_drag_dest_row:
10942 * @tree_view: a #PsppSheetView
10943 * @path: (allow-none): The path of the row to highlight, or %NULL.
10944 * @pos: Specifies whether to drop before, after or into the row
10946 * Sets the row that is highlighted for feedback.
10949 pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view,
10951 PsppSheetViewDropPosition pos)
10953 GtkTreePath *current_dest;
10955 /* Note; this function is exported to allow a custom DND
10956 * implementation, so it can't touch TreeViewDragInfo
10959 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10961 current_dest = NULL;
10963 if (tree_view->priv->drag_dest_row)
10965 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
10966 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
10969 /* special case a drop on an empty model */
10970 tree_view->priv->empty_view_drop = 0;
10972 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
10973 && gtk_tree_path_get_depth (path) == 1
10974 && gtk_tree_path_get_indices (path)[0] == 0)
10978 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
10982 tree_view->priv->empty_view_drop = 1;
10985 tree_view->priv->drag_dest_pos = pos;
10989 tree_view->priv->drag_dest_row =
10990 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
10991 pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
10994 tree_view->priv->drag_dest_row = NULL;
10998 int node, new_node;
11000 _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11001 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11005 new_node = pspp_sheet_view_node_next (tree_view, node);
11007 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11009 new_node = pspp_sheet_view_node_prev (tree_view, node);
11011 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11013 gtk_tree_path_free (current_dest);
11018 * pspp_sheet_view_get_drag_dest_row:
11019 * @tree_view: a #PsppSheetView
11020 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11021 * @pos: (allow-none): Return location for the drop position, or %NULL
11023 * Gets information about the row that is highlighted for feedback.
11026 pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view,
11027 GtkTreePath **path,
11028 PsppSheetViewDropPosition *pos)
11030 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11034 if (tree_view->priv->drag_dest_row)
11035 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11038 if (tree_view->priv->empty_view_drop)
11039 *path = gtk_tree_path_new_from_indices (0, -1);
11046 *pos = tree_view->priv->drag_dest_pos;
11050 * pspp_sheet_view_get_dest_row_at_pos:
11051 * @tree_view: a #PsppSheetView
11052 * @drag_x: the position to determine the destination row for
11053 * @drag_y: the position to determine the destination row for
11054 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11055 * @pos: (allow-none): Return location for the drop position, or %NULL
11057 * Determines the destination row for a given position. @drag_x and
11058 * @drag_y are expected to be in widget coordinates. This function is only
11059 * meaningful if @tree_view is realized. Therefore this function will always
11060 * return %FALSE if @tree_view is not realized or does not have a model.
11062 * Return value: whether there is a row at the given position, %TRUE if this
11063 * is indeed the case.
11066 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view,
11069 GtkTreePath **path,
11070 PsppSheetViewDropPosition *pos)
11074 gdouble offset_into_row;
11077 PsppSheetViewColumn *column = NULL;
11078 GtkTreePath *tmp_path = NULL;
11080 /* Note; this function is exported to allow a custom DND
11081 * implementation, so it can't touch TreeViewDragInfo
11084 g_return_val_if_fail (tree_view != NULL, FALSE);
11085 g_return_val_if_fail (drag_x >= 0, FALSE);
11086 g_return_val_if_fail (drag_y >= 0, FALSE);
11091 if (tree_view->priv->bin_window == NULL)
11094 if (tree_view->priv->row_count == 0)
11097 /* If in the top third of a row, we drop before that row; if
11098 * in the bottom third, drop after that row; if in the middle,
11099 * and the row has children, drop into the row.
11101 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11104 if (!pspp_sheet_view_get_path_at_pos (tree_view,
11113 pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11116 offset_into_row = cell_y;
11121 gtk_tree_path_free (tmp_path);
11125 third = cell.height / 3.0;
11129 if (offset_into_row < third)
11131 *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11133 else if (offset_into_row < (cell.height / 2.0))
11135 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11137 else if (offset_into_row < third * 2.0)
11139 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11143 *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11152 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11154 * pspp_sheet_view_create_row_drag_icon:
11155 * @tree_view: a #PsppSheetView
11156 * @path: a #GtkTreePath in @tree_view
11158 * Creates a #GdkPixmap representation of the row at @path.
11159 * This image is used for a drag icon.
11161 * Return value: a newly-allocated pixmap of the drag icon.
11164 pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view,
11171 GdkRectangle background_area;
11172 GdkRectangle expose_area;
11174 /* start drawing inside the black outline */
11176 GdkDrawable *drawable;
11177 gint bin_window_width;
11180 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11181 g_return_val_if_fail (path != NULL, NULL);
11183 widget = GTK_WIDGET (tree_view);
11185 if (!gtk_widget_get_realized (widget))
11188 _pspp_sheet_view_find_node (tree_view,
11195 if (!gtk_tree_model_get_iter (tree_view->priv->model,
11202 background_area.y = y;
11203 background_area.height = ROW_HEIGHT (tree_view);
11205 gdk_drawable_get_size (tree_view->priv->bin_window,
11206 &bin_window_width, NULL);
11208 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11209 bin_window_width + 2,
11210 background_area.height + 2,
11215 expose_area.width = bin_window_width + 2;
11216 expose_area.height = background_area.height + 2;
11218 gdk_draw_rectangle (drawable,
11219 widget->style->base_gc [gtk_widget_get_state (widget)],
11222 bin_window_width + 2,
11223 background_area.height + 2);
11225 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11227 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11229 list = (rtl ? list->prev : list->next))
11231 PsppSheetViewColumn *column = list->data;
11232 GdkRectangle cell_area;
11233 gint vertical_separator;
11235 if (!column->visible)
11238 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11240 background_area.x = cell_offset;
11241 background_area.width = column->width;
11243 gtk_widget_style_get (widget,
11244 "vertical-separator", &vertical_separator,
11247 cell_area = background_area;
11249 cell_area.y += vertical_separator / 2;
11250 cell_area.height -= vertical_separator;
11252 if (pspp_sheet_view_column_cell_is_visible (column))
11253 _pspp_sheet_view_column_cell_render (column,
11259 cell_offset += column->width;
11262 gdk_draw_rectangle (drawable,
11263 widget->style->black_gc,
11266 bin_window_width + 1,
11267 background_area.height + 1);
11274 * pspp_sheet_view_set_destroy_count_func:
11275 * @tree_view: A #PsppSheetView
11276 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11277 * @data: (allow-none): User data to be passed to @func, or %NULL
11278 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11280 * This function should almost never be used. It is meant for private use by
11281 * ATK for determining the number of visible children that are removed when a row is deleted.
11284 pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view,
11285 PsppSheetDestroyCountFunc func,
11287 GDestroyNotify destroy)
11289 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11291 if (tree_view->priv->destroy_count_destroy)
11292 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11294 tree_view->priv->destroy_count_func = func;
11295 tree_view->priv->destroy_count_data = data;
11296 tree_view->priv->destroy_count_destroy = destroy;
11301 * Interactive search
11305 * pspp_sheet_view_set_enable_search:
11306 * @tree_view: A #PsppSheetView
11307 * @enable_search: %TRUE, if the user can search interactively
11309 * If @enable_search is set, then the user can type in text to search through
11310 * the tree interactively (this is sometimes called "typeahead find").
11312 * Note that even if this is %FALSE, the user can still initiate a search
11313 * using the "start-interactive-search" key binding.
11316 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11317 gboolean enable_search)
11319 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11321 enable_search = !!enable_search;
11323 if (tree_view->priv->enable_search != enable_search)
11325 tree_view->priv->enable_search = enable_search;
11326 g_object_notify (G_OBJECT (tree_view), "enable-search");
11331 * pspp_sheet_view_get_enable_search:
11332 * @tree_view: A #PsppSheetView
11334 * Returns whether or not the tree allows to start interactive searching
11335 * by typing in text.
11337 * Return value: whether or not to let the user search interactively
11340 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11342 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11344 return tree_view->priv->enable_search;
11349 * pspp_sheet_view_get_search_column:
11350 * @tree_view: A #PsppSheetView
11352 * Gets the column searched on by the interactive search code.
11354 * Return value: the column the interactive search code searches in.
11357 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11359 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11361 return (tree_view->priv->search_column);
11365 * pspp_sheet_view_set_search_column:
11366 * @tree_view: A #PsppSheetView
11367 * @column: the column of the model to search in, or -1 to disable searching
11369 * Sets @column as the column where the interactive search code should
11370 * search in for the current model.
11372 * If the search column is set, users can use the "start-interactive-search"
11373 * key binding to bring up search popup. The enable-search property controls
11374 * whether simply typing text will also start an interactive search.
11376 * Note that @column refers to a column of the current model. The search
11377 * column is reset to -1 when the model is changed.
11380 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11383 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11384 g_return_if_fail (column >= -1);
11386 if (tree_view->priv->search_column == column)
11389 tree_view->priv->search_column = column;
11390 g_object_notify (G_OBJECT (tree_view), "search-column");
11394 * pspp_sheet_view_get_search_equal_func:
11395 * @tree_view: A #PsppSheetView
11397 * Returns the compare function currently in use.
11399 * Return value: the currently used compare function for the search code.
11402 PsppSheetViewSearchEqualFunc
11403 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11405 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11407 return tree_view->priv->search_equal_func;
11411 * pspp_sheet_view_set_search_equal_func:
11412 * @tree_view: A #PsppSheetView
11413 * @search_equal_func: the compare function to use during the search
11414 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11415 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11417 * Sets the compare function for the interactive search capabilities; note
11418 * that somewhat like strcmp() returning 0 for equality
11419 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11422 pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view,
11423 PsppSheetViewSearchEqualFunc search_equal_func,
11424 gpointer search_user_data,
11425 GDestroyNotify search_destroy)
11427 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11428 g_return_if_fail (search_equal_func != NULL);
11430 if (tree_view->priv->search_destroy)
11431 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11433 tree_view->priv->search_equal_func = search_equal_func;
11434 tree_view->priv->search_user_data = search_user_data;
11435 tree_view->priv->search_destroy = search_destroy;
11436 if (tree_view->priv->search_equal_func == NULL)
11437 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11441 * pspp_sheet_view_get_search_entry:
11442 * @tree_view: A #PsppSheetView
11444 * Returns the #GtkEntry which is currently in use as interactive search
11445 * entry for @tree_view. In case the built-in entry is being used, %NULL
11446 * will be returned.
11448 * Return value: the entry currently in use as search entry.
11453 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11455 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11457 if (tree_view->priv->search_custom_entry_set)
11458 return GTK_ENTRY (tree_view->priv->search_entry);
11464 * pspp_sheet_view_set_search_entry:
11465 * @tree_view: A #PsppSheetView
11466 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11468 * Sets the entry which the interactive search code will use for this
11469 * @tree_view. This is useful when you want to provide a search entry
11470 * in our interface at all time at a fixed position. Passing %NULL for
11471 * @entry will make the interactive search code use the built-in popup
11477 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11480 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11481 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11483 if (tree_view->priv->search_custom_entry_set)
11485 if (tree_view->priv->search_entry_changed_id)
11487 g_signal_handler_disconnect (tree_view->priv->search_entry,
11488 tree_view->priv->search_entry_changed_id);
11489 tree_view->priv->search_entry_changed_id = 0;
11491 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11492 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11495 g_object_unref (tree_view->priv->search_entry);
11497 else if (tree_view->priv->search_window)
11499 gtk_widget_destroy (tree_view->priv->search_window);
11501 tree_view->priv->search_window = NULL;
11506 tree_view->priv->search_entry = g_object_ref (entry);
11507 tree_view->priv->search_custom_entry_set = TRUE;
11509 if (tree_view->priv->search_entry_changed_id == 0)
11511 tree_view->priv->search_entry_changed_id =
11512 g_signal_connect (tree_view->priv->search_entry, "changed",
11513 G_CALLBACK (pspp_sheet_view_search_init),
11517 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11518 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11521 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11525 tree_view->priv->search_entry = NULL;
11526 tree_view->priv->search_custom_entry_set = FALSE;
11531 * pspp_sheet_view_set_search_position_func:
11532 * @tree_view: A #PsppSheetView
11533 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11534 * to use the default search position function
11535 * @data: (allow-none): user data to pass to @func, or %NULL
11536 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11538 * Sets the function to use when positioning the search dialog.
11543 pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view,
11544 PsppSheetViewSearchPositionFunc func,
11545 gpointer user_data,
11546 GDestroyNotify destroy)
11548 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11550 if (tree_view->priv->search_position_destroy)
11551 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11553 tree_view->priv->search_position_func = func;
11554 tree_view->priv->search_position_user_data = user_data;
11555 tree_view->priv->search_position_destroy = destroy;
11556 if (tree_view->priv->search_position_func == NULL)
11557 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11561 * pspp_sheet_view_get_search_position_func:
11562 * @tree_view: A #PsppSheetView
11564 * Returns the positioning function currently in use.
11566 * Return value: the currently used function for positioning the search dialog.
11570 PsppSheetViewSearchPositionFunc
11571 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11573 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11575 return tree_view->priv->search_position_func;
11580 pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
11581 PsppSheetView *tree_view)
11583 if (tree_view->priv->disable_popdown)
11586 if (tree_view->priv->search_entry_changed_id)
11588 g_signal_handler_disconnect (tree_view->priv->search_entry,
11589 tree_view->priv->search_entry_changed_id);
11590 tree_view->priv->search_entry_changed_id = 0;
11592 if (tree_view->priv->typeselect_flush_timeout)
11594 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11595 tree_view->priv->typeselect_flush_timeout = 0;
11598 if (gtk_widget_get_visible (search_dialog))
11600 /* send focus-in event */
11601 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11602 gtk_widget_hide (search_dialog);
11603 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11604 send_focus_change (GTK_WIDGET (tree_view), TRUE);
11609 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11610 GtkWidget *search_dialog,
11611 gpointer user_data)
11614 gint tree_x, tree_y;
11615 gint tree_width, tree_height;
11616 GdkWindow *tree_window = GTK_WIDGET (tree_view)->window;
11617 GdkScreen *screen = gdk_drawable_get_screen (tree_window);
11618 GtkRequisition requisition;
11620 GdkRectangle monitor;
11622 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11623 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11625 gtk_widget_realize (search_dialog);
11627 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11628 gdk_drawable_get_size (tree_window,
11631 gtk_widget_size_request (search_dialog, &requisition);
11633 if (tree_x + tree_width > gdk_screen_get_width (screen))
11634 x = gdk_screen_get_width (screen) - requisition.width;
11635 else if (tree_x + tree_width - requisition.width < 0)
11638 x = tree_x + tree_width - requisition.width;
11640 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11641 y = gdk_screen_get_height (screen) - requisition.height;
11642 else if (tree_y + tree_height < 0) /* isn't really possible ... */
11645 y = tree_y + tree_height;
11647 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11651 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11655 PsppSheetView *tree_view = (PsppSheetView *)data;
11657 tree_view->priv->disable_popdown = 1;
11658 g_signal_connect (menu, "hide",
11659 G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11662 /* Because we're visible but offscreen, we just set a flag in the preedit
11666 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11667 PsppSheetView *tree_view)
11669 tree_view->priv->imcontext_changed = 1;
11670 if (tree_view->priv->typeselect_flush_timeout)
11672 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11673 tree_view->priv->typeselect_flush_timeout =
11674 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11675 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11682 pspp_sheet_view_search_activate (GtkEntry *entry,
11683 PsppSheetView *tree_view)
11688 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11691 /* If we have a row selected and it's the cursor row, we activate
11693 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11695 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11697 _pspp_sheet_view_find_node (tree_view, path, &node);
11699 if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11700 pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11702 gtk_tree_path_free (path);
11707 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11709 PsppSheetView *tree_view = (PsppSheetView *)data;
11711 tree_view->priv->disable_popdown = 0;
11717 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11720 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11724 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11725 GdkEventAny *event,
11726 PsppSheetView *tree_view)
11728 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11730 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11736 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11737 GdkEventButton *event,
11738 PsppSheetView *tree_view)
11740 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11742 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11744 if (event->window == tree_view->priv->bin_window)
11745 pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
11751 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
11752 GdkEventScroll *event,
11753 PsppSheetView *tree_view)
11755 gboolean retval = FALSE;
11757 if (event->direction == GDK_SCROLL_UP)
11759 pspp_sheet_view_search_move (widget, tree_view, TRUE);
11762 else if (event->direction == GDK_SCROLL_DOWN)
11764 pspp_sheet_view_search_move (widget, tree_view, FALSE);
11768 /* renew the flush timeout */
11769 if (retval && tree_view->priv->typeselect_flush_timeout
11770 && !tree_view->priv->search_custom_entry_set)
11772 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11773 tree_view->priv->typeselect_flush_timeout =
11774 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11775 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11783 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
11784 GdkEventKey *event,
11785 PsppSheetView *tree_view)
11787 gboolean retval = FALSE;
11789 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11790 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11792 /* close window and cancel the search */
11793 if (!tree_view->priv->search_custom_entry_set
11794 && (event->keyval == GDK_Escape ||
11795 event->keyval == GDK_Tab ||
11796 event->keyval == GDK_KP_Tab ||
11797 event->keyval == GDK_ISO_Left_Tab))
11799 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11803 /* select previous matching iter */
11804 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
11806 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11807 gtk_widget_error_bell (widget);
11812 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
11813 && (event->keyval == GDK_g || event->keyval == GDK_G))
11815 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11816 gtk_widget_error_bell (widget);
11821 /* select next matching iter */
11822 if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
11824 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11825 gtk_widget_error_bell (widget);
11830 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
11831 && (event->keyval == GDK_g || event->keyval == GDK_G))
11833 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11834 gtk_widget_error_bell (widget);
11839 /* renew the flush timeout */
11840 if (retval && tree_view->priv->typeselect_flush_timeout
11841 && !tree_view->priv->search_custom_entry_set)
11843 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11844 tree_view->priv->typeselect_flush_timeout =
11845 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11846 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11853 /* this function returns FALSE if there is a search string but
11854 * nothing was found, and TRUE otherwise.
11857 pspp_sheet_view_search_move (GtkWidget *window,
11858 PsppSheetView *tree_view,
11866 GtkTreeModel *model;
11867 PsppSheetSelection *selection;
11869 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
11871 g_return_val_if_fail (text != NULL, FALSE);
11873 len = strlen (text);
11875 if (up && tree_view->priv->selected_iter == 1)
11876 return strlen (text) < 1;
11878 len = strlen (text);
11883 model = pspp_sheet_view_get_model (tree_view);
11884 selection = pspp_sheet_view_get_selection (tree_view);
11887 pspp_sheet_selection_unselect_all (selection);
11888 if (!gtk_tree_model_get_iter_first (model, &iter))
11891 ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
11892 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
11897 tree_view->priv->selected_iter += up?(-1):(1);
11902 /* return to old iter */
11904 gtk_tree_model_get_iter_first (model, &iter);
11905 pspp_sheet_view_search_iter (model, selection,
11907 &count, tree_view->priv->selected_iter);
11913 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
11917 gpointer search_data)
11919 gboolean retval = TRUE;
11921 gchar *normalized_string;
11922 gchar *normalized_key;
11923 gchar *case_normalized_string = NULL;
11924 gchar *case_normalized_key = NULL;
11925 GValue value = {0,};
11926 GValue transformed = {0,};
11928 gtk_tree_model_get_value (model, iter, column, &value);
11930 g_value_init (&transformed, G_TYPE_STRING);
11932 if (!g_value_transform (&value, &transformed))
11934 g_value_unset (&value);
11938 g_value_unset (&value);
11940 str = g_value_get_string (&transformed);
11943 g_value_unset (&transformed);
11947 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
11948 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
11950 if (normalized_string && normalized_key)
11952 case_normalized_string = g_utf8_casefold (normalized_string, -1);
11953 case_normalized_key = g_utf8_casefold (normalized_key, -1);
11955 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
11959 g_value_unset (&transformed);
11960 g_free (normalized_key);
11961 g_free (normalized_string);
11962 g_free (case_normalized_key);
11963 g_free (case_normalized_string);
11969 pspp_sheet_view_search_iter (GtkTreeModel *model,
11970 PsppSheetSelection *selection,
11979 PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
11981 path = gtk_tree_model_get_path (model, iter);
11982 _pspp_sheet_view_find_node (tree_view, path, &node);
11986 gboolean done = FALSE;
11988 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
11993 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
11995 pspp_sheet_selection_select_iter (selection, iter);
11996 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE);
11999 gtk_tree_path_free (path);
12008 node = pspp_sheet_view_node_next (tree_view, node);
12014 has_next = gtk_tree_model_iter_next (model, iter);
12017 gtk_tree_path_next (path);
12020 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12025 gtk_tree_path_free (path);
12027 /* we've run out of tree, done with this func */
12039 pspp_sheet_view_search_init (GtkWidget *entry,
12040 PsppSheetView *tree_view)
12046 GtkTreeModel *model;
12047 PsppSheetSelection *selection;
12049 g_return_if_fail (GTK_IS_ENTRY (entry));
12050 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12052 text = gtk_entry_get_text (GTK_ENTRY (entry));
12054 model = pspp_sheet_view_get_model (tree_view);
12055 selection = pspp_sheet_view_get_selection (tree_view);
12058 pspp_sheet_selection_unselect_all (selection);
12059 if (tree_view->priv->typeselect_flush_timeout
12060 && !tree_view->priv->search_custom_entry_set)
12062 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12063 tree_view->priv->typeselect_flush_timeout =
12064 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12065 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12072 if (!gtk_tree_model_get_iter_first (model, &iter))
12075 ret = pspp_sheet_view_search_iter (model, selection,
12080 tree_view->priv->selected_iter = 1;
12084 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12085 PsppSheetView *tree_view)
12087 if (tree_view->priv->edited_column == NULL)
12090 _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12091 tree_view->priv->edited_column = NULL;
12093 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12094 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12096 g_signal_handlers_disconnect_by_func (cell_editable,
12097 pspp_sheet_view_remove_widget,
12099 g_signal_handlers_disconnect_by_func (cell_editable,
12100 pspp_sheet_view_editable_button_press_event,
12102 g_signal_handlers_disconnect_by_func (cell_editable,
12103 pspp_sheet_view_editable_clicked,
12106 gtk_container_remove (GTK_CONTAINER (tree_view),
12107 GTK_WIDGET (cell_editable));
12109 /* FIXME should only redraw a single node */
12110 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12114 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12115 GtkTreePath *cursor_path)
12118 GdkRectangle background_area;
12119 GdkRectangle cell_area;
12120 GtkCellEditable *editable_widget = NULL;
12121 gchar *path_string;
12122 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12123 gint retval = FALSE;
12126 g_assert (tree_view->priv->focus_column);
12128 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12131 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12132 if (cursor_node < 0)
12135 path_string = gtk_tree_path_to_string (cursor_path);
12136 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12138 pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12139 tree_view->priv->model,
12141 pspp_sheet_view_get_background_area (tree_view,
12143 tree_view->priv->focus_column,
12145 pspp_sheet_view_get_cell_area (tree_view,
12147 tree_view->priv->focus_column,
12150 if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12159 if (editable_widget != NULL)
12163 GtkCellRenderer *cell;
12166 cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12168 _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12171 area.width -= right + left;
12173 pspp_sheet_view_real_start_editing (tree_view,
12174 tree_view->priv->focus_column,
12183 g_free (path_string);
12188 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12189 GdkEventButton *event,
12190 PsppSheetView *sheet_view)
12194 node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12195 "pspp-sheet-view-node"));
12196 return pspp_sheet_view_row_head_clicked (sheet_view,
12198 sheet_view->priv->edited_column,
12203 pspp_sheet_view_editable_clicked (GtkButton *button,
12204 PsppSheetView *sheet_view)
12206 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12211 is_all_selected (GtkWidget *widget)
12213 GtkEntryBuffer *buffer;
12214 gint start_pos, end_pos;
12216 if (!GTK_IS_ENTRY (widget))
12219 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12220 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12221 &start_pos, &end_pos)
12223 && end_pos == gtk_entry_buffer_get_length (buffer));
12227 is_at_left (GtkWidget *widget)
12229 return (GTK_IS_ENTRY (widget)
12230 && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12234 is_at_right (GtkWidget *widget)
12236 GtkEntryBuffer *buffer;
12239 if (!GTK_IS_ENTRY (widget))
12242 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12243 length = gtk_entry_buffer_get_length (buffer);
12244 return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12248 pspp_sheet_view_event (GtkWidget *widget,
12249 GdkEventKey *event,
12250 PsppSheetView *tree_view)
12252 PsppSheetViewColumn *column;
12260 /* Intercept only key press events.
12261 It would make sense to use "key-press-event" instead of "event", but
12262 GtkEntry attaches its own signal handler to "key-press-event" that runs
12263 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12265 if (event->type != GDK_KEY_PRESS)
12268 if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12270 /* Pass through most keys that include modifiers. */
12271 if ((event->keyval == GDK_Tab || event->keyval == GDK_ISO_Left_Tab)
12272 && !(event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)))
12274 /* Special case for Shift-Tab. */
12280 keyval = event->keyval;
12281 state = event->state & ~(GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK);
12283 switch (event->keyval)
12285 case GDK_Left: case GDK_KP_Left:
12286 if (!is_all_selected (widget) && !is_at_left (widget))
12290 case GDK_Right: case GDK_KP_Right:
12291 if (!is_all_selected (widget) && !is_at_right (widget))
12295 case GDK_Up: case GDK_KP_Up:
12296 case GDK_Down: case GDK_KP_Down:
12299 case GDK_Page_Up: case GDK_KP_Page_Up:
12300 case GDK_Page_Down: case GDK_KP_Page_Down:
12311 case GDK_Tab: case GDK_KP_Tab:
12312 case GDK_ISO_Left_Tab:
12314 state |= event->state & GDK_SHIFT_MASK;
12321 row = tree_view->priv->edited_row;
12322 column = tree_view->priv->edited_column;
12323 path = gtk_tree_path_new_from_indices (row, -1);
12325 pspp_sheet_view_stop_editing (tree_view, cancel);
12326 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12328 pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12329 gtk_tree_path_free (path);
12331 handled = gtk_binding_set_activate (edit_bindings, keyval, state,
12332 GTK_OBJECT (tree_view));
12334 g_signal_stop_emission_by_name (widget, "event");
12336 pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12337 pspp_sheet_view_start_editing (tree_view, path);
12338 gtk_tree_path_free (path);
12344 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12347 PsppSheetView *sheet_view = data;
12349 g_signal_connect (widget, "event",
12350 G_CALLBACK (pspp_sheet_view_event),
12353 if (GTK_IS_CONTAINER (widget))
12354 gtk_container_foreach (GTK_CONTAINER (widget),
12355 pspp_sheet_view_override_cell_keypresses,
12360 pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
12361 PsppSheetViewColumn *column,
12363 GtkCellEditable *cell_editable,
12364 GdkRectangle *cell_area,
12368 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12369 gint pre_val = tree_view->priv->vadjustment->value;
12370 GtkRequisition requisition;
12373 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12375 tree_view->priv->edited_column = column;
12376 _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12378 row = gtk_tree_path_get_indices (path)[0];
12379 tree_view->priv->edited_row = row;
12380 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE);
12381 cell_area->y += pre_val - (int)tree_view->priv->vadjustment->value;
12383 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12384 pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12385 tree_view->priv->anchor_column = column;
12387 gtk_widget_size_request (GTK_WIDGET (cell_editable), &requisition);
12389 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12391 if (requisition.height < cell_area->height)
12393 gint diff = cell_area->height - requisition.height;
12394 pspp_sheet_view_put (tree_view,
12395 GTK_WIDGET (cell_editable),
12396 cell_area->x, cell_area->y + diff/2,
12397 cell_area->width, requisition.height);
12401 pspp_sheet_view_put (tree_view,
12402 GTK_WIDGET (cell_editable),
12403 cell_area->x, cell_area->y,
12404 cell_area->width, cell_area->height);
12407 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12408 (GdkEvent *)event);
12410 gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12411 g_signal_connect (cell_editable, "remove-widget",
12412 G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12413 if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12414 GTK_IS_BUTTON (cell_editable))
12416 g_signal_connect (cell_editable, "button-press-event",
12417 G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12419 g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12420 GINT_TO_POINTER (row));
12421 g_signal_connect (cell_editable, "clicked",
12422 G_CALLBACK (pspp_sheet_view_editable_clicked),
12426 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12431 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12432 gboolean cancel_editing)
12434 PsppSheetViewColumn *column;
12435 GtkCellRenderer *cell;
12437 if (tree_view->priv->edited_column == NULL)
12441 * This is very evil. We need to do this, because
12442 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12443 * later on. If pspp_sheet_view_row_changed notices
12444 * tree_view->priv->edited_column != NULL, it'll call
12445 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12447 * Please read that again if you intend to modify anything here.
12450 column = tree_view->priv->edited_column;
12451 tree_view->priv->edited_column = NULL;
12453 cell = _pspp_sheet_view_column_get_edited_cell (column);
12454 gtk_cell_renderer_stop_editing (cell, cancel_editing);
12456 if (!cancel_editing)
12457 gtk_cell_editable_editing_done (column->editable_widget);
12459 tree_view->priv->edited_column = column;
12461 gtk_cell_editable_remove_widget (column->editable_widget);
12466 * pspp_sheet_view_set_hover_selection:
12467 * @tree_view: a #PsppSheetView
12468 * @hover: %TRUE to enable hover selection mode
12470 * Enables of disables the hover selection mode of @tree_view.
12471 * Hover selection makes the selected row follow the pointer.
12472 * Currently, this works only for the selection modes
12473 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12478 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12481 hover = hover != FALSE;
12483 if (hover != tree_view->priv->hover_selection)
12485 tree_view->priv->hover_selection = hover;
12487 g_object_notify (G_OBJECT (tree_view), "hover-selection");
12492 * pspp_sheet_view_get_hover_selection:
12493 * @tree_view: a #PsppSheetView
12495 * Returns whether hover selection mode is turned on for @tree_view.
12497 * Return value: %TRUE if @tree_view is in hover selection mode
12502 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12504 return tree_view->priv->hover_selection;
12508 * pspp_sheet_view_set_rubber_banding:
12509 * @tree_view: a #PsppSheetView
12510 * @enable: %TRUE to enable rubber banding
12512 * Enables or disables rubber banding in @tree_view. If the selection mode is
12513 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12514 * banding will allow the user to select multiple rows by dragging the mouse.
12519 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12522 enable = enable != FALSE;
12524 if (enable != tree_view->priv->rubber_banding_enable)
12526 tree_view->priv->rubber_banding_enable = enable;
12528 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12533 * pspp_sheet_view_get_rubber_banding:
12534 * @tree_view: a #PsppSheetView
12536 * Returns whether rubber banding is turned on for @tree_view. If the
12537 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12538 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12539 * select multiple rows by dragging the mouse.
12541 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12546 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12548 return tree_view->priv->rubber_banding_enable;
12552 * pspp_sheet_view_is_rubber_banding_active:
12553 * @tree_view: a #PsppSheetView
12555 * Returns whether a rubber banding operation is currently being done
12558 * Return value: %TRUE if a rubber banding operation is currently being
12559 * done in @tree_view.
12564 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12566 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12568 if (tree_view->priv->rubber_banding_enable
12569 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12576 pspp_sheet_view_grab_notify (GtkWidget *widget,
12577 gboolean was_grabbed)
12579 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12581 tree_view->priv->in_grab = !was_grabbed;
12585 tree_view->priv->pressed_button = -1;
12587 if (tree_view->priv->rubber_band_status)
12588 pspp_sheet_view_stop_rubber_band (tree_view);
12593 pspp_sheet_view_state_changed (GtkWidget *widget,
12594 GtkStateType previous_state)
12596 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12598 if (gtk_widget_get_realized (widget))
12600 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
12601 gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
12604 gtk_widget_queue_draw (widget);
12608 * pspp_sheet_view_get_grid_lines:
12609 * @tree_view: a #PsppSheetView
12611 * Returns which grid lines are enabled in @tree_view.
12613 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12618 PsppSheetViewGridLines
12619 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12621 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12623 return tree_view->priv->grid_lines;
12627 * pspp_sheet_view_set_grid_lines:
12628 * @tree_view: a #PsppSheetView
12629 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12632 * Sets which grid lines to draw in @tree_view.
12637 pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view,
12638 PsppSheetViewGridLines grid_lines)
12640 PsppSheetViewPrivate *priv;
12641 PsppSheetViewGridLines old_grid_lines;
12643 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12645 priv = tree_view->priv;
12647 old_grid_lines = priv->grid_lines;
12648 priv->grid_lines = grid_lines;
12650 if (old_grid_lines != grid_lines)
12652 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12654 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12659 * pspp_sheet_view_get_special_cells:
12660 * @tree_view: a #PsppSheetView
12662 * Returns which grid lines are enabled in @tree_view.
12664 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12665 * the sheet view contain special cells.
12667 PsppSheetViewSpecialCells
12668 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12670 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12672 return tree_view->priv->special_cells;
12676 * pspp_sheet_view_set_special_cells:
12677 * @tree_view: a #PsppSheetView
12678 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12679 * the sheet view contain special cells.
12681 * Sets whether rows in the sheet view contain special cells, controlling the
12682 * rendering of row selections.
12685 pspp_sheet_view_set_special_cells (PsppSheetView *tree_view,
12686 PsppSheetViewSpecialCells special_cells)
12688 PsppSheetViewPrivate *priv;
12690 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12692 priv = tree_view->priv;
12694 if (priv->special_cells != special_cells)
12696 priv->special_cells = special_cells;
12697 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12698 g_object_notify (G_OBJECT (tree_view), "special-cells");
12703 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12705 /* XXX (re)calculate fixed_height if necessary */
12706 return tree_view->priv->fixed_height;
12710 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
12713 g_return_if_fail (fixed_height > 0);
12715 if (tree_view->priv->fixed_height != fixed_height)
12717 tree_view->priv->fixed_height = fixed_height;
12718 g_object_notify (G_OBJECT (tree_view), "fixed-height");
12720 if (!tree_view->priv->fixed_height_set)
12722 tree_view->priv->fixed_height_set = TRUE;
12723 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
12728 * pspp_sheet_view_set_tooltip_row:
12729 * @tree_view: a #PsppSheetView
12730 * @tooltip: a #GtkTooltip
12731 * @path: a #GtkTreePath
12733 * Sets the tip area of @tooltip to be the area covered by the row at @path.
12734 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12735 * See also gtk_tooltip_set_tip_area().
12740 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
12741 GtkTooltip *tooltip,
12744 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12745 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12747 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
12751 * pspp_sheet_view_set_tooltip_cell:
12752 * @tree_view: a #PsppSheetView
12753 * @tooltip: a #GtkTooltip
12754 * @path: (allow-none): a #GtkTreePath or %NULL
12755 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
12756 * @cell: (allow-none): a #GtkCellRenderer or %NULL
12758 * Sets the tip area of @tooltip to the area @path, @column and @cell have
12759 * in common. For example if @path is %NULL and @column is set, the tip
12760 * area will be set to the full area covered by @column. See also
12761 * gtk_tooltip_set_tip_area().
12763 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12768 pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view,
12769 GtkTooltip *tooltip,
12771 PsppSheetViewColumn *column,
12772 GtkCellRenderer *cell)
12776 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12777 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12778 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
12779 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
12781 /* Determine x values. */
12782 if (column && cell)
12787 pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
12788 pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
12790 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12793 rect.width = width;
12799 pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
12800 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12803 rect.width = tmp.width;
12808 rect.width = GTK_WIDGET (tree_view)->allocation.width;
12811 /* Determine y values. */
12816 pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
12817 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12820 rect.height = tmp.height;
12825 rect.height = tree_view->priv->vadjustment->page_size;
12828 gtk_tooltip_set_tip_area (tooltip, &rect);
12832 * pspp_sheet_view_get_tooltip_context:
12833 * @tree_view: a #PsppSheetView
12834 * @x: the x coordinate (relative to widget coordinates)
12835 * @y: the y coordinate (relative to widget coordinates)
12836 * @keyboard_tip: whether this is a keyboard tooltip or not
12837 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
12838 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
12839 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
12841 * This function is supposed to be used in a #GtkWidget::query-tooltip
12842 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
12843 * which are received in the signal handler, should be passed to this
12844 * function without modification.
12846 * The return value indicates whether there is a tree view row at the given
12847 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
12848 * tooltips the row returned will be the cursor row. When %TRUE, then any of
12849 * @model, @path and @iter which have been provided will be set to point to
12850 * that row and the corresponding model. @x and @y will always be converted
12851 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
12853 * Return value: whether or not the given tooltip context points to a row.
12858 pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view,
12861 gboolean keyboard_tip,
12862 GtkTreeModel **model,
12863 GtkTreePath **path,
12866 GtkTreePath *tmppath = NULL;
12868 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12869 g_return_val_if_fail (x != NULL, FALSE);
12870 g_return_val_if_fail (y != NULL, FALSE);
12874 pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
12881 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
12884 if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
12885 &tmppath, NULL, NULL, NULL))
12890 *model = pspp_sheet_view_get_model (tree_view);
12893 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
12899 gtk_tree_path_free (tmppath);
12905 pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget,
12908 gboolean keyboard_tip,
12909 GtkTooltip *tooltip,
12912 GValue value = { 0, };
12913 GValue transformed = { 0, };
12916 GtkTreeModel *model;
12917 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12919 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
12922 &model, &path, &iter))
12925 gtk_tree_model_get_value (model, &iter,
12926 tree_view->priv->tooltip_column, &value);
12928 g_value_init (&transformed, G_TYPE_STRING);
12930 if (!g_value_transform (&value, &transformed))
12932 g_value_unset (&value);
12933 gtk_tree_path_free (path);
12938 g_value_unset (&value);
12940 if (!g_value_get_string (&transformed))
12942 g_value_unset (&transformed);
12943 gtk_tree_path_free (path);
12948 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
12949 pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
12951 gtk_tree_path_free (path);
12952 g_value_unset (&transformed);
12958 * pspp_sheet_view_set_tooltip_column:
12959 * @tree_view: a #PsppSheetView
12960 * @column: an integer, which is a valid column number for @tree_view's model
12962 * If you only plan to have simple (text-only) tooltips on full rows, you
12963 * can use this function to have #PsppSheetView handle these automatically
12964 * for you. @column should be set to the column in @tree_view's model
12965 * containing the tooltip texts, or -1 to disable this feature.
12967 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
12968 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
12970 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
12971 * so &, <, etc have to be escaped in the text.
12976 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
12979 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12981 if (column == tree_view->priv->tooltip_column)
12986 g_signal_handlers_disconnect_by_func (tree_view,
12987 pspp_sheet_view_set_tooltip_query_cb,
12989 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
12993 if (tree_view->priv->tooltip_column == -1)
12995 g_signal_connect (tree_view, "query-tooltip",
12996 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
12997 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13001 tree_view->priv->tooltip_column = column;
13002 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13006 * pspp_sheet_view_get_tooltip_column:
13007 * @tree_view: a #PsppSheetView
13009 * Returns the column of @tree_view's model which is being used for
13010 * displaying tooltips on @tree_view's rows.
13012 * Return value: the index of the tooltip column that is currently being
13013 * used, or -1 if this is disabled.
13018 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13020 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13022 return tree_view->priv->tooltip_column;
13026 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13027 GValue *return_accu,
13028 const GValue *handler_return,
13031 gboolean continue_emission;
13032 gboolean signal_handled;
13034 signal_handled = g_value_get_boolean (handler_return);
13035 g_value_set_boolean (return_accu, signal_handled);
13036 continue_emission = !signal_handled;
13038 return continue_emission;
13042 pspp_sheet_view_grid_lines_get_type (void)
13044 static GType etype = 0;
13045 if (G_UNLIKELY(etype == 0)) {
13046 static const GEnumValue values[] = {
13047 { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13048 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13049 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13050 { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13053 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13059 pspp_sheet_view_special_cells_get_type (void)
13061 static GType etype = 0;
13062 if (G_UNLIKELY(etype == 0)) {
13063 static const GEnumValue values[] = {
13064 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13065 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13066 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13069 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);