1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012, 2013 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Library General Public
22 * License as published by the Free Software Foundation; either
23 * version 2 of the License, or (at your option) any later version.
25 * This library is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Library General Public License for more details.
30 * You should have received a copy of the GNU Library General Public
31 * License along with this library; if not, write to the
32 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
33 * Boston, MA 02111-1307, USA.
38 #include "ui/gui/pspp-sheet-private.h"
42 #include <gdk/gdkkeysyms.h>
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 PsppSheetSelectMode mode);
261 static gboolean pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view);
262 static void pspp_sheet_view_row_changed (GtkTreeModel *model,
266 static void pspp_sheet_view_row_inserted (GtkTreeModel *model,
270 static void pspp_sheet_view_row_deleted (GtkTreeModel *model,
273 static void pspp_sheet_view_rows_reordered (GtkTreeModel *model,
279 /* Incremental reflow */
280 static gint validate_row (PsppSheetView *tree_view,
284 static void validate_visible_area (PsppSheetView *tree_view);
285 static gboolean validate_rows_handler (PsppSheetView *tree_view);
286 static gboolean presize_handler_callback (gpointer data);
287 static void install_presize_handler (PsppSheetView *tree_view);
288 static void install_scroll_sync_handler (PsppSheetView *tree_view);
289 static void pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
292 static void pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view);
293 static void pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view);
294 static void invalidate_empty_focus (PsppSheetView *tree_view);
296 /* Internal functions */
297 static void pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
300 gboolean add_shifted_binding,
301 GtkMovementStep step,
303 static void pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
305 const GdkRectangle *clip_rect);
306 static gint pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
309 static void pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
310 PsppSheetView *tree_view);
311 static void pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
313 static void pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
314 PsppSheetViewColumn *column,
315 gboolean focus_to_cell);
316 static gboolean pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
317 GdkEventMotion *event);
318 static void pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view);
319 static gboolean pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
321 PsppSheetSelectMode mode);
322 static void pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
324 PsppSheetSelectMode mode);
325 static void pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
327 PsppSheetSelectMode mode);
328 static void pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
330 PsppSheetSelectMode mode);
331 static void pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
333 static void pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
335 PsppSheetSelectMode mode);
336 static void pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
338 gboolean clear_and_select,
340 PsppSheetSelectMode mode);
341 static gboolean pspp_sheet_view_has_special_cell (PsppSheetView *tree_view);
342 static void pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view);
343 static void update_prelight (PsppSheetView *tree_view,
346 static void initialize_fixed_height_mode (PsppSheetView *tree_view);
348 /* interactive search */
349 static void pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view);
350 static void pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
351 PsppSheetView *tree_view);
352 static void pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
353 GtkWidget *search_dialog,
355 static void pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
358 static void pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
359 PsppSheetView *tree_view);
360 static void pspp_sheet_view_search_activate (GtkEntry *entry,
361 PsppSheetView *tree_view);
362 static gboolean pspp_sheet_view_real_search_enable_popdown(gpointer data);
363 static void pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
365 static gboolean pspp_sheet_view_search_delete_event (GtkWidget *widget,
367 PsppSheetView *tree_view);
368 static gboolean pspp_sheet_view_search_button_press_event (GtkWidget *widget,
369 GdkEventButton *event,
370 PsppSheetView *tree_view);
371 static gboolean pspp_sheet_view_search_scroll_event (GtkWidget *entry,
372 GdkEventScroll *event,
373 PsppSheetView *tree_view);
374 static gboolean pspp_sheet_view_search_key_press_event (GtkWidget *entry,
376 PsppSheetView *tree_view);
377 static gboolean pspp_sheet_view_search_move (GtkWidget *window,
378 PsppSheetView *tree_view,
380 static gboolean pspp_sheet_view_search_equal_func (GtkTreeModel *model,
384 gpointer search_data);
385 static gboolean pspp_sheet_view_search_iter (GtkTreeModel *model,
386 PsppSheetSelection *selection,
391 static void pspp_sheet_view_search_init (GtkWidget *entry,
392 PsppSheetView *tree_view);
393 static void pspp_sheet_view_put (PsppSheetView *tree_view,
394 GtkWidget *child_widget,
399 static gboolean pspp_sheet_view_start_editing (PsppSheetView *tree_view,
400 GtkTreePath *cursor_path);
401 static gboolean pspp_sheet_view_editable_button_press_event (GtkWidget *,
404 static void pspp_sheet_view_editable_clicked (GtkButton *, PsppSheetView *);
405 static void pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
406 PsppSheetViewColumn *column,
408 GtkCellEditable *cell_editable,
409 GdkRectangle *cell_area,
412 static gboolean pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
413 gboolean keybinding);
414 static gboolean pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view);
415 static PsppSheetViewColumn *pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
416 PsppSheetViewColumn *column,
419 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
420 PsppSheetViewColumn *column,
421 const GdkRectangle *background_area,
422 gboolean subtract_focus_rect,
423 GdkRectangle *cell_area);
424 static gint pspp_sheet_view_find_offset (PsppSheetView *tree_view,
429 static void pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
433 static void pspp_sheet_view_buildable_init (GtkBuildableIface *iface);
436 static gboolean scroll_row_timeout (gpointer data);
437 static void add_scroll_timeout (PsppSheetView *tree_view);
438 static void remove_scroll_timeout (PsppSheetView *tree_view);
440 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
442 static GtkBindingSet *edit_bindings;
449 G_DEFINE_TYPE_WITH_CODE (PsppSheetView, pspp_sheet_view, GTK_TYPE_CONTAINER,
450 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
451 pspp_sheet_view_buildable_init))
454 pspp_sheet_view_class_init (PsppSheetViewClass *class)
456 GObjectClass *o_class;
457 GtkWidgetClass *widget_class;
458 GtkContainerClass *container_class;
459 GtkBindingSet *binding_set[2];
462 binding_set[0] = gtk_binding_set_by_class (class);
464 binding_set[1] = gtk_binding_set_new ("PsppSheetViewEditing");
465 edit_bindings = binding_set[1];
467 o_class = (GObjectClass *) class;
468 widget_class = (GtkWidgetClass *) class;
469 container_class = (GtkContainerClass *) class;
471 /* GObject signals */
472 o_class->set_property = pspp_sheet_view_set_property;
473 o_class->get_property = pspp_sheet_view_get_property;
474 o_class->finalize = pspp_sheet_view_finalize;
475 o_class->dispose = pspp_sheet_view_dispose;
477 /* GtkWidget signals */
478 widget_class->map = pspp_sheet_view_map;
479 widget_class->realize = pspp_sheet_view_realize;
480 widget_class->unrealize = pspp_sheet_view_unrealize;
481 widget_class->size_request = pspp_sheet_view_size_request;
482 widget_class->size_allocate = pspp_sheet_view_size_allocate;
483 widget_class->button_press_event = pspp_sheet_view_button_press;
484 widget_class->button_release_event = pspp_sheet_view_button_release;
485 widget_class->grab_broken_event = pspp_sheet_view_grab_broken;
486 /*widget_class->configure_event = pspp_sheet_view_configure;*/
487 widget_class->motion_notify_event = pspp_sheet_view_motion;
488 widget_class->expose_event = pspp_sheet_view_expose;
489 widget_class->key_press_event = pspp_sheet_view_key_press;
490 widget_class->key_release_event = pspp_sheet_view_key_release;
491 widget_class->enter_notify_event = pspp_sheet_view_enter_notify;
492 widget_class->leave_notify_event = pspp_sheet_view_leave_notify;
493 widget_class->focus_out_event = pspp_sheet_view_focus_out;
494 widget_class->drag_begin = pspp_sheet_view_drag_begin;
495 widget_class->drag_end = pspp_sheet_view_drag_end;
496 widget_class->drag_data_get = pspp_sheet_view_drag_data_get;
497 widget_class->drag_data_delete = pspp_sheet_view_drag_data_delete;
498 widget_class->drag_leave = pspp_sheet_view_drag_leave;
499 widget_class->drag_motion = pspp_sheet_view_drag_motion;
500 widget_class->drag_drop = pspp_sheet_view_drag_drop;
501 widget_class->drag_data_received = pspp_sheet_view_drag_data_received;
502 widget_class->focus = pspp_sheet_view_focus;
503 widget_class->grab_focus = pspp_sheet_view_grab_focus;
504 widget_class->style_set = pspp_sheet_view_style_set;
505 widget_class->grab_notify = pspp_sheet_view_grab_notify;
506 widget_class->state_changed = pspp_sheet_view_state_changed;
508 /* GtkContainer signals */
509 container_class->remove = pspp_sheet_view_remove;
510 container_class->forall = pspp_sheet_view_forall;
511 container_class->set_focus_child = pspp_sheet_view_set_focus_child;
513 class->set_scroll_adjustments = pspp_sheet_view_set_adjustments;
514 class->move_cursor = pspp_sheet_view_real_move_cursor;
515 class->select_all = pspp_sheet_view_real_select_all;
516 class->unselect_all = pspp_sheet_view_real_unselect_all;
517 class->select_cursor_row = pspp_sheet_view_real_select_cursor_row;
518 class->toggle_cursor_row = pspp_sheet_view_real_toggle_cursor_row;
519 class->start_interactive_search = pspp_sheet_view_start_interactive_search;
523 g_object_class_install_property (o_class,
525 g_param_spec_object ("model",
526 P_("TreeView Model"),
527 P_("The model for the tree view"),
529 GTK_PARAM_READWRITE));
531 g_object_class_install_property (o_class,
533 g_param_spec_object ("hadjustment",
534 P_("Horizontal Adjustment"),
535 P_("Horizontal Adjustment for the widget"),
537 GTK_PARAM_READWRITE));
539 g_object_class_install_property (o_class,
541 g_param_spec_object ("vadjustment",
542 P_("Vertical Adjustment"),
543 P_("Vertical Adjustment for the widget"),
545 GTK_PARAM_READWRITE));
547 g_object_class_install_property (o_class,
548 PROP_HEADERS_VISIBLE,
549 g_param_spec_boolean ("headers-visible",
550 P_("Headers Visible"),
551 P_("Show the column header buttons"),
553 GTK_PARAM_READWRITE));
555 g_object_class_install_property (o_class,
556 PROP_HEADERS_CLICKABLE,
557 g_param_spec_boolean ("headers-clickable",
558 P_("Headers Clickable"),
559 P_("Column headers respond to click events"),
561 GTK_PARAM_READWRITE));
563 g_object_class_install_property (o_class,
565 g_param_spec_boolean ("reorderable",
567 P_("View is reorderable"),
569 GTK_PARAM_READWRITE));
571 g_object_class_install_property (o_class,
573 g_param_spec_boolean ("rules-hint",
575 P_("Set a hint to the theme engine to draw rows in alternating colors"),
577 GTK_PARAM_READWRITE));
579 g_object_class_install_property (o_class,
581 g_param_spec_boolean ("enable-search",
583 P_("View allows user to search through columns interactively"),
585 GTK_PARAM_READWRITE));
587 g_object_class_install_property (o_class,
589 g_param_spec_int ("search-column",
591 P_("Model column to search through during interactive search"),
595 GTK_PARAM_READWRITE));
598 * PsppSheetView:hover-selection:
600 * Enables of disables the hover selection mode of @tree_view.
601 * Hover selection makes the selected row follow the pointer.
602 * Currently, this works only for the selection modes
603 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
605 * This mode is primarily intended for treeviews in popups, e.g.
606 * in #GtkComboBox or #GtkEntryCompletion.
610 g_object_class_install_property (o_class,
611 PROP_HOVER_SELECTION,
612 g_param_spec_boolean ("hover-selection",
613 P_("Hover Selection"),
614 P_("Whether the selection should follow the pointer"),
616 GTK_PARAM_READWRITE));
618 g_object_class_install_property (o_class,
620 g_param_spec_boolean ("rubber-banding",
621 P_("Rubber Banding"),
622 P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
624 GTK_PARAM_READWRITE));
626 g_object_class_install_property (o_class,
627 PROP_ENABLE_GRID_LINES,
628 g_param_spec_enum ("enable-grid-lines",
629 P_("Enable Grid Lines"),
630 P_("Whether grid lines should be drawn in the tree view"),
631 PSPP_TYPE_SHEET_VIEW_GRID_LINES,
632 PSPP_SHEET_VIEW_GRID_LINES_NONE,
633 GTK_PARAM_READWRITE));
635 g_object_class_install_property (o_class,
637 g_param_spec_int ("tooltip-column",
638 P_("Tooltip Column"),
639 P_("The column in the model containing the tooltip texts for the rows"),
643 GTK_PARAM_READWRITE));
645 g_object_class_install_property (o_class,
647 g_param_spec_enum ("special-cells",
649 P_("Whether rows have special cells."),
650 PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS,
651 PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT,
652 GTK_PARAM_READWRITE));
654 g_object_class_install_property (o_class,
656 g_param_spec_int ("fixed-height",
658 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."),
662 GTK_PARAM_READWRITE));
664 g_object_class_install_property (o_class,
665 PROP_FIXED_HEIGHT_SET,
666 g_param_spec_boolean ("fixed-height-set",
667 P_("Fixed Height Set"),
668 P_("Whether fixed-height was set externally."),
670 GTK_PARAM_READWRITE));
672 /* Style properties */
673 #define _TREE_VIEW_EXPANDER_SIZE 12
674 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
675 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
677 gtk_widget_class_install_style_property (widget_class,
678 g_param_spec_int ("expander-size",
680 P_("Size of the expander arrow"),
683 _TREE_VIEW_EXPANDER_SIZE,
684 GTK_PARAM_READABLE));
686 gtk_widget_class_install_style_property (widget_class,
687 g_param_spec_int ("vertical-separator",
688 P_("Vertical Separator Width"),
689 P_("Vertical space between cells. Must be an even number"),
692 _TREE_VIEW_VERTICAL_SEPARATOR,
693 GTK_PARAM_READABLE));
695 gtk_widget_class_install_style_property (widget_class,
696 g_param_spec_int ("horizontal-separator",
697 P_("Horizontal Separator Width"),
698 P_("Horizontal space between cells. Must be an even number"),
701 _TREE_VIEW_HORIZONTAL_SEPARATOR,
702 GTK_PARAM_READABLE));
704 gtk_widget_class_install_style_property (widget_class,
705 g_param_spec_boolean ("allow-rules",
707 P_("Allow drawing of alternating color rows"),
709 GTK_PARAM_READABLE));
711 gtk_widget_class_install_style_property (widget_class,
712 g_param_spec_boxed ("even-row-color",
713 P_("Even Row Color"),
714 P_("Color to use for even rows"),
716 GTK_PARAM_READABLE));
718 gtk_widget_class_install_style_property (widget_class,
719 g_param_spec_boxed ("odd-row-color",
721 P_("Color to use for odd rows"),
723 GTK_PARAM_READABLE));
725 gtk_widget_class_install_style_property (widget_class,
726 g_param_spec_boolean ("row-ending-details",
727 P_("Row Ending details"),
728 P_("Enable extended row background theming"),
730 GTK_PARAM_READABLE));
732 gtk_widget_class_install_style_property (widget_class,
733 g_param_spec_int ("grid-line-width",
734 P_("Grid line width"),
735 P_("Width, in pixels, of the tree view grid lines"),
737 GTK_PARAM_READABLE));
739 gtk_widget_class_install_style_property (widget_class,
740 g_param_spec_int ("tree-line-width",
741 P_("Tree line width"),
742 P_("Width, in pixels, of the tree view lines"),
744 GTK_PARAM_READABLE));
746 gtk_widget_class_install_style_property (widget_class,
747 g_param_spec_string ("tree-line-pattern",
748 P_("Tree line pattern"),
749 P_("Dash pattern used to draw the tree view lines"),
751 GTK_PARAM_READABLE));
755 * PsppSheetView::set-scroll-adjustments
756 * @horizontal: the horizontal #GtkAdjustment
757 * @vertical: the vertical #GtkAdjustment
759 * Set the scroll adjustments for the tree view. Usually scrolled containers
760 * like #GtkScrolledWindow will emit this signal to connect two instances
761 * of #GtkScrollbar to the scroll directions of the #PsppSheetView.
763 widget_class->set_scroll_adjustments_signal =
764 g_signal_new ("set-scroll-adjustments",
765 G_TYPE_FROM_CLASS (o_class),
766 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
767 G_STRUCT_OFFSET (PsppSheetViewClass, set_scroll_adjustments),
769 psppire_marshal_VOID__OBJECT_OBJECT,
772 GTK_TYPE_ADJUSTMENT);
775 * PsppSheetView::row-activated:
776 * @tree_view: the object on which the signal is emitted
777 * @path: the #GtkTreePath for the activated row
778 * @column: the #PsppSheetViewColumn in which the activation occurred
780 * The "row-activated" signal is emitted when the method
781 * pspp_sheet_view_row_activated() is called or the user double clicks
782 * a treeview row. It is also emitted when a non-editable row is
783 * selected and one of the keys: Space, Shift+Space, Return or
786 * For selection handling refer to the <link linkend="TreeWidget">tree
787 * widget conceptual overview</link> as well as #PsppSheetSelection.
789 tree_view_signals[ROW_ACTIVATED] =
790 g_signal_new ("row-activated",
791 G_TYPE_FROM_CLASS (o_class),
792 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
793 G_STRUCT_OFFSET (PsppSheetViewClass, row_activated),
795 psppire_marshal_VOID__BOXED_OBJECT,
798 PSPP_TYPE_SHEET_VIEW_COLUMN);
801 * PsppSheetView::columns-changed:
802 * @tree_view: the object on which the signal is emitted
804 * The number of columns of the treeview has changed.
806 tree_view_signals[COLUMNS_CHANGED] =
807 g_signal_new ("columns-changed",
808 G_TYPE_FROM_CLASS (o_class),
810 G_STRUCT_OFFSET (PsppSheetViewClass, columns_changed),
812 g_cclosure_marshal_VOID__VOID,
816 * PsppSheetView::cursor-changed:
817 * @tree_view: the object on which the signal is emitted
819 * The position of the cursor (focused cell) has changed.
821 tree_view_signals[CURSOR_CHANGED] =
822 g_signal_new ("cursor-changed",
823 G_TYPE_FROM_CLASS (o_class),
825 G_STRUCT_OFFSET (PsppSheetViewClass, cursor_changed),
827 g_cclosure_marshal_VOID__VOID,
830 tree_view_signals[MOVE_CURSOR] =
831 g_signal_new ("move-cursor",
832 G_TYPE_FROM_CLASS (o_class),
833 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
834 G_STRUCT_OFFSET (PsppSheetViewClass, move_cursor),
836 psppire_marshal_BOOLEAN__ENUM_INT,
838 GTK_TYPE_MOVEMENT_STEP,
841 tree_view_signals[SELECT_ALL] =
842 g_signal_new ("select-all",
843 G_TYPE_FROM_CLASS (o_class),
844 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
845 G_STRUCT_OFFSET (PsppSheetViewClass, select_all),
847 psppire_marshal_BOOLEAN__VOID,
850 tree_view_signals[UNSELECT_ALL] =
851 g_signal_new ("unselect-all",
852 G_TYPE_FROM_CLASS (o_class),
853 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
854 G_STRUCT_OFFSET (PsppSheetViewClass, unselect_all),
856 psppire_marshal_BOOLEAN__VOID,
859 tree_view_signals[SELECT_CURSOR_ROW] =
860 g_signal_new ("select-cursor-row",
861 G_TYPE_FROM_CLASS (o_class),
862 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
863 G_STRUCT_OFFSET (PsppSheetViewClass, select_cursor_row),
865 psppire_marshal_BOOLEAN__BOOLEAN,
867 G_TYPE_BOOLEAN, G_TYPE_INT);
869 tree_view_signals[TOGGLE_CURSOR_ROW] =
870 g_signal_new ("toggle-cursor-row",
871 G_TYPE_FROM_CLASS (o_class),
872 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
873 G_STRUCT_OFFSET (PsppSheetViewClass, toggle_cursor_row),
875 psppire_marshal_BOOLEAN__VOID,
878 tree_view_signals[START_INTERACTIVE_SEARCH] =
879 g_signal_new ("start-interactive-search",
880 G_TYPE_FROM_CLASS (o_class),
881 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
882 G_STRUCT_OFFSET (PsppSheetViewClass, start_interactive_search),
884 psppire_marshal_BOOLEAN__VOID,
888 for (i = 0; i < 2; i++)
890 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Up, 0, TRUE,
891 GTK_MOVEMENT_DISPLAY_LINES, -1);
892 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Up, 0, TRUE,
893 GTK_MOVEMENT_DISPLAY_LINES, -1);
895 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Down, 0, TRUE,
896 GTK_MOVEMENT_DISPLAY_LINES, 1);
897 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Down, 0, TRUE,
898 GTK_MOVEMENT_DISPLAY_LINES, 1);
900 pspp_sheet_view_add_move_binding (binding_set[i], GDK_p, GDK_CONTROL_MASK, FALSE,
901 GTK_MOVEMENT_DISPLAY_LINES, -1);
903 pspp_sheet_view_add_move_binding (binding_set[i], GDK_n, GDK_CONTROL_MASK, FALSE,
904 GTK_MOVEMENT_DISPLAY_LINES, 1);
906 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Home, 0, TRUE,
907 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
908 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Home, 0, TRUE,
909 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
911 pspp_sheet_view_add_move_binding (binding_set[i], GDK_End, 0, TRUE,
912 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
913 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_End, 0, TRUE,
914 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
916 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Up, 0, TRUE,
917 GTK_MOVEMENT_PAGES, -1);
918 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Up, 0, TRUE,
919 GTK_MOVEMENT_PAGES, -1);
921 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Down, 0, TRUE,
922 GTK_MOVEMENT_PAGES, 1);
923 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Down, 0, TRUE,
924 GTK_MOVEMENT_PAGES, 1);
927 gtk_binding_entry_add_signal (binding_set[i], GDK_Up, GDK_CONTROL_MASK, "move-cursor", 2,
928 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
931 gtk_binding_entry_add_signal (binding_set[i], GDK_Down, GDK_CONTROL_MASK, "move-cursor", 2,
932 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
935 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, 0, "move-cursor", 2,
936 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
939 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, 0, "move-cursor", 2,
940 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
943 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, 0, "move-cursor", 2,
944 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
947 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, GDK_SHIFT_MASK, "move-cursor", 2,
948 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
951 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, 0, "move-cursor", 2,
952 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
955 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, 0, "move-cursor", 2,
956 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
959 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, GDK_CONTROL_MASK,
961 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
964 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, GDK_CONTROL_MASK,
966 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
969 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, GDK_CONTROL_MASK,
971 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
974 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, GDK_CONTROL_MASK,
976 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
979 gtk_binding_entry_add_signal (binding_set[i], GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
981 gtk_binding_entry_add_signal (binding_set[i], GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
984 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
985 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
987 gtk_binding_entry_add_signal (binding_set[0], GDK_a, GDK_CONTROL_MASK, "select-all", 0);
988 gtk_binding_entry_add_signal (binding_set[0], GDK_slash, GDK_CONTROL_MASK, "select-all", 0);
990 gtk_binding_entry_add_signal (binding_set[0], GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
991 gtk_binding_entry_add_signal (binding_set[0], GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0);
993 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1,
994 G_TYPE_BOOLEAN, TRUE,
995 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
996 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1,
997 G_TYPE_BOOLEAN, TRUE,
998 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1000 gtk_binding_entry_add_signal (binding_set[0], GDK_space, 0, "select-cursor-row", 1,
1001 G_TYPE_BOOLEAN, TRUE,
1003 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, 0, "select-cursor-row", 1,
1004 G_TYPE_BOOLEAN, TRUE,
1006 gtk_binding_entry_add_signal (binding_set[0], GDK_Return, 0, "select-cursor-row", 1,
1007 G_TYPE_BOOLEAN, TRUE,
1009 gtk_binding_entry_add_signal (binding_set[0], GDK_ISO_Enter, 0, "select-cursor-row", 1,
1010 G_TYPE_BOOLEAN, TRUE,
1012 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Enter, 0, "select-cursor-row", 1,
1013 G_TYPE_BOOLEAN, TRUE,
1016 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, 0, "select-cursor-parent", 0);
1017 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0);
1019 g_type_class_add_private (o_class, sizeof (PsppSheetViewPrivate));
1023 pspp_sheet_view_buildable_init (GtkBuildableIface *iface)
1025 iface->add_child = pspp_sheet_view_buildable_add_child;
1029 pspp_sheet_view_init (PsppSheetView *tree_view)
1031 tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, PSPP_TYPE_SHEET_VIEW, PsppSheetViewPrivate);
1033 gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
1034 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE);
1036 tree_view->priv->flags = PSPP_SHEET_VIEW_DRAW_KEYFOCUS
1037 | PSPP_SHEET_VIEW_HEADERS_VISIBLE;
1039 /* We need some padding */
1040 tree_view->priv->selected = range_tower_create ();
1041 tree_view->priv->dy = 0;
1042 tree_view->priv->cursor_offset = 0;
1043 tree_view->priv->n_columns = 0;
1044 tree_view->priv->header_height = 1;
1045 tree_view->priv->x_drag = 0;
1046 tree_view->priv->drag_pos = -1;
1047 tree_view->priv->header_has_focus = FALSE;
1048 tree_view->priv->pressed_button = -1;
1049 tree_view->priv->press_start_x = -1;
1050 tree_view->priv->press_start_y = -1;
1051 tree_view->priv->reorderable = FALSE;
1052 tree_view->priv->presize_handler_timer = 0;
1053 tree_view->priv->scroll_sync_timer = 0;
1054 tree_view->priv->fixed_height = -1;
1055 tree_view->priv->fixed_height_set = FALSE;
1056 pspp_sheet_view_set_adjustments (tree_view, NULL, NULL);
1057 tree_view->priv->selection = _pspp_sheet_selection_new_with_tree_view (tree_view);
1058 tree_view->priv->enable_search = TRUE;
1059 tree_view->priv->search_column = -1;
1060 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
1061 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
1062 tree_view->priv->search_custom_entry_set = FALSE;
1063 tree_view->priv->typeselect_flush_timeout = 0;
1064 tree_view->priv->init_hadjust_value = TRUE;
1065 tree_view->priv->width = 0;
1067 tree_view->priv->hover_selection = FALSE;
1069 tree_view->priv->rubber_banding_enable = FALSE;
1071 tree_view->priv->grid_lines = PSPP_SHEET_VIEW_GRID_LINES_NONE;
1073 tree_view->priv->tooltip_column = -1;
1075 tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT;
1077 tree_view->priv->post_validation_flag = FALSE;
1079 tree_view->priv->last_button_x = -1;
1080 tree_view->priv->last_button_y = -1;
1082 tree_view->priv->event_last_x = -10000;
1083 tree_view->priv->event_last_y = -10000;
1085 tree_view->priv->prelight_node = -1;
1086 tree_view->priv->rubber_band_start_node = -1;
1087 tree_view->priv->rubber_band_end_node = -1;
1089 tree_view->priv->anchor_column = NULL;
1091 tree_view->priv->button_style = NULL;
1093 tree_view->dispose_has_run = FALSE;
1102 pspp_sheet_view_set_property (GObject *object,
1104 const GValue *value,
1107 PsppSheetView *tree_view;
1109 tree_view = PSPP_SHEET_VIEW (object);
1114 pspp_sheet_view_set_model (tree_view, g_value_get_object (value));
1116 case PROP_HADJUSTMENT:
1117 pspp_sheet_view_set_hadjustment (tree_view, g_value_get_object (value));
1119 case PROP_VADJUSTMENT:
1120 pspp_sheet_view_set_vadjustment (tree_view, g_value_get_object (value));
1122 case PROP_HEADERS_VISIBLE:
1123 pspp_sheet_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1125 case PROP_HEADERS_CLICKABLE:
1126 pspp_sheet_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1128 case PROP_REORDERABLE:
1129 pspp_sheet_view_set_reorderable (tree_view, g_value_get_boolean (value));
1131 case PROP_RULES_HINT:
1132 pspp_sheet_view_set_rules_hint (tree_view, g_value_get_boolean (value));
1134 case PROP_ENABLE_SEARCH:
1135 pspp_sheet_view_set_enable_search (tree_view, g_value_get_boolean (value));
1137 case PROP_SEARCH_COLUMN:
1138 pspp_sheet_view_set_search_column (tree_view, g_value_get_int (value));
1140 case PROP_HOVER_SELECTION:
1141 tree_view->priv->hover_selection = g_value_get_boolean (value);
1143 case PROP_RUBBER_BANDING:
1144 tree_view->priv->rubber_banding_enable = g_value_get_boolean (value);
1146 case PROP_ENABLE_GRID_LINES:
1147 pspp_sheet_view_set_grid_lines (tree_view, g_value_get_enum (value));
1149 case PROP_TOOLTIP_COLUMN:
1150 pspp_sheet_view_set_tooltip_column (tree_view, g_value_get_int (value));
1152 case PROP_SPECIAL_CELLS:
1153 pspp_sheet_view_set_special_cells (tree_view, g_value_get_enum (value));
1155 case PROP_FIXED_HEIGHT:
1156 pspp_sheet_view_set_fixed_height (tree_view, g_value_get_int (value));
1158 case PROP_FIXED_HEIGHT_SET:
1159 if (g_value_get_boolean (value))
1161 if (!tree_view->priv->fixed_height_set
1162 && tree_view->priv->fixed_height >= 0)
1164 tree_view->priv->fixed_height_set = true;
1165 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1170 if (tree_view->priv->fixed_height_set)
1172 tree_view->priv->fixed_height_set = false;
1173 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1174 install_presize_handler (tree_view);
1179 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1185 pspp_sheet_view_get_property (GObject *object,
1190 PsppSheetView *tree_view;
1192 tree_view = PSPP_SHEET_VIEW (object);
1197 g_value_set_object (value, tree_view->priv->model);
1199 case PROP_HADJUSTMENT:
1200 g_value_set_object (value, tree_view->priv->hadjustment);
1202 case PROP_VADJUSTMENT:
1203 g_value_set_object (value, tree_view->priv->vadjustment);
1205 case PROP_HEADERS_VISIBLE:
1206 g_value_set_boolean (value, pspp_sheet_view_get_headers_visible (tree_view));
1208 case PROP_HEADERS_CLICKABLE:
1209 g_value_set_boolean (value, pspp_sheet_view_get_headers_clickable (tree_view));
1211 case PROP_REORDERABLE:
1212 g_value_set_boolean (value, tree_view->priv->reorderable);
1214 case PROP_RULES_HINT:
1215 g_value_set_boolean (value, tree_view->priv->has_rules);
1217 case PROP_ENABLE_SEARCH:
1218 g_value_set_boolean (value, tree_view->priv->enable_search);
1220 case PROP_SEARCH_COLUMN:
1221 g_value_set_int (value, tree_view->priv->search_column);
1223 case PROP_HOVER_SELECTION:
1224 g_value_set_boolean (value, tree_view->priv->hover_selection);
1226 case PROP_RUBBER_BANDING:
1227 g_value_set_boolean (value, tree_view->priv->rubber_banding_enable);
1229 case PROP_ENABLE_GRID_LINES:
1230 g_value_set_enum (value, tree_view->priv->grid_lines);
1232 case PROP_TOOLTIP_COLUMN:
1233 g_value_set_int (value, tree_view->priv->tooltip_column);
1235 case PROP_SPECIAL_CELLS:
1236 g_value_set_enum (value, tree_view->priv->special_cells);
1238 case PROP_FIXED_HEIGHT:
1239 g_value_set_int (value, pspp_sheet_view_get_fixed_height (tree_view));
1241 case PROP_FIXED_HEIGHT_SET:
1242 g_value_set_boolean (value, tree_view->priv->fixed_height_set);
1245 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1251 pspp_sheet_view_dispose (GObject *object)
1253 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1255 if (tree_view->dispose_has_run)
1258 tree_view->dispose_has_run = TRUE;
1260 if (tree_view->priv->selection != NULL)
1262 _pspp_sheet_selection_set_tree_view (tree_view->priv->selection, NULL);
1263 g_object_unref (tree_view->priv->selection);
1264 tree_view->priv->selection = NULL;
1267 if (tree_view->priv->hadjustment)
1269 g_object_unref (tree_view->priv->hadjustment);
1270 tree_view->priv->hadjustment = NULL;
1272 if (tree_view->priv->vadjustment)
1274 g_object_unref (tree_view->priv->vadjustment);
1275 tree_view->priv->vadjustment = NULL;
1278 if (tree_view->priv->button_style)
1280 g_object_unref (tree_view->priv->button_style);
1281 tree_view->priv->button_style = NULL;
1285 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->dispose (object);
1291 pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
1292 GtkBuilder *builder,
1296 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), PSPP_SHEET_VIEW_COLUMN (child));
1300 pspp_sheet_view_finalize (GObject *object)
1302 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1304 pspp_sheet_view_stop_editing (tree_view, TRUE);
1306 if (tree_view->priv->selected != NULL)
1308 range_tower_destroy (tree_view->priv->selected);
1309 tree_view->priv->selected = NULL;
1313 tree_view->priv->prelight_node = -1;
1316 if (tree_view->priv->scroll_to_path != NULL)
1318 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
1319 tree_view->priv->scroll_to_path = NULL;
1322 if (tree_view->priv->drag_dest_row != NULL)
1324 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
1325 tree_view->priv->drag_dest_row = NULL;
1328 if (tree_view->priv->top_row != NULL)
1330 gtk_tree_row_reference_free (tree_view->priv->top_row);
1331 tree_view->priv->top_row = NULL;
1334 if (tree_view->priv->column_drop_func_data &&
1335 tree_view->priv->column_drop_func_data_destroy)
1337 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
1338 tree_view->priv->column_drop_func_data = NULL;
1341 if (tree_view->priv->destroy_count_destroy &&
1342 tree_view->priv->destroy_count_data)
1344 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
1345 tree_view->priv->destroy_count_data = NULL;
1348 gtk_tree_row_reference_free (tree_view->priv->cursor);
1349 tree_view->priv->cursor = NULL;
1351 gtk_tree_row_reference_free (tree_view->priv->anchor);
1352 tree_view->priv->anchor = NULL;
1354 /* destroy interactive search dialog */
1355 if (tree_view->priv->search_window)
1357 gtk_widget_destroy (tree_view->priv->search_window);
1358 tree_view->priv->search_window = NULL;
1359 tree_view->priv->search_entry = NULL;
1360 if (tree_view->priv->typeselect_flush_timeout)
1362 g_source_remove (tree_view->priv->typeselect_flush_timeout);
1363 tree_view->priv->typeselect_flush_timeout = 0;
1367 if (tree_view->priv->search_destroy && tree_view->priv->search_user_data)
1369 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
1370 tree_view->priv->search_user_data = NULL;
1373 if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data)
1375 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
1376 tree_view->priv->search_position_user_data = NULL;
1379 pspp_sheet_view_set_model (tree_view, NULL);
1382 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->finalize (object);
1387 /* GtkWidget Methods
1390 /* GtkWidget::map helper */
1392 pspp_sheet_view_map_buttons (PsppSheetView *tree_view)
1396 g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
1398 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
1400 PsppSheetViewColumn *column;
1402 for (list = tree_view->priv->columns; list; list = list->next)
1404 column = list->data;
1405 if (column->button != NULL &&
1406 gtk_widget_get_visible (column->button) &&
1407 !gtk_widget_get_mapped (column->button))
1408 gtk_widget_map (column->button);
1410 for (list = tree_view->priv->columns; list; list = list->next)
1412 column = list->data;
1413 if (column->visible == FALSE || column->window == NULL)
1415 if (column->resizable)
1417 gdk_window_raise (column->window);
1418 gdk_window_show (column->window);
1421 gdk_window_hide (column->window);
1423 gdk_window_show (tree_view->priv->header_window);
1428 pspp_sheet_view_map (GtkWidget *widget)
1430 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1433 gtk_widget_set_mapped (widget, TRUE);
1435 tmp_list = tree_view->priv->children;
1438 PsppSheetViewChild *child = tmp_list->data;
1439 tmp_list = tmp_list->next;
1441 if (gtk_widget_get_visible (child->widget))
1443 if (!gtk_widget_get_mapped (child->widget))
1444 gtk_widget_map (child->widget);
1447 gdk_window_show (tree_view->priv->bin_window);
1449 pspp_sheet_view_map_buttons (tree_view);
1451 gdk_window_show (widget->window);
1455 pspp_sheet_view_realize (GtkWidget *widget)
1458 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1460 GdkWindowAttr attributes;
1461 gint attributes_mask;
1463 gtk_widget_set_realized (widget, TRUE);
1465 /* Make the main, clipping window */
1466 attributes.window_type = GDK_WINDOW_CHILD;
1467 attributes.x = widget->allocation.x;
1468 attributes.y = widget->allocation.y;
1469 attributes.width = widget->allocation.width;
1470 attributes.height = widget->allocation.height;
1471 attributes.wclass = GDK_INPUT_OUTPUT;
1472 attributes.visual = gtk_widget_get_visual (widget);
1473 attributes.colormap = gtk_widget_get_colormap (widget);
1474 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1476 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1478 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1479 &attributes, attributes_mask);
1480 gdk_window_set_user_data (widget->window, widget);
1482 /* Make the window for the tree */
1484 attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
1485 attributes.width = MAX (tree_view->priv->width, widget->allocation.width);
1486 attributes.height = widget->allocation.height;
1487 attributes.event_mask = (GDK_EXPOSURE_MASK |
1489 GDK_POINTER_MOTION_MASK |
1490 GDK_ENTER_NOTIFY_MASK |
1491 GDK_LEAVE_NOTIFY_MASK |
1492 GDK_BUTTON_PRESS_MASK |
1493 GDK_BUTTON_RELEASE_MASK |
1494 gtk_widget_get_events (widget));
1496 tree_view->priv->bin_window = gdk_window_new (widget->window,
1497 &attributes, attributes_mask);
1498 gdk_window_set_user_data (tree_view->priv->bin_window, widget);
1500 /* Make the column header window */
1503 attributes.width = MAX (tree_view->priv->width, widget->allocation.width);
1504 attributes.height = tree_view->priv->header_height;
1505 attributes.event_mask = (GDK_EXPOSURE_MASK |
1507 GDK_BUTTON_PRESS_MASK |
1508 GDK_BUTTON_RELEASE_MASK |
1509 GDK_KEY_PRESS_MASK |
1510 GDK_KEY_RELEASE_MASK |
1511 gtk_widget_get_events (widget));
1513 tree_view->priv->header_window = gdk_window_new (widget->window,
1514 &attributes, attributes_mask);
1515 gdk_window_set_user_data (tree_view->priv->header_window, widget);
1517 /* Add them all up. */
1518 widget->style = gtk_style_attach (widget->style, widget->window);
1519 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
1520 gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
1521 gtk_style_set_background (widget->style, tree_view->priv->header_window, GTK_STATE_NORMAL);
1523 tmp_list = tree_view->priv->children;
1526 PsppSheetViewChild *child = tmp_list->data;
1527 tmp_list = tmp_list->next;
1529 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
1532 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
1533 _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list->data));
1535 /* Need to call those here, since they create GCs */
1536 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
1538 install_presize_handler (tree_view);
1540 for (i = 0; i < 5; ++i)
1542 tree_view->priv->grid_line_gc[i] = gdk_gc_new (widget->window);
1543 gdk_gc_copy (tree_view->priv->grid_line_gc[i], widget->style->text_aa_gc[i]);
1548 pspp_sheet_view_unrealize (GtkWidget *widget)
1551 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1552 PsppSheetViewPrivate *priv = tree_view->priv;
1555 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget);
1557 if (priv->scroll_timeout != 0)
1559 g_source_remove (priv->scroll_timeout);
1560 priv->scroll_timeout = 0;
1563 if (priv->open_dest_timeout != 0)
1565 g_source_remove (priv->open_dest_timeout);
1566 priv->open_dest_timeout = 0;
1569 if (priv->presize_handler_timer != 0)
1571 g_source_remove (priv->presize_handler_timer);
1572 priv->presize_handler_timer = 0;
1575 if (priv->validate_rows_timer != 0)
1577 g_source_remove (priv->validate_rows_timer);
1578 priv->validate_rows_timer = 0;
1581 if (priv->scroll_sync_timer != 0)
1583 g_source_remove (priv->scroll_sync_timer);
1584 priv->scroll_sync_timer = 0;
1587 if (priv->typeselect_flush_timeout)
1589 g_source_remove (priv->typeselect_flush_timeout);
1590 priv->typeselect_flush_timeout = 0;
1593 for (list = priv->columns; list; list = list->next)
1594 _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list->data));
1596 gdk_window_set_user_data (priv->bin_window, NULL);
1597 gdk_window_destroy (priv->bin_window);
1598 priv->bin_window = NULL;
1600 gdk_window_set_user_data (priv->header_window, NULL);
1601 gdk_window_destroy (priv->header_window);
1602 priv->header_window = NULL;
1604 if (priv->drag_window)
1606 gdk_window_set_user_data (priv->drag_window, NULL);
1607 gdk_window_destroy (priv->drag_window);
1608 priv->drag_window = NULL;
1611 if (priv->drag_highlight_window)
1613 gdk_window_set_user_data (priv->drag_highlight_window, NULL);
1614 gdk_window_destroy (priv->drag_highlight_window);
1615 priv->drag_highlight_window = NULL;
1618 for (x = 0 ; x < 5 ; ++x)
1619 g_object_unref (priv->grid_line_gc[x]);
1621 if (tree_view->priv->columns != NULL)
1623 list = tree_view->priv->columns;
1626 PsppSheetViewColumn *column;
1627 column = PSPP_SHEET_VIEW_COLUMN (list->data);
1629 pspp_sheet_view_remove_column (tree_view, column);
1631 tree_view->priv->columns = NULL;
1635 /* GtkWidget::size_request helper */
1637 pspp_sheet_view_size_request_columns (PsppSheetView *tree_view)
1641 tree_view->priv->header_height = 0;
1643 if (tree_view->priv->model)
1645 for (list = tree_view->priv->columns; list; list = list->next)
1647 GtkRequisition requisition;
1648 PsppSheetViewColumn *column = list->data;
1650 pspp_sheet_view_column_size_request (column, &requisition);
1651 column->button_request = requisition.width;
1652 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
1658 /* Called only by ::size_request */
1660 pspp_sheet_view_update_size (PsppSheetView *tree_view)
1663 PsppSheetViewColumn *column;
1666 if (tree_view->priv->model == NULL)
1668 tree_view->priv->width = 0;
1669 tree_view->priv->prev_width = 0;
1670 tree_view->priv->height = 0;
1674 tree_view->priv->prev_width = tree_view->priv->width;
1675 tree_view->priv->width = 0;
1677 /* keep this in sync with size_allocate below */
1678 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
1680 gint real_requested_width = 0;
1681 column = list->data;
1682 if (!column->visible)
1685 if (column->use_resized_width)
1687 real_requested_width = column->resized_width;
1691 real_requested_width = column->fixed_width;
1694 if (column->min_width != -1)
1695 real_requested_width = MAX (real_requested_width, column->min_width);
1696 if (column->max_width != -1)
1697 real_requested_width = MIN (real_requested_width, column->max_width);
1699 tree_view->priv->width += real_requested_width;
1702 tree_view->priv->height = tree_view->priv->fixed_height * tree_view->priv->row_count;
1706 pspp_sheet_view_size_request (GtkWidget *widget,
1707 GtkRequisition *requisition)
1709 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1712 /* we validate some rows initially just to make sure we have some size.
1713 * In practice, with a lot of static lists, this should get a good width.
1715 initialize_fixed_height_mode (tree_view);
1716 pspp_sheet_view_size_request_columns (tree_view);
1717 pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget));
1719 requisition->width = tree_view->priv->width;
1720 requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
1722 tmp_list = tree_view->priv->children;
1726 PsppSheetViewChild *child = tmp_list->data;
1727 GtkRequisition child_requisition;
1729 tmp_list = tmp_list->next;
1731 if (gtk_widget_get_visible (child->widget))
1732 gtk_widget_size_request (child->widget, &child_requisition);
1737 invalidate_column (PsppSheetView *tree_view,
1738 PsppSheetViewColumn *column)
1740 gint column_offset = 0;
1742 GtkWidget *widget = GTK_WIDGET (tree_view);
1745 if (!gtk_widget_get_realized (widget))
1748 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1749 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
1751 list = (rtl ? list->prev : list->next))
1753 PsppSheetViewColumn *tmpcolumn = list->data;
1754 if (tmpcolumn == column)
1756 GdkRectangle invalid_rect;
1758 invalid_rect.x = column_offset;
1760 invalid_rect.width = column->width;
1761 invalid_rect.height = widget->allocation.height;
1763 gdk_window_invalidate_rect (widget->window, &invalid_rect, TRUE);
1767 column_offset += tmpcolumn->width;
1772 invalidate_last_column (PsppSheetView *tree_view)
1777 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1779 for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
1781 last_column = (rtl ? last_column->next : last_column->prev))
1783 if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible)
1785 invalidate_column (tree_view, last_column->data);
1792 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_view,
1793 PsppSheetViewColumn *column)
1795 gint real_requested_width;
1797 if (column->use_resized_width)
1799 real_requested_width = column->resized_width;
1803 real_requested_width = column->fixed_width;
1806 if (column->min_width != -1)
1807 real_requested_width = MAX (real_requested_width, column->min_width);
1808 if (column->max_width != -1)
1809 real_requested_width = MIN (real_requested_width, column->max_width);
1811 return real_requested_width;
1815 span_intersects (int a0, int a_width,
1816 int b0, int b_width)
1818 int a1 = a0 + a_width;
1819 int b1 = b0 + b_width;
1820 return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1);
1823 /* GtkWidget::size_allocate helper */
1825 pspp_sheet_view_size_allocate_columns (GtkWidget *widget,
1826 gboolean *width_changed)
1828 PsppSheetView *tree_view;
1829 GList *list, *first_column, *last_column;
1830 PsppSheetViewColumn *column;
1831 GtkAllocation allocation;
1833 gint extra, extra_per_column;
1834 gint full_requested_width = 0;
1835 gint number_of_expand_columns = 0;
1836 gboolean column_changed = FALSE;
1839 tree_view = PSPP_SHEET_VIEW (widget);
1841 for (last_column = g_list_last (tree_view->priv->columns);
1842 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
1843 last_column = last_column->prev)
1845 if (last_column == NULL)
1848 for (first_column = g_list_first (tree_view->priv->columns);
1849 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
1850 first_column = first_column->next)
1854 allocation.height = tree_view->priv->header_height;
1856 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1858 /* find out how many extra space and expandable columns we have */
1859 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
1861 column = (PsppSheetViewColumn *)list->data;
1863 if (!column->visible)
1866 full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1869 number_of_expand_columns++;
1872 extra = MAX (widget->allocation.width - full_requested_width, 0);
1873 if (number_of_expand_columns > 0)
1874 extra_per_column = extra/number_of_expand_columns;
1876 extra_per_column = 0;
1878 for (list = (rtl ? last_column : first_column);
1879 list != (rtl ? first_column->prev : last_column->next);
1880 list = (rtl ? list->prev : list->next))
1882 gint real_requested_width = 0;
1885 column = list->data;
1886 old_width = column->width;
1888 if (!column->visible)
1891 /* We need to handle the dragged button specially.
1893 if (column == tree_view->priv->drag_column)
1895 GtkAllocation drag_allocation;
1896 gdk_drawable_get_size (tree_view->priv->drag_window,
1897 &(drag_allocation.width),
1898 &(drag_allocation.height));
1899 drag_allocation.x = 0;
1900 drag_allocation.y = 0;
1901 pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column,
1903 width += drag_allocation.width;
1907 real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1909 allocation.x = width;
1910 column->width = real_requested_width;
1914 if (number_of_expand_columns == 1)
1916 /* We add the remander to the last column as
1918 column->width += extra;
1922 column->width += extra_per_column;
1923 extra -= extra_per_column;
1924 number_of_expand_columns --;
1928 if (column->width != old_width)
1929 g_object_notify (G_OBJECT (column), "width");
1931 allocation.width = column->width;
1932 width += column->width;
1934 if (column->width > old_width)
1935 column_changed = TRUE;
1937 pspp_sheet_view_column_size_allocate (column, &allocation);
1939 if (span_intersects (allocation.x, allocation.width,
1940 tree_view->priv->hadjustment->value,
1941 widget->allocation.width)
1942 && gtk_widget_get_realized (widget))
1943 pspp_sheet_view_column_set_need_button (column, TRUE);
1946 gdk_window_move_resize (column->window,
1947 allocation.x + (rtl ? 0 : allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
1949 TREE_VIEW_DRAG_WIDTH, allocation.height);
1952 /* We change the width here. The user might have been resizing columns,
1953 * so the total width of the tree view changes.
1955 tree_view->priv->width = width;
1957 *width_changed = TRUE;
1960 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
1965 pspp_sheet_view_size_allocate (GtkWidget *widget,
1966 GtkAllocation *allocation)
1968 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1970 gboolean width_changed = FALSE;
1971 gint old_width = widget->allocation.width;
1973 if (allocation->width != widget->allocation.width)
1974 width_changed = TRUE;
1976 widget->allocation = *allocation;
1978 tmp_list = tree_view->priv->children;
1982 GtkAllocation allocation;
1984 PsppSheetViewChild *child = tmp_list->data;
1985 tmp_list = tmp_list->next;
1987 /* totally ignore our child's requisition */
1988 allocation.x = child->x;
1989 allocation.y = child->y;
1990 allocation.width = child->width;
1991 allocation.height = child->height;
1992 gtk_widget_size_allocate (child->widget, &allocation);
1995 /* We size-allocate the columns first because the width of the
1996 * tree view (used in updating the adjustments below) might change.
1998 pspp_sheet_view_size_allocate_columns (widget, &width_changed);
2000 tree_view->priv->hadjustment->page_size = allocation->width;
2001 tree_view->priv->hadjustment->page_increment = allocation->width * 0.9;
2002 tree_view->priv->hadjustment->step_increment = allocation->width * 0.1;
2003 tree_view->priv->hadjustment->lower = 0;
2004 tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->page_size, tree_view->priv->width);
2006 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2008 if (allocation->width < tree_view->priv->width)
2010 if (tree_view->priv->init_hadjust_value)
2012 tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
2013 tree_view->priv->init_hadjust_value = FALSE;
2015 else if (allocation->width != old_width)
2017 tree_view->priv->hadjustment->value = CLAMP (tree_view->priv->hadjustment->value - allocation->width + old_width, 0, tree_view->priv->width - allocation->width);
2020 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);
2024 tree_view->priv->hadjustment->value = 0;
2025 tree_view->priv->init_hadjust_value = TRUE;
2029 if (tree_view->priv->hadjustment->value + allocation->width > tree_view->priv->width)
2030 tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
2032 gtk_adjustment_changed (tree_view->priv->hadjustment);
2034 tree_view->priv->vadjustment->page_size = allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view);
2035 tree_view->priv->vadjustment->step_increment = tree_view->priv->vadjustment->page_size * 0.1;
2036 tree_view->priv->vadjustment->page_increment = tree_view->priv->vadjustment->page_size * 0.9;
2037 tree_view->priv->vadjustment->lower = 0;
2038 tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->page_size, tree_view->priv->height);
2040 gtk_adjustment_changed (tree_view->priv->vadjustment);
2042 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2043 if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size)
2044 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2045 else if (tree_view->priv->vadjustment->value + tree_view->priv->vadjustment->page_size > tree_view->priv->height)
2046 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2047 tree_view->priv->height - tree_view->priv->vadjustment->page_size);
2048 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2049 pspp_sheet_view_top_row_to_dy (tree_view);
2051 pspp_sheet_view_dy_to_top_row (tree_view);
2053 if (gtk_widget_get_realized (widget))
2055 gdk_window_move_resize (widget->window,
2056 allocation->x, allocation->y,
2057 allocation->width, allocation->height);
2058 gdk_window_move_resize (tree_view->priv->header_window,
2059 - (gint) tree_view->priv->hadjustment->value,
2061 MAX (tree_view->priv->width, allocation->width),
2062 tree_view->priv->header_height);
2063 gdk_window_move_resize (tree_view->priv->bin_window,
2064 - (gint) tree_view->priv->hadjustment->value,
2065 TREE_VIEW_HEADER_HEIGHT (tree_view),
2066 MAX (tree_view->priv->width, allocation->width),
2067 allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2070 if (tree_view->priv->row_count == 0)
2071 invalidate_empty_focus (tree_view);
2073 if (gtk_widget_get_realized (widget))
2075 gboolean has_expand_column = FALSE;
2076 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2078 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2080 has_expand_column = TRUE;
2085 /* This little hack only works if we have an LTR locale, and no column has the */
2088 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2089 ! has_expand_column)
2090 invalidate_last_column (tree_view);
2092 gtk_widget_queue_draw (widget);
2097 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2099 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2101 GtkWidget *widget = GTK_WIDGET (tree_view);
2103 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2104 gtk_widget_grab_focus (widget);
2105 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2109 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2112 return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2116 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2119 range_tower_set1 (tree_view->priv->selected, node, 1);
2123 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2126 range_tower_set0 (tree_view->priv->selected, node, 1);
2130 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2133 return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2137 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2140 return node > 0 ? node - 1 : -1;
2144 all_columns_selected (PsppSheetView *tree_view)
2148 for (list = tree_view->priv->columns; list; list = list->next)
2150 PsppSheetViewColumn *column = list->data;
2151 if (column->selectable && !column->selected)
2159 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2161 PsppSheetViewColumn *column,
2162 GdkEventButton *event)
2164 PsppSheetSelection *selection;
2165 PsppSheetSelectionMode mode;
2167 gboolean update_anchor;
2171 g_return_val_if_fail (tree_view != NULL, FALSE);
2172 g_return_val_if_fail (column != NULL, FALSE);
2174 selection = tree_view->priv->selection;
2175 mode = pspp_sheet_selection_get_mode (selection);
2176 if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2179 if (!column->row_head)
2184 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2185 if (event->type != GDK_BUTTON_PRESS
2186 || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2192 path = gtk_tree_path_new_from_indices (node, -1);
2195 pspp_sheet_selection_unselect_all (selection);
2196 pspp_sheet_selection_select_path (selection, path);
2197 pspp_sheet_selection_select_all_columns (selection);
2198 update_anchor = TRUE;
2201 else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2203 if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2204 || !all_columns_selected (tree_view))
2206 pspp_sheet_selection_unselect_all (selection);
2207 pspp_sheet_selection_select_path (selection, path);
2208 pspp_sheet_selection_select_all_columns (selection);
2209 update_anchor = TRUE;
2213 update_anchor = handled = FALSE;
2215 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2216 && modifiers == GDK_CONTROL_MASK)
2218 if (!all_columns_selected (tree_view))
2220 pspp_sheet_selection_unselect_all (selection);
2221 pspp_sheet_selection_select_all_columns (selection);
2224 if (pspp_sheet_selection_path_is_selected (selection, path))
2225 pspp_sheet_selection_unselect_path (selection, path);
2227 pspp_sheet_selection_select_path (selection, path);
2228 update_anchor = TRUE;
2231 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2232 && modifiers == GDK_SHIFT_MASK)
2234 GtkTreeRowReference *anchor = tree_view->priv->anchor;
2235 GtkTreePath *anchor_path;
2237 if (all_columns_selected (tree_view)
2238 && gtk_tree_row_reference_valid (anchor))
2240 update_anchor = FALSE;
2241 anchor_path = gtk_tree_row_reference_get_path (anchor);
2245 update_anchor = TRUE;
2246 anchor_path = gtk_tree_path_copy (path);
2249 pspp_sheet_selection_unselect_all (selection);
2250 pspp_sheet_selection_select_range (selection, anchor_path, path);
2251 pspp_sheet_selection_select_all_columns (selection);
2253 gtk_tree_path_free (anchor_path);
2258 update_anchor = handled = FALSE;
2262 if (tree_view->priv->anchor)
2263 gtk_tree_row_reference_free (tree_view->priv->anchor);
2264 tree_view->priv->anchor =
2265 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2266 tree_view->priv->model,
2270 gtk_tree_path_free (path);
2275 find_click (PsppSheetView *tree_view,
2278 PsppSheetViewColumn **column,
2279 GdkRectangle *background_area,
2280 GdkRectangle *cell_area)
2287 /* find the node that was clicked */
2288 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2291 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2296 background_area->y = y_offset + y;
2297 background_area->height = ROW_HEIGHT (tree_view);
2298 background_area->x = 0;
2300 /* Let the column have a chance at selecting it. */
2301 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2302 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2303 list; list = (rtl ? list->prev : list->next))
2305 PsppSheetViewColumn *candidate = list->data;
2307 if (!candidate->visible)
2310 background_area->width = candidate->width;
2311 if ((background_area->x > x) ||
2312 (background_area->x + background_area->width <= x))
2314 background_area->x += background_area->width;
2318 /* we found the focus column */
2320 pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2322 *column = candidate;
2330 pspp_sheet_view_button_press (GtkWidget *widget,
2331 GdkEventButton *event)
2333 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2335 PsppSheetViewColumn *column = NULL;
2337 GdkRectangle background_area;
2338 GdkRectangle cell_area;
2341 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2342 pspp_sheet_view_stop_editing (tree_view, FALSE);
2345 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2346 * we're done handling the button press.
2349 if (event->window == tree_view->priv->bin_window)
2354 gint pre_val, aft_val;
2355 PsppSheetViewColumn *column = NULL;
2356 GtkCellRenderer *focus_cell = NULL;
2357 gboolean row_double_click = FALSE;
2360 if (tree_view->priv->row_count == 0)
2362 grab_focus_and_unset_draw_keyfocus (tree_view);
2366 if (!find_click (tree_view, event->x, event->y, &node, &column,
2367 &background_area, &cell_area))
2369 grab_focus_and_unset_draw_keyfocus (tree_view);
2373 tree_view->priv->focus_column = column;
2375 if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2379 pre_val = tree_view->priv->vadjustment->value;
2381 path = _pspp_sheet_view_find_path (tree_view, node);
2383 /* we only handle selection modifications on the first button press
2385 if (event->type == GDK_BUTTON_PRESS)
2387 PsppSheetSelectionMode mode = 0;
2389 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2390 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
2391 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2392 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
2394 focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2396 pspp_sheet_view_column_focus_cell (column, focus_cell);
2398 if (event->state & GDK_CONTROL_MASK)
2400 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, mode);
2401 pspp_sheet_view_real_toggle_cursor_row (tree_view);
2403 else if (event->state & GDK_SHIFT_MASK)
2405 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
2406 pspp_sheet_view_real_select_cursor_row (tree_view, FALSE, mode);
2410 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
2413 if (tree_view->priv->anchor_column == NULL ||
2414 !(event->state & GDK_SHIFT_MASK))
2415 tree_view->priv->anchor_column = column;
2416 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2417 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2418 tree_view->priv->anchor_column,
2422 /* the treeview may have been scrolled because of _set_cursor,
2426 aft_val = tree_view->priv->vadjustment->value;
2427 dval = pre_val - aft_val;
2429 cell_area.y += dval;
2430 background_area.y += dval;
2432 /* Save press to possibly begin a drag
2434 if (!tree_view->priv->in_grab &&
2435 tree_view->priv->pressed_button < 0)
2437 tree_view->priv->pressed_button = event->button;
2438 tree_view->priv->press_start_x = event->x;
2439 tree_view->priv->press_start_y = event->y;
2440 tree_view->priv->press_start_node = node;
2442 if (tree_view->priv->rubber_banding_enable
2443 && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2444 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2446 tree_view->priv->press_start_y += tree_view->priv->dy;
2447 tree_view->priv->rubber_band_x = event->x;
2448 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2449 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2451 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2452 tree_view->priv->rubber_band_ctrl = TRUE;
2453 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2454 tree_view->priv->rubber_band_shift = TRUE;
2459 /* Test if a double click happened on the same row. */
2460 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2462 int double_click_time, double_click_distance;
2464 g_object_get (gtk_settings_get_for_screen (
2465 gtk_widget_get_screen (widget)),
2466 "gtk-double-click-time", &double_click_time,
2467 "gtk-double-click-distance", &double_click_distance,
2470 /* Same conditions as _gdk_event_button_generate */
2471 if (tree_view->priv->last_button_x != -1 &&
2472 (event->time < tree_view->priv->last_button_time + double_click_time) &&
2473 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2474 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2476 /* We do no longer compare paths of this row and the
2477 * row clicked previously. We use the double click
2478 * distance to decide whether this is a valid click,
2479 * allowing the mouse to slightly move over another row.
2481 row_double_click = TRUE;
2483 tree_view->priv->last_button_time = 0;
2484 tree_view->priv->last_button_x = -1;
2485 tree_view->priv->last_button_y = -1;
2489 tree_view->priv->last_button_time = event->time;
2490 tree_view->priv->last_button_x = event->x;
2491 tree_view->priv->last_button_y = event->y;
2495 if (row_double_click)
2497 gtk_grab_remove (widget);
2498 pspp_sheet_view_row_activated (tree_view, path, column);
2500 if (tree_view->priv->pressed_button == event->button)
2501 tree_view->priv->pressed_button = -1;
2504 gtk_tree_path_free (path);
2506 /* If we activated the row through a double click we don't want to grab
2507 * focus back, as moving focus to another widget is pretty common.
2509 if (!row_double_click)
2510 grab_focus_and_unset_draw_keyfocus (tree_view);
2515 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2517 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2519 column = list->data;
2520 if (event->window == column->window &&
2521 column->resizable &&
2526 if (gdk_pointer_grab (column->window, FALSE,
2527 GDK_POINTER_MOTION_HINT_MASK |
2528 GDK_BUTTON1_MOTION_MASK |
2529 GDK_BUTTON_RELEASE_MASK,
2530 NULL, NULL, event->time))
2533 gtk_grab_add (widget);
2534 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2535 column->resized_width = column->width;
2537 /* block attached dnd signal handler */
2538 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2540 g_signal_handlers_block_matched (widget,
2541 G_SIGNAL_MATCH_DATA,
2545 tree_view->priv->drag_pos = i;
2546 tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2548 if (!gtk_widget_has_focus (widget))
2549 gtk_widget_grab_focus (widget);
2557 /* GtkWidget::button_release_event helper */
2559 pspp_sheet_view_button_release_drag_column (GtkWidget *widget,
2560 GdkEventButton *event)
2562 PsppSheetView *tree_view;
2566 tree_view = PSPP_SHEET_VIEW (widget);
2568 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2569 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2570 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2572 /* Move the button back */
2573 g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2575 g_object_ref (tree_view->priv->drag_column->button);
2576 gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2577 gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2578 gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2579 g_object_unref (tree_view->priv->drag_column->button);
2580 gtk_widget_queue_resize (widget);
2581 if (tree_view->priv->drag_column->resizable)
2583 gdk_window_raise (tree_view->priv->drag_column->window);
2584 gdk_window_show (tree_view->priv->drag_column->window);
2587 gdk_window_hide (tree_view->priv->drag_column->window);
2589 gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2593 if (tree_view->priv->cur_reorder &&
2594 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2595 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2596 tree_view->priv->cur_reorder->right_column);
2600 if (tree_view->priv->cur_reorder &&
2601 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2602 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2603 tree_view->priv->cur_reorder->left_column);
2605 tree_view->priv->drag_column = NULL;
2606 gdk_window_hide (tree_view->priv->drag_window);
2608 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2609 g_slice_free (PsppSheetViewColumnReorder, l->data);
2610 g_list_free (tree_view->priv->column_drag_info);
2611 tree_view->priv->column_drag_info = NULL;
2612 tree_view->priv->cur_reorder = NULL;
2614 if (tree_view->priv->drag_highlight_window)
2615 gdk_window_hide (tree_view->priv->drag_highlight_window);
2617 /* Reset our flags */
2618 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2619 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2624 /* GtkWidget::button_release_event helper */
2626 pspp_sheet_view_button_release_column_resize (GtkWidget *widget,
2627 GdkEventButton *event)
2629 PsppSheetView *tree_view;
2632 tree_view = PSPP_SHEET_VIEW (widget);
2634 tree_view->priv->drag_pos = -1;
2636 /* unblock attached dnd signal handler */
2637 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2639 g_signal_handlers_unblock_matched (widget,
2640 G_SIGNAL_MATCH_DATA,
2644 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2645 gtk_grab_remove (widget);
2646 gdk_display_pointer_ungrab (gdk_drawable_get_display (event->window),
2652 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2653 GdkEventButton *event)
2655 GtkCellEditable *cell_editable;
2660 PsppSheetViewColumn *column;
2661 GdkRectangle background_area;
2662 GdkRectangle cell_area;
2668 if (event->window != tree_view->priv->bin_window)
2671 /* Ignore a released button, if that button wasn't depressed */
2672 if (tree_view->priv->pressed_button != event->button)
2675 if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2679 /* decide if we edit */
2680 path = _pspp_sheet_view_find_path (tree_view, node);
2681 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2682 if (event->button != 1 || modifiers)
2685 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2686 pspp_sheet_view_column_cell_set_cell_data (column,
2687 tree_view->priv->model,
2690 if (!pspp_sheet_view_column_get_quick_edit (column)
2691 && _pspp_sheet_view_column_has_editable_cell (column))
2694 flags = 0; /* FIXME: get the right flags */
2695 path_string = gtk_tree_path_to_string (path);
2697 if (!_pspp_sheet_view_column_cell_event (column,
2705 if (cell_editable == NULL)
2708 pspp_sheet_view_real_set_cursor (tree_view, path,
2709 TRUE, TRUE, 0); /* XXX mode? */
2710 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2713 _pspp_sheet_view_column_get_neighbor_sizes (
2714 column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2717 area.width -= right + left;
2719 pspp_sheet_view_real_start_editing (tree_view,
2726 g_free (path_string);
2727 gtk_tree_path_free (path);
2732 pspp_sheet_view_button_release (GtkWidget *widget,
2733 GdkEventButton *event)
2735 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2737 pspp_sheet_view_stop_editing (tree_view, FALSE);
2738 if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2739 && pspp_sheet_view_button_release_edit (tree_view, event))
2741 if (tree_view->priv->pressed_button == event->button)
2742 tree_view->priv->pressed_button = -1;
2744 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2748 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2749 return pspp_sheet_view_button_release_drag_column (widget, event);
2751 if (tree_view->priv->rubber_band_status)
2752 pspp_sheet_view_stop_rubber_band (tree_view);
2754 if (tree_view->priv->pressed_button == event->button)
2755 tree_view->priv->pressed_button = -1;
2757 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2758 return pspp_sheet_view_button_release_column_resize (widget, event);
2764 pspp_sheet_view_grab_broken (GtkWidget *widget,
2765 GdkEventGrabBroken *event)
2767 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2769 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2770 pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2772 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2773 pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2778 /* GtkWidget::motion_event function set.
2782 do_prelight (PsppSheetView *tree_view,
2784 /* these are in bin_window coords */
2788 int prev_node = tree_view->priv->prelight_node;
2790 if (prev_node != node)
2792 tree_view->priv->prelight_node = node;
2795 _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2798 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2803 prelight_or_select (PsppSheetView *tree_view,
2805 /* these are in bin_window coords */
2809 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2811 if (tree_view->priv->hover_selection &&
2812 (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2813 !(tree_view->priv->edited_column &&
2814 tree_view->priv->edited_column->editable_widget))
2818 if (!pspp_sheet_view_node_is_selected (tree_view, node))
2822 path = _pspp_sheet_view_find_path (tree_view, node);
2823 pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2824 if (pspp_sheet_view_node_is_selected (tree_view, node))
2826 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2827 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE, 0); /* XXX mode? */
2829 gtk_tree_path_free (path);
2833 else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2834 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2837 do_prelight (tree_view, node, x, y);
2841 ensure_unprelighted (PsppSheetView *tree_view)
2843 do_prelight (tree_view,
2845 -1000, -1000); /* coords not possibly over an arrow */
2847 g_assert (tree_view->priv->prelight_node < 0);
2851 update_prelight (PsppSheetView *tree_view,
2858 if (tree_view->priv->row_count == 0)
2863 ensure_unprelighted (tree_view);
2867 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2871 pspp_sheet_view_find_offset (tree_view, new_y, &node);
2874 prelight_or_select (tree_view, node, x, y);
2880 /* Our motion arrow is either a box (in the case of the original spot)
2881 * or an arrow. It is expander_size wide.
2904 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2906 PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2907 GtkWidget *widget = GTK_WIDGET (tree_view);
2908 GdkBitmap *mask = NULL;
2913 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2914 GdkWindowAttr attributes;
2915 guint attributes_mask;
2918 reorder->left_column == tree_view->priv->drag_column ||
2919 reorder->right_column == tree_view->priv->drag_column)
2920 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2921 else if (reorder->left_column || reorder->right_column)
2923 GdkRectangle visible_rect;
2924 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2925 if (reorder->left_column)
2926 x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2928 x = reorder->right_column->allocation.x;
2930 if (x < visible_rect.x)
2931 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2932 else if (x > visible_rect.x + visible_rect.width)
2933 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2935 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2938 /* We want to draw the rectangle over the initial location. */
2939 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2944 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2946 if (tree_view->priv->drag_highlight_window)
2948 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2950 gdk_window_destroy (tree_view->priv->drag_highlight_window);
2953 attributes.window_type = GDK_WINDOW_CHILD;
2954 attributes.wclass = GDK_INPUT_OUTPUT;
2955 attributes.x = tree_view->priv->drag_column_x;
2957 width = attributes.width = tree_view->priv->drag_column->allocation.width;
2958 height = attributes.height = tree_view->priv->drag_column->allocation.height;
2959 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
2960 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
2961 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
2962 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
2963 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
2964 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
2966 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
2967 gc = gdk_gc_new (mask);
2969 gdk_gc_set_foreground (gc, &col);
2970 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
2972 gdk_gc_set_foreground(gc, &col);
2973 gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
2974 g_object_unref (gc);
2976 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
2978 if (mask) g_object_unref (mask);
2979 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2982 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
2988 width = tree_view->priv->expander_size;
2990 /* Get x, y, width, height of arrow */
2991 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
2992 if (reorder->left_column)
2994 x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
2995 height = reorder->left_column->allocation.height;
2999 x += reorder->right_column->allocation.x - width/2;
3000 height = reorder->right_column->allocation.height;
3002 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3003 height += tree_view->priv->expander_size;
3005 /* Create the new window */
3006 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3008 if (tree_view->priv->drag_highlight_window)
3010 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3012 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3015 attributes.window_type = GDK_WINDOW_TEMP;
3016 attributes.wclass = GDK_INPUT_OUTPUT;
3017 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3018 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3019 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3020 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3023 attributes.width = width;
3024 attributes.height = height;
3025 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3026 &attributes, attributes_mask);
3027 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3029 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3030 gc = gdk_gc_new (mask);
3032 gdk_gc_set_foreground (gc, &col);
3033 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3035 /* Draw the 2 arrows as per above */
3037 gdk_gc_set_foreground (gc, &col);
3038 for (i = 0; i < width; i ++)
3040 if (i == (width/2 - 1))
3042 gdk_draw_line (mask, gc, i, j, i, height - j);
3043 if (i < (width/2 - 1))
3048 g_object_unref (gc);
3049 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3051 if (mask) g_object_unref (mask);
3054 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3055 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3057 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3058 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3064 width = tree_view->priv->expander_size;
3066 /* Get x, y, width, height of arrow */
3067 width = width/2; /* remember, the arrow only takes half the available width */
3068 gdk_window_get_origin (widget->window, &x, &y);
3069 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3070 x += widget->allocation.width - width;
3072 if (reorder->left_column)
3073 height = reorder->left_column->allocation.height;
3075 height = reorder->right_column->allocation.height;
3077 y -= tree_view->priv->expander_size;
3078 height += 2*tree_view->priv->expander_size;
3080 /* Create the new window */
3081 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3082 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3084 if (tree_view->priv->drag_highlight_window)
3086 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3088 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3091 attributes.window_type = GDK_WINDOW_TEMP;
3092 attributes.wclass = GDK_INPUT_OUTPUT;
3093 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3094 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3095 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3096 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3099 attributes.width = width;
3100 attributes.height = height;
3101 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3102 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3104 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3105 gc = gdk_gc_new (mask);
3107 gdk_gc_set_foreground (gc, &col);
3108 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3110 /* Draw the 2 arrows as per above */
3112 gdk_gc_set_foreground (gc, &col);
3113 j = tree_view->priv->expander_size;
3114 for (i = 0; i < width; i ++)
3117 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3121 gdk_draw_line (mask, gc, k, j, k, height - j);
3122 gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3123 gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3126 g_object_unref (gc);
3127 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3129 if (mask) g_object_unref (mask);
3132 tree_view->priv->drag_column_window_state = arrow_type;
3133 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3137 g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3138 gdk_window_hide (tree_view->priv->drag_highlight_window);
3142 gdk_window_show (tree_view->priv->drag_highlight_window);
3143 gdk_window_raise (tree_view->priv->drag_highlight_window);
3147 pspp_sheet_view_motion_resize_column (GtkWidget *widget,
3148 GdkEventMotion *event)
3152 PsppSheetViewColumn *column;
3153 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3155 column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3157 if (event->is_hint || event->window != widget->window)
3158 gtk_widget_get_pointer (widget, &x, NULL);
3162 if (tree_view->priv->hadjustment)
3163 x += tree_view->priv->hadjustment->value;
3165 new_width = pspp_sheet_view_new_column_width (tree_view,
3166 tree_view->priv->drag_pos, &x);
3167 if (x != tree_view->priv->x_drag &&
3168 (new_width != column->fixed_width))
3170 column->use_resized_width = TRUE;
3171 column->resized_width = new_width;
3174 column->resized_width -= tree_view->priv->last_extra_space_per_column;
3176 gtk_widget_queue_resize (widget);
3184 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3186 PsppSheetViewColumnReorder *reorder = NULL;
3190 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3191 for (list = tree_view->priv->column_drag_info; list; list = list->next)
3193 reorder = (PsppSheetViewColumnReorder *) list->data;
3194 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3199 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3202 tree_view->priv->cur_reorder = reorder;
3203 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3207 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3209 GdkRectangle visible_rect;
3214 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3215 y += tree_view->priv->dy;
3217 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3219 /* see if we are near the edge. */
3220 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3223 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3228 value = CLAMP (tree_view->priv->vadjustment->value + offset, 0.0,
3229 tree_view->priv->vadjustment->upper - tree_view->priv->vadjustment->page_size);
3230 gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3234 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3236 GdkRectangle visible_rect;
3241 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3243 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3245 /* See if we are near the edge. */
3246 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3249 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3255 value = CLAMP (tree_view->priv->hadjustment->value + offset,
3256 0.0, tree_view->priv->hadjustment->upper - tree_view->priv->hadjustment->page_size);
3257 gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3264 pspp_sheet_view_motion_drag_column (GtkWidget *widget,
3265 GdkEventMotion *event)
3267 PsppSheetView *tree_view = (PsppSheetView *) widget;
3268 PsppSheetViewColumn *column = tree_view->priv->drag_column;
3272 if ((column == NULL) ||
3273 (event->window != tree_view->priv->drag_window))
3276 /* Handle moving the header */
3277 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3278 x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3279 MAX (tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.width) - column->allocation.width);
3280 gdk_window_move (tree_view->priv->drag_window, x, y);
3282 /* autoscroll, if needed */
3283 pspp_sheet_view_horizontal_autoscroll (tree_view);
3284 /* Update the current reorder position and arrow; */
3285 pspp_sheet_view_update_current_reorder (tree_view);
3291 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3293 remove_scroll_timeout (tree_view);
3294 gtk_grab_remove (GTK_WIDGET (tree_view));
3296 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3298 GtkTreePath *tmp_path;
3300 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3302 /* The anchor path should be set to the start path */
3303 tmp_path = _pspp_sheet_view_find_path (tree_view,
3304 tree_view->priv->rubber_band_start_node);
3306 if (tree_view->priv->anchor)
3307 gtk_tree_row_reference_free (tree_view->priv->anchor);
3309 tree_view->priv->anchor =
3310 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3311 tree_view->priv->model,
3314 gtk_tree_path_free (tmp_path);
3316 /* ... and the cursor to the end path */
3317 tmp_path = _pspp_sheet_view_find_path (tree_view,
3318 tree_view->priv->rubber_band_end_node);
3319 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE, 0); /* XXX mode? */
3320 gtk_tree_path_free (tmp_path);
3322 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3325 /* Clear status variables */
3326 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3327 tree_view->priv->rubber_band_shift = 0;
3328 tree_view->priv->rubber_band_ctrl = 0;
3330 tree_view->priv->rubber_band_start_node = -1;
3331 tree_view->priv->rubber_band_end_node = -1;
3335 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3339 gboolean skip_start,
3342 if (start_node == end_node)
3345 /* We skip the first node and jump inside the loop */
3351 /* Small optimization by assuming insensitive nodes are never
3356 if (tree_view->priv->rubber_band_shift)
3357 pspp_sheet_view_node_select (tree_view, start_node);
3358 else if (tree_view->priv->rubber_band_ctrl)
3360 /* Toggle the selection state */
3361 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3362 pspp_sheet_view_node_unselect (tree_view, start_node);
3364 pspp_sheet_view_node_select (tree_view, start_node);
3367 pspp_sheet_view_node_select (tree_view, start_node);
3371 /* Mirror the above */
3372 if (tree_view->priv->rubber_band_shift)
3373 pspp_sheet_view_node_unselect (tree_view, start_node);
3374 else if (tree_view->priv->rubber_band_ctrl)
3376 /* Toggle the selection state */
3377 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3378 pspp_sheet_view_node_unselect (tree_view, start_node);
3380 pspp_sheet_view_node_select (tree_view, start_node);
3383 pspp_sheet_view_node_unselect (tree_view, start_node);
3386 _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3388 if (start_node == end_node)
3393 start_node = pspp_sheet_view_node_next (tree_view, start_node);
3396 /* Ran out of tree */
3399 if (skip_end && start_node == end_node)
3406 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3409 return node * tree_view->priv->fixed_height;
3413 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3417 int fixed_height = tree_view->priv->fixed_height;
3418 if (fixed_height <= 0
3420 || height >= tree_view->priv->row_count * fixed_height)
3427 *new_node = height / fixed_height;
3428 return height % fixed_height;
3433 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3438 pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3439 pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3441 /* Handle the start area first */
3442 if (tree_view->priv->rubber_band_start_node < 0)
3444 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3451 else if (start_node < tree_view->priv->rubber_band_start_node)
3453 /* New node is above the old one; selection became bigger */
3454 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3456 tree_view->priv->rubber_band_start_node,
3461 else if (start_node > tree_view->priv->rubber_band_start_node)
3463 /* New node is below the old one; selection became smaller */
3464 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3465 tree_view->priv->rubber_band_start_node,
3472 tree_view->priv->rubber_band_start_node = start_node;
3474 /* Next, handle the end area */
3475 if (tree_view->priv->rubber_band_end_node < 0)
3477 /* In the event this happens, start_node was also -1; this case is
3481 else if (end_node < 0)
3483 /* Find the last node in the tree */
3484 pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3487 /* Selection reached end of the tree */
3488 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3489 tree_view->priv->rubber_band_end_node,
3495 else if (end_node > tree_view->priv->rubber_band_end_node)
3497 /* New node is below the old one; selection became bigger */
3498 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3499 tree_view->priv->rubber_band_end_node,
3505 else if (end_node < tree_view->priv->rubber_band_end_node)
3507 /* New node is above the old one; selection became smaller */
3508 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3510 tree_view->priv->rubber_band_end_node,
3516 tree_view->priv->rubber_band_end_node = end_node;
3520 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3523 GdkRectangle old_area;
3524 GdkRectangle new_area;
3525 GdkRectangle common;
3526 GdkRegion *invalid_region;
3527 PsppSheetViewColumn *column;
3529 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3530 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3531 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3532 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3534 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3537 y = MAX (y, 0) + tree_view->priv->dy;
3539 new_area.x = MIN (tree_view->priv->press_start_x, x);
3540 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3541 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3542 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3544 invalid_region = gdk_region_rectangle (&old_area);
3545 gdk_region_union_with_rect (invalid_region, &new_area);
3547 gdk_rectangle_intersect (&old_area, &new_area, &common);
3548 if (common.width > 2 && common.height > 2)
3550 GdkRegion *common_region;
3552 /* make sure the border is invalidated */
3558 common_region = gdk_region_rectangle (&common);
3560 gdk_region_subtract (invalid_region, common_region);
3561 gdk_region_destroy (common_region);
3564 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
3566 gdk_region_destroy (invalid_region);
3568 tree_view->priv->rubber_band_x = x;
3569 tree_view->priv->rubber_band_y = y;
3570 pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3572 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3573 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3574 tree_view->priv->anchor_column,
3577 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3579 pspp_sheet_view_update_rubber_band_selection (tree_view);
3583 pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view,
3588 GdkRectangle rubber_rect;
3591 rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3592 rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3593 rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3594 rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3596 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3599 cr = gdk_cairo_create (tree_view->priv->bin_window);
3600 cairo_set_line_width (cr, 1.0);
3602 cairo_set_source_rgba (cr,
3603 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].red / 65535.,
3604 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].green / 65535.,
3605 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].blue / 65535.,
3608 gdk_cairo_rectangle (cr, &rect);
3612 cairo_set_source_rgb (cr,
3613 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].red / 65535.,
3614 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].green / 65535.,
3615 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].blue / 65535.);
3617 cairo_rectangle (cr,
3618 rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3619 rubber_rect.width - 1, rubber_rect.height - 1);
3626 pspp_sheet_view_motion_bin_window (GtkWidget *widget,
3627 GdkEventMotion *event)
3629 PsppSheetView *tree_view;
3633 tree_view = (PsppSheetView *) widget;
3635 if (tree_view->priv->row_count == 0)
3638 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3640 GdkRectangle background_area, cell_area;
3641 PsppSheetViewColumn *column;
3643 if (find_click (tree_view, event->x, event->y, &node, &column,
3644 &background_area, &cell_area)
3645 && tree_view->priv->focus_column == column
3646 && tree_view->priv->press_start_node == node)
3649 gtk_grab_add (GTK_WIDGET (tree_view));
3650 pspp_sheet_view_update_rubber_band (tree_view);
3652 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3654 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3656 pspp_sheet_view_update_rubber_band (tree_view);
3658 add_scroll_timeout (tree_view);
3661 /* only check for an initiated drag when a button is pressed */
3662 if (tree_view->priv->pressed_button >= 0
3663 && !tree_view->priv->rubber_band_status)
3664 pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3666 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3670 pspp_sheet_view_find_offset (tree_view, new_y, &node);
3672 tree_view->priv->event_last_x = event->x;
3673 tree_view->priv->event_last_y = event->y;
3675 prelight_or_select (tree_view, node, event->x, event->y);
3681 pspp_sheet_view_motion (GtkWidget *widget,
3682 GdkEventMotion *event)
3684 PsppSheetView *tree_view;
3686 tree_view = (PsppSheetView *) widget;
3688 /* Resizing a column */
3689 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3690 return pspp_sheet_view_motion_resize_column (widget, event);
3693 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3694 return pspp_sheet_view_motion_drag_column (widget, event);
3696 /* Sanity check it */
3697 if (event->window == tree_view->priv->bin_window)
3698 return pspp_sheet_view_motion_bin_window (widget, event);
3703 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3704 * the tree is empty.
3707 invalidate_empty_focus (PsppSheetView *tree_view)
3711 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3716 gdk_drawable_get_size (tree_view->priv->bin_window, &area.width, &area.height);
3717 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3720 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3724 draw_empty_focus (PsppSheetView *tree_view, GdkRectangle *clip_area)
3726 GtkWidget *widget = GTK_WIDGET (tree_view);
3729 if (!gtk_widget_has_focus (widget))
3732 gdk_drawable_get_size (tree_view->priv->bin_window, &w, &h);
3738 gtk_paint_focus (gtk_widget_get_style (widget),
3739 tree_view->priv->bin_window,
3740 gtk_widget_get_state (widget),
3748 pspp_sheet_view_draw_grid_lines (PsppSheetView *tree_view,
3749 GdkEventExpose *event,
3750 gint n_visible_columns,
3754 GList *list = tree_view->priv->columns;
3759 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3760 && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3763 gdk_drawable_get_size (event->window, NULL, &height);
3765 /* Only draw the lines for visible rows and columns */
3766 for (list = tree_view->priv->columns; list; list = list->next, i++)
3768 PsppSheetViewColumn *column = list->data;
3771 if (! column->visible)
3774 current_x += column->width;
3776 /* Generally the grid lines should fit within the column, but for the
3777 last visible column we put it just past the end of the column.
3778 (Otherwise horizontal grid lines sometimes stick out by one pixel.) */
3780 if (i != n_visible_columns - 1)
3783 if (x >= event->area.x && x < event->area.x + event->area.width)
3784 gdk_draw_line (event->window,
3785 tree_view->priv->grid_line_gc[GTK_WIDGET(tree_view)->state],
3791 /* Warning: Very scary function.
3792 * Modify at your own risk
3794 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3795 * FIXME: It's not...
3798 pspp_sheet_view_bin_expose (GtkWidget *widget,
3799 GdkEventExpose *event)
3801 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3806 int drag_highlight = -1;
3809 gint y_offset, cell_offset;
3811 GdkRectangle background_area;
3812 GdkRectangle cell_area;
3814 gint bin_window_width;
3815 gint bin_window_height;
3816 GtkTreePath *cursor_path;
3817 GtkTreePath *drag_dest_path;
3818 GList *first_column, *last_column;
3819 gint vertical_separator;
3820 gint horizontal_separator;
3821 gint focus_line_width;
3822 gboolean allow_rules;
3823 gboolean has_special_cell;
3825 gint n_visible_columns;
3826 gint grid_line_width;
3827 gboolean row_ending_details;
3828 gboolean draw_vgrid_lines, draw_hgrid_lines;
3831 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3833 gtk_widget_style_get (widget,
3834 "horizontal-separator", &horizontal_separator,
3835 "vertical-separator", &vertical_separator,
3836 "allow-rules", &allow_rules,
3837 "focus-line-width", &focus_line_width,
3838 "row-ending-details", &row_ending_details,
3841 if (tree_view->priv->row_count == 0)
3843 draw_empty_focus (tree_view, &event->area);
3847 /* clip event->area to the visible area */
3848 if (event->area.height < 0)
3851 validate_visible_area (tree_view);
3853 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, event->area.y);
3857 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3858 gdk_drawable_get_size (tree_view->priv->bin_window,
3859 &bin_window_width, &bin_window_height);
3861 if (tree_view->priv->height < bin_window_height)
3863 gtk_paint_flat_box (widget->style,
3870 0, tree_view->priv->height,
3872 bin_window_height - tree_view->priv->height);
3878 /* find the path for the node */
3879 path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3880 gtk_tree_model_get_iter (tree_view->priv->model,
3883 gtk_tree_path_free (path);
3886 drag_dest_path = NULL;
3888 if (tree_view->priv->cursor)
3889 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3892 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3894 if (tree_view->priv->drag_dest_row)
3895 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3898 _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3902 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3903 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3905 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
3906 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3908 if (draw_vgrid_lines || draw_hgrid_lines)
3909 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
3911 n_visible_columns = 0;
3912 for (list = tree_view->priv->columns; list; list = list->next)
3914 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
3916 n_visible_columns ++;
3919 /* Find the last column */
3920 for (last_column = g_list_last (tree_view->priv->columns);
3921 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
3922 last_column = last_column->prev)
3926 for (first_column = g_list_first (tree_view->priv->columns);
3927 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
3928 first_column = first_column->next)
3931 /* Actually process the expose event. To do this, we want to
3932 * start at the first node of the event, and walk the tree in
3933 * order, drawing each successive node.
3940 gboolean is_first = FALSE;
3941 gboolean is_last = FALSE;
3942 gboolean done = FALSE;
3945 max_height = ROW_HEIGHT (tree_view);
3949 background_area.y = y_offset + event->area.y;
3950 background_area.height = max_height;
3951 max_y = background_area.y + max_height;
3955 if (node == tree_view->priv->prelight_node)
3956 flags |= GTK_CELL_RENDERER_PRELIT;
3958 selected = pspp_sheet_view_node_is_selected (tree_view, node);
3962 if (tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
3964 /* we *need* to set cell data on all cells before the call
3965 * to _has_special_cell, else _has_special_cell() does not
3966 * return a correct value.
3968 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
3970 list = (rtl ? list->prev : list->next))
3972 PsppSheetViewColumn *column = list->data;
3973 pspp_sheet_view_column_cell_set_cell_data (column,
3974 tree_view->priv->model,
3978 has_special_cell = pspp_sheet_view_has_special_cell (tree_view);
3981 has_special_cell = tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
3983 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
3985 list = (rtl ? list->prev : list->next))
3987 PsppSheetViewColumn *column = list->data;
3988 const gchar *detail = NULL;
3989 gboolean selected_column;
3992 if (!column->visible)
3995 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
3996 selected_column = column->selected && column->selectable;
3998 selected_column = TRUE;
4000 if (cell_offset > event->area.x + event->area.width ||
4001 cell_offset + column->width < event->area.x)
4003 cell_offset += column->width;
4007 if (selected && selected_column)
4008 flags |= GTK_CELL_RENDERER_SELECTED;
4010 flags &= ~GTK_CELL_RENDERER_SELECTED;
4012 if (column->show_sort_indicator)
4013 flags |= GTK_CELL_RENDERER_SORTED;
4015 flags &= ~GTK_CELL_RENDERER_SORTED;
4018 flags |= GTK_CELL_RENDERER_FOCUSED;
4020 flags &= ~GTK_CELL_RENDERER_FOCUSED;
4022 background_area.x = cell_offset;
4023 background_area.width = column->width;
4025 cell_area = background_area;
4026 cell_area.y += vertical_separator / 2;
4027 cell_area.x += horizontal_separator / 2;
4028 cell_area.height -= vertical_separator;
4029 cell_area.width -= horizontal_separator;
4031 if (draw_vgrid_lines)
4033 if (list == first_column)
4035 cell_area.width -= grid_line_width / 2;
4037 else if (list == last_column)
4039 cell_area.x += grid_line_width / 2;
4040 cell_area.width -= grid_line_width / 2;
4044 cell_area.x += grid_line_width / 2;
4045 cell_area.width -= grid_line_width;
4049 if (draw_hgrid_lines)
4051 cell_area.y += grid_line_width / 2;
4052 cell_area.height -= grid_line_width;
4055 if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4057 cell_offset += column->width;
4061 pspp_sheet_view_column_cell_set_cell_data (column,
4062 tree_view->priv->model,
4065 /* Select the detail for drawing the cell. relevant
4066 * factors are parity, sortedness, and whether to
4069 if (allow_rules && tree_view->priv->has_rules)
4071 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4072 n_visible_columns >= 3)
4075 detail = "cell_odd_ruled_sorted";
4077 detail = "cell_even_ruled_sorted";
4082 detail = "cell_odd_ruled";
4084 detail = "cell_even_ruled";
4089 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4090 n_visible_columns >= 3)
4093 detail = "cell_odd_sorted";
4095 detail = "cell_even_sorted";
4100 detail = "cell_odd";
4102 detail = "cell_even";
4108 if (widget->state == GTK_STATE_INSENSITIVE)
4109 state = GTK_STATE_INSENSITIVE;
4110 else if (flags & GTK_CELL_RENDERER_SELECTED)
4111 state = GTK_STATE_SELECTED;
4113 state = GTK_STATE_NORMAL;
4115 /* Draw background */
4116 if (row_ending_details)
4118 char new_detail[128];
4120 is_first = (rtl ? !list->next : !list->prev);
4121 is_last = (rtl ? !list->prev : !list->next);
4123 /* (I don't like the snprintfs either, but couldn't find a
4126 if (is_first && is_last)
4127 g_snprintf (new_detail, 127, "%s", detail);
4129 g_snprintf (new_detail, 127, "%s_start", detail);
4131 g_snprintf (new_detail, 127, "%s_end", detail);
4133 g_snprintf (new_detail, 128, "%s_middle", detail);
4135 gtk_paint_flat_box (widget->style,
4144 background_area.width,
4145 background_area.height);
4149 gtk_paint_flat_box (widget->style,
4158 background_area.width,
4159 background_area.height);
4162 if (draw_hgrid_lines)
4164 if (background_area.y > 0)
4165 gdk_draw_line (event->window,
4166 tree_view->priv->grid_line_gc[widget->state],
4167 background_area.x, background_area.y,
4168 background_area.x + background_area.width,
4171 if (y_offset + max_height <= event->area.height)
4172 gdk_draw_line (event->window,
4173 tree_view->priv->grid_line_gc[widget->state],
4174 background_area.x, background_area.y + max_height,
4175 background_area.x + background_area.width,
4176 background_area.y + max_height);
4179 _pspp_sheet_view_column_cell_render (column,
4186 if (node == cursor && has_special_cell &&
4187 ((column == tree_view->priv->focus_column &&
4188 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4189 gtk_widget_has_focus (widget)) ||
4190 (column == tree_view->priv->edited_column)))
4192 _pspp_sheet_view_column_cell_draw_focus (column,
4200 cell_offset += column->width;
4203 if (cell_offset < event->area.x)
4205 gtk_paint_flat_box (widget->style,
4214 event->area.x - cell_offset,
4215 background_area.height);
4218 if (node == drag_highlight)
4220 /* Draw indicator for the drop
4222 gint highlight_y = -1;
4226 switch (tree_view->priv->drag_dest_pos)
4228 case PSPP_SHEET_VIEW_DROP_BEFORE:
4229 highlight_y = background_area.y - 1;
4230 if (highlight_y < 0)
4234 case PSPP_SHEET_VIEW_DROP_AFTER:
4235 highlight_y = background_area.y + background_area.height - 1;
4238 case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE:
4239 case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER:
4240 _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node);
4244 gdk_drawable_get_size (tree_view->priv->bin_window,
4247 if (row_ending_details)
4248 gtk_paint_focus (widget->style,
4249 tree_view->priv->bin_window,
4250 gtk_widget_get_state (widget),
4254 ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4255 : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4256 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4257 - focus_line_width / 2,
4258 width, ROW_HEIGHT (tree_view)
4259 - focus_line_width + 1);
4261 gtk_paint_focus (widget->style,
4262 tree_view->priv->bin_window,
4263 gtk_widget_get_state (widget),
4266 "treeview-drop-indicator",
4267 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4268 - focus_line_width / 2,
4269 width, ROW_HEIGHT (tree_view)
4270 - focus_line_width + 1);
4274 if (highlight_y >= 0)
4276 gdk_draw_line (event->window,
4277 widget->style->fg_gc[gtk_widget_get_state (widget)],
4280 rtl ? 0 : bin_window_width,
4285 /* draw the big row-spanning focus rectangle, if needed */
4286 if (!has_special_cell && node == cursor &&
4287 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4288 gtk_widget_has_focus (widget))
4290 gint tmp_y, tmp_height;
4292 GtkStateType focus_rect_state;
4295 flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
4296 (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
4297 (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE :
4300 gdk_drawable_get_size (tree_view->priv->bin_window,
4303 if (draw_hgrid_lines)
4305 tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node) + grid_line_width / 2;
4306 tmp_height = ROW_HEIGHT (tree_view) - grid_line_width;
4310 tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node);
4311 tmp_height = ROW_HEIGHT (tree_view);
4314 if (row_ending_details)
4315 gtk_paint_focus (widget->style,
4316 tree_view->priv->bin_window,
4321 ? (is_last ? "treeview" : "treeview-left" )
4322 : (is_last ? "treeview-right" : "treeview-middle" )),
4326 gtk_paint_focus (widget->style,
4327 tree_view->priv->bin_window,
4336 y_offset += max_height;
4340 node = pspp_sheet_view_node_next (tree_view, node);
4343 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
4347 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
4354 while (y_offset < event->area.height);
4357 pspp_sheet_view_draw_grid_lines (tree_view, event, n_visible_columns,
4360 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4362 GdkRectangle *rectangles;
4365 gdk_region_get_rectangles (event->region,
4369 while (n_rectangles--)
4370 pspp_sheet_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
4372 g_free (rectangles);
4376 gtk_tree_path_free (cursor_path);
4379 gtk_tree_path_free (drag_dest_path);
4385 pspp_sheet_view_expose (GtkWidget *widget,
4386 GdkEventExpose *event)
4388 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4390 if (event->window == tree_view->priv->bin_window)
4395 retval = pspp_sheet_view_bin_expose (widget, event);
4397 /* We can't just chain up to Container::expose as it will try to send the
4398 * event to the headers, so we handle propagating it to our children
4399 * (eg. widgets being edited) ourselves.
4401 tmp_list = tree_view->priv->children;
4404 PsppSheetViewChild *child = tmp_list->data;
4405 tmp_list = tmp_list->next;
4407 gtk_container_propagate_expose (GTK_CONTAINER (tree_view), child->widget, event);
4413 else if (event->window == tree_view->priv->header_window)
4415 gint n_visible_columns;
4418 gtk_paint_flat_box (widget->style,
4428 event->area.height);
4430 for (list = tree_view->priv->columns; list != NULL; list = list->next)
4432 PsppSheetViewColumn *column = list->data;
4434 if (column == tree_view->priv->drag_column || !column->visible)
4437 if (span_intersects (column->allocation.x, column->allocation.width,
4438 event->area.x, event->area.width)
4439 && column->button != NULL)
4440 gtk_container_propagate_expose (GTK_CONTAINER (tree_view),
4441 column->button, event);
4444 n_visible_columns = 0;
4445 for (list = tree_view->priv->columns; list; list = list->next)
4447 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4449 n_visible_columns ++;
4451 pspp_sheet_view_draw_grid_lines (tree_view,
4455 event->area.height);
4457 else if (event->window == tree_view->priv->drag_window)
4459 gtk_container_propagate_expose (GTK_CONTAINER (tree_view),
4460 tree_view->priv->drag_column->button,
4474 /* returns 0x1 when no column has been found -- yes it's hackish */
4475 static PsppSheetViewColumn *
4476 pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
4477 PsppSheetViewColumn *column,
4480 PsppSheetViewColumn *left_column = NULL;
4481 PsppSheetViewColumn *cur_column = NULL;
4484 if (!column->reorderable)
4485 return (PsppSheetViewColumn *)0x1;
4487 switch (drop_position)
4490 /* find first column where we can drop */
4491 tmp_list = tree_view->priv->columns;
4492 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4493 return (PsppSheetViewColumn *)0x1;
4497 g_assert (tmp_list);
4499 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4500 tmp_list = tmp_list->next;
4502 if (left_column && left_column->visible == FALSE)
4505 if (!tree_view->priv->column_drop_func)
4508 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4510 left_column = cur_column;
4517 if (!tree_view->priv->column_drop_func)
4520 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4523 return (PsppSheetViewColumn *)0x1;
4527 /* find first column after column where we can drop */
4528 tmp_list = tree_view->priv->columns;
4530 for (; tmp_list; tmp_list = tmp_list->next)
4531 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4534 if (!tmp_list || !tmp_list->next)
4535 return (PsppSheetViewColumn *)0x1;
4537 tmp_list = tmp_list->next;
4538 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4539 tmp_list = tmp_list->next;
4543 g_assert (tmp_list);
4545 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4546 tmp_list = tmp_list->next;
4548 if (left_column && left_column->visible == FALSE)
4550 left_column = cur_column;
4552 tmp_list = tmp_list->next;
4556 if (!tree_view->priv->column_drop_func)
4559 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4561 left_column = cur_column;
4568 if (!tree_view->priv->column_drop_func)
4571 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4574 return (PsppSheetViewColumn *)0x1;
4578 /* find first column before column where we can drop */
4579 tmp_list = tree_view->priv->columns;
4581 for (; tmp_list; tmp_list = tmp_list->next)
4582 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4585 if (!tmp_list || !tmp_list->prev)
4586 return (PsppSheetViewColumn *)0x1;
4588 tmp_list = tmp_list->prev;
4589 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4590 tmp_list = tmp_list->prev;
4594 g_assert (tmp_list);
4596 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4598 if (left_column && !left_column->visible)
4600 /*if (!tmp_list->prev)
4601 return (PsppSheetViewColumn *)0x1;
4604 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4605 tmp_list = tmp_list->prev->prev;
4608 cur_column = left_column;
4610 tmp_list = tmp_list->prev;
4614 if (!tree_view->priv->column_drop_func)
4617 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4620 cur_column = left_column;
4621 tmp_list = tmp_list->prev;
4624 if (!tree_view->priv->column_drop_func)
4627 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4630 return (PsppSheetViewColumn *)0x1;
4634 /* same as DROP_HOME case, but doing it backwards */
4635 tmp_list = g_list_last (tree_view->priv->columns);
4638 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4639 return (PsppSheetViewColumn *)0x1;
4643 g_assert (tmp_list);
4645 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4647 if (left_column && !left_column->visible)
4649 cur_column = left_column;
4650 tmp_list = tmp_list->prev;
4653 if (!tree_view->priv->column_drop_func)
4656 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4659 cur_column = left_column;
4660 tmp_list = tmp_list->prev;
4663 if (!tree_view->priv->column_drop_func)
4666 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4669 return (PsppSheetViewColumn *)0x1;
4673 return (PsppSheetViewColumn *)0x1;
4677 pspp_sheet_view_key_press (GtkWidget *widget,
4680 PsppSheetView *tree_view = (PsppSheetView *) widget;
4682 if (tree_view->priv->rubber_band_status)
4684 if (event->keyval == GDK_Escape)
4685 pspp_sheet_view_stop_rubber_band (tree_view);
4690 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4692 if (event->keyval == GDK_Escape)
4694 tree_view->priv->cur_reorder = NULL;
4695 pspp_sheet_view_button_release_drag_column (widget, NULL);
4700 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4702 GList *focus_column;
4705 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4707 for (focus_column = tree_view->priv->columns;
4709 focus_column = focus_column->next)
4711 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4713 if (column->button && gtk_widget_has_focus (column->button))
4718 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4719 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4720 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4722 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4724 if (!column->resizable)
4726 gtk_widget_error_bell (widget);
4730 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4731 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4733 gint old_width = column->resized_width;
4735 column->resized_width = MAX (column->resized_width,
4737 column->resized_width -= 2;
4738 if (column->resized_width < 0)
4739 column->resized_width = 0;
4741 if (column->min_width == -1)
4742 column->resized_width = MAX (column->button_request,
4743 column->resized_width);
4745 column->resized_width = MAX (column->min_width,
4746 column->resized_width);
4748 if (column->max_width != -1)
4749 column->resized_width = MIN (column->resized_width,
4752 column->use_resized_width = TRUE;
4754 if (column->resized_width != old_width)
4755 gtk_widget_queue_resize (widget);
4757 gtk_widget_error_bell (widget);
4759 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4760 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4762 gint old_width = column->resized_width;
4764 column->resized_width = MAX (column->resized_width,
4766 column->resized_width += 2;
4768 if (column->max_width != -1)
4769 column->resized_width = MIN (column->resized_width,
4772 column->use_resized_width = TRUE;
4774 if (column->resized_width != old_width)
4775 gtk_widget_queue_resize (widget);
4777 gtk_widget_error_bell (widget);
4784 (event->state & GDK_MOD1_MASK) &&
4785 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4786 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4787 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4788 || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4790 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4792 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4793 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4795 PsppSheetViewColumn *col;
4796 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
4797 if (col != (PsppSheetViewColumn *)0x1)
4798 pspp_sheet_view_move_column_after (tree_view, column, col);
4800 gtk_widget_error_bell (widget);
4802 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4803 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4805 PsppSheetViewColumn *col;
4806 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4807 if (col != (PsppSheetViewColumn *)0x1)
4808 pspp_sheet_view_move_column_after (tree_view, column, col);
4810 gtk_widget_error_bell (widget);
4812 else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4814 PsppSheetViewColumn *col;
4815 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4816 if (col != (PsppSheetViewColumn *)0x1)
4817 pspp_sheet_view_move_column_after (tree_view, column, col);
4819 gtk_widget_error_bell (widget);
4821 else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4823 PsppSheetViewColumn *col;
4824 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4825 if (col != (PsppSheetViewColumn *)0x1)
4826 pspp_sheet_view_move_column_after (tree_view, column, col);
4828 gtk_widget_error_bell (widget);
4835 /* Chain up to the parent class. It handles the keybindings. */
4836 if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4839 if (tree_view->priv->search_entry_avoid_unhandled_binding)
4841 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4845 /* We pass the event to the search_entry. If its text changes, then we start
4846 * the typeahead find capabilities. */
4847 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4848 && tree_view->priv->enable_search
4849 && !tree_view->priv->search_custom_entry_set)
4851 GdkEvent *new_event;
4853 const char *new_text;
4856 gboolean text_modified;
4857 gulong popup_menu_id;
4859 pspp_sheet_view_ensure_interactive_directory (tree_view);
4861 /* Make a copy of the current text */
4862 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4863 new_event = gdk_event_copy ((GdkEvent *) event);
4864 g_object_unref (((GdkEventKey *) new_event)->window);
4865 ((GdkEventKey *) new_event)->window = g_object_ref (tree_view->priv->search_window->window);
4866 gtk_widget_realize (tree_view->priv->search_window);
4868 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
4869 "popup-menu", G_CALLBACK (gtk_true),
4872 /* Move the entry off screen */
4873 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4874 gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4875 gdk_screen_get_width (screen) + 1,
4876 gdk_screen_get_height (screen) + 1);
4877 gtk_widget_show (tree_view->priv->search_window);
4879 /* Send the event to the window. If the preedit_changed signal is emitted
4880 * during this event, we will set priv->imcontext_changed */
4881 tree_view->priv->imcontext_changed = FALSE;
4882 retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4883 gdk_event_free (new_event);
4884 gtk_widget_hide (tree_view->priv->search_window);
4886 g_signal_handler_disconnect (tree_view->priv->search_entry,
4889 /* We check to make sure that the entry tried to handle the text, and that
4890 * the text has changed.
4892 new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
4893 text_modified = strcmp (old_text, new_text) != 0;
4895 if (tree_view->priv->imcontext_changed || /* we're in a preedit */
4896 (retval && text_modified)) /* ...or the text was modified */
4898 if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
4900 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
4905 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
4915 pspp_sheet_view_key_release (GtkWidget *widget,
4918 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4920 if (tree_view->priv->rubber_band_status)
4923 return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
4926 /* FIXME Is this function necessary? Can I get an enter_notify event
4927 * w/o either an expose event or a mouse motion event?
4930 pspp_sheet_view_enter_notify (GtkWidget *widget,
4931 GdkEventCrossing *event)
4933 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4937 /* Sanity check it */
4938 if (event->window != tree_view->priv->bin_window)
4941 if (tree_view->priv->row_count == 0)
4944 if (event->mode == GDK_CROSSING_GRAB ||
4945 event->mode == GDK_CROSSING_GTK_GRAB ||
4946 event->mode == GDK_CROSSING_GTK_UNGRAB ||
4947 event->mode == GDK_CROSSING_STATE_CHANGED)
4950 /* find the node internally */
4951 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
4954 pspp_sheet_view_find_offset (tree_view, new_y, &node);
4956 tree_view->priv->event_last_x = event->x;
4957 tree_view->priv->event_last_y = event->y;
4959 prelight_or_select (tree_view, node, event->x, event->y);
4965 pspp_sheet_view_leave_notify (GtkWidget *widget,
4966 GdkEventCrossing *event)
4968 PsppSheetView *tree_view;
4970 if (event->mode == GDK_CROSSING_GRAB)
4973 tree_view = PSPP_SHEET_VIEW (widget);
4975 if (tree_view->priv->prelight_node >= 0)
4976 _pspp_sheet_view_queue_draw_node (tree_view,
4977 tree_view->priv->prelight_node,
4980 tree_view->priv->event_last_x = -10000;
4981 tree_view->priv->event_last_y = -10000;
4983 prelight_or_select (tree_view,
4985 -1000, -1000); /* coords not possibly over an arrow */
4992 pspp_sheet_view_focus_out (GtkWidget *widget,
4993 GdkEventFocus *event)
4995 PsppSheetView *tree_view;
4997 tree_view = PSPP_SHEET_VIEW (widget);
4999 gtk_widget_queue_draw (widget);
5001 /* destroy interactive search dialog */
5002 if (tree_view->priv->search_window)
5003 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
5009 /* Incremental Reflow
5013 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
5018 y = pspp_sheet_view_node_find_offset (tree_view, node)
5019 - tree_view->priv->vadjustment->value
5020 + TREE_VIEW_HEADER_HEIGHT (tree_view);
5022 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
5024 GTK_WIDGET (tree_view)->allocation.width,
5025 tree_view->priv->fixed_height);
5029 node_is_visible (PsppSheetView *tree_view,
5035 y = pspp_sheet_view_node_find_offset (tree_view, node);
5036 height = ROW_HEIGHT (tree_view);
5038 if (y >= tree_view->priv->vadjustment->value &&
5039 y + height <= (tree_view->priv->vadjustment->value
5040 + tree_view->priv->vadjustment->page_size))
5046 /* Returns the row height. */
5048 validate_row (PsppSheetView *tree_view,
5053 PsppSheetViewColumn *column;
5054 GList *list, *first_column, *last_column;
5056 gint horizontal_separator;
5057 gint vertical_separator;
5058 gint focus_line_width;
5059 gboolean draw_vgrid_lines, draw_hgrid_lines;
5061 gint grid_line_width;
5062 gboolean wide_separators;
5063 gint separator_height;
5065 gtk_widget_style_get (GTK_WIDGET (tree_view),
5066 "focus-padding", &focus_pad,
5067 "focus-line-width", &focus_line_width,
5068 "horizontal-separator", &horizontal_separator,
5069 "vertical-separator", &vertical_separator,
5070 "grid-line-width", &grid_line_width,
5071 "wide-separators", &wide_separators,
5072 "separator-height", &separator_height,
5076 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5077 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5079 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5080 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5082 for (last_column = g_list_last (tree_view->priv->columns);
5083 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5084 last_column = last_column->prev)
5087 for (first_column = g_list_first (tree_view->priv->columns);
5088 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5089 first_column = first_column->next)
5092 for (list = tree_view->priv->columns; list; list = list->next)
5097 column = list->data;
5099 if (! column->visible)
5102 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5103 pspp_sheet_view_column_cell_get_size (column,
5105 &tmp_width, &tmp_height);
5107 tmp_height += vertical_separator;
5108 height = MAX (height, tmp_height);
5110 tmp_width = tmp_width + horizontal_separator;
5112 if (draw_vgrid_lines)
5114 if (list->data == first_column || list->data == last_column)
5115 tmp_width += grid_line_width / 2.0;
5117 tmp_width += grid_line_width;
5120 if (tmp_width > column->requested_width)
5121 column->requested_width = tmp_width;
5124 if (draw_hgrid_lines)
5125 height += grid_line_width;
5127 tree_view->priv->post_validation_flag = TRUE;
5133 validate_visible_area (PsppSheetView *tree_view)
5135 GtkTreePath *path = NULL;
5136 GtkTreePath *above_path = NULL;
5139 gboolean size_changed = FALSE;
5141 gint area_above = 0;
5142 gint area_below = 0;
5144 if (tree_view->priv->row_count == 0)
5147 if (tree_view->priv->scroll_to_path == NULL)
5150 total_height = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5152 if (total_height == 0)
5155 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5158 /* we are going to scroll, and will update dy */
5159 _pspp_sheet_view_find_node (tree_view, path, &node);
5160 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5162 if (tree_view->priv->scroll_to_use_align)
5164 gint height = ROW_HEIGHT (tree_view);
5165 area_above = (total_height - height) *
5166 tree_view->priv->scroll_to_row_align;
5167 area_below = total_height - area_above - height;
5168 area_above = MAX (area_above, 0);
5169 area_below = MAX (area_below, 0);
5174 * 1) row not visible
5178 gint height = ROW_HEIGHT (tree_view);
5180 dy = pspp_sheet_view_node_find_offset (tree_view, node);
5182 if (dy >= tree_view->priv->vadjustment->value &&
5183 dy + height <= (tree_view->priv->vadjustment->value
5184 + tree_view->priv->vadjustment->page_size))
5186 /* row visible: keep the row at the same position */
5187 area_above = dy - tree_view->priv->vadjustment->value;
5188 area_below = (tree_view->priv->vadjustment->value +
5189 tree_view->priv->vadjustment->page_size)
5194 /* row not visible */
5196 && dy + height <= tree_view->priv->vadjustment->page_size)
5198 /* row at the beginning -- fixed */
5200 area_below = tree_view->priv->vadjustment->page_size
5201 - area_above - height;
5203 else if (dy >= (tree_view->priv->vadjustment->upper -
5204 tree_view->priv->vadjustment->page_size))
5206 /* row at the end -- fixed */
5207 area_above = dy - (tree_view->priv->vadjustment->upper -
5208 tree_view->priv->vadjustment->page_size);
5209 area_below = tree_view->priv->vadjustment->page_size -
5210 area_above - height;
5214 area_above = tree_view->priv->vadjustment->page_size - height;
5220 /* row somewhere in the middle, bring it to the top
5224 area_below = total_height - height;
5230 /* the scroll to isn't valid; ignore it.
5233 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5234 tree_view->priv->scroll_to_path = NULL;
5238 above_path = gtk_tree_path_copy (path);
5240 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5241 * backwards is much slower then forward, as there is no iter_prev function.
5242 * We go forwards first in case we run out of tree. Then we go backwards to
5245 while (node >= 0 && area_below > 0)
5247 gboolean done = FALSE;
5250 node = pspp_sheet_view_node_next (tree_view, node);
5253 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5255 gtk_tree_path_next (path);
5258 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5268 area_below -= ROW_HEIGHT (tree_view);
5270 gtk_tree_path_free (path);
5272 /* If we ran out of tree, and have extra area_below left, we need to add it
5275 area_above += area_below;
5277 _pspp_sheet_view_find_node (tree_view, above_path, &node);
5279 /* We walk backwards */
5280 while (area_above > 0)
5282 node = pspp_sheet_view_node_prev (tree_view, node);
5284 /* Always find the new path in the tree. We cannot just assume
5285 * a gtk_tree_path_prev() is enough here, as there might be children
5286 * in between this node and the previous sibling node. If this
5287 * appears to be a performance hotspot in profiles, we can look into
5288 * intrigate logic for keeping path, node and iter in sync like
5289 * we do for forward walks. (Which will be hard because of the lacking
5296 gtk_tree_path_free (above_path);
5297 above_path = _pspp_sheet_view_find_path (tree_view, node);
5299 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5301 area_above -= ROW_HEIGHT (tree_view);
5304 /* set the dy here to scroll to the path,
5305 * and sync the top row accordingly
5307 pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5308 pspp_sheet_view_top_row_to_dy (tree_view);
5310 /* update width/height and queue a resize */
5313 GtkRequisition requisition;
5315 /* We temporarily guess a size, under the assumption that it will be the
5316 * same when we get our next size_allocate. If we don't do this, we'll be
5317 * in an inconsistent state if we call top_row_to_dy. */
5319 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5320 tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width);
5321 tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height);
5322 gtk_adjustment_changed (tree_view->priv->hadjustment);
5323 gtk_adjustment_changed (tree_view->priv->vadjustment);
5324 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5327 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5328 tree_view->priv->scroll_to_path = NULL;
5331 gtk_tree_path_free (above_path);
5333 if (tree_view->priv->scroll_to_column)
5335 tree_view->priv->scroll_to_column = NULL;
5337 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5341 initialize_fixed_height_mode (PsppSheetView *tree_view)
5343 if (!tree_view->priv->row_count)
5346 if (tree_view->priv->fixed_height_set)
5349 if (tree_view->priv->fixed_height < 0)
5356 path = _pspp_sheet_view_find_path (tree_view, node);
5357 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5359 tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5361 gtk_tree_path_free (path);
5363 g_object_notify (G_OBJECT (tree_view), "fixed-height");
5367 /* Our strategy for finding nodes to validate is a little convoluted. We find
5368 * the left-most uninvalidated node. We then try walking right, validating
5369 * nodes. Once we find a valid node, we repeat the previous process of finding
5370 * the first invalid node.
5374 validate_rows_handler (PsppSheetView *tree_view)
5376 initialize_fixed_height_mode (tree_view);
5377 if (tree_view->priv->validate_rows_timer)
5379 g_source_remove (tree_view->priv->validate_rows_timer);
5380 tree_view->priv->validate_rows_timer = 0;
5387 do_presize_handler (PsppSheetView *tree_view)
5389 GtkRequisition requisition;
5391 validate_visible_area (tree_view);
5392 tree_view->priv->presize_handler_timer = 0;
5394 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5397 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5399 tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width);
5400 tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height);
5401 gtk_adjustment_changed (tree_view->priv->hadjustment);
5402 gtk_adjustment_changed (tree_view->priv->vadjustment);
5403 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5409 presize_handler_callback (gpointer data)
5411 do_presize_handler (PSPP_SHEET_VIEW (data));
5417 install_presize_handler (PsppSheetView *tree_view)
5419 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5422 if (! tree_view->priv->presize_handler_timer)
5424 tree_view->priv->presize_handler_timer =
5425 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5427 if (! tree_view->priv->validate_rows_timer)
5429 tree_view->priv->validate_rows_timer =
5430 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5435 scroll_sync_handler (PsppSheetView *tree_view)
5437 if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size)
5438 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5439 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5440 pspp_sheet_view_top_row_to_dy (tree_view);
5442 pspp_sheet_view_dy_to_top_row (tree_view);
5444 tree_view->priv->scroll_sync_timer = 0;
5450 install_scroll_sync_handler (PsppSheetView *tree_view)
5452 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5455 if (!tree_view->priv->scroll_sync_timer)
5457 tree_view->priv->scroll_sync_timer =
5458 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5463 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5467 gtk_tree_row_reference_free (tree_view->priv->top_row);
5471 tree_view->priv->top_row = NULL;
5472 tree_view->priv->top_row_dy = 0;
5476 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5477 tree_view->priv->top_row_dy = offset;
5481 /* Always call this iff dy is in the visible range. If the tree is empty, then
5482 * it's set to be NULL, and top_row_dy is 0;
5485 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5491 if (tree_view->priv->row_count == 0)
5493 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5497 offset = pspp_sheet_view_find_offset (tree_view,
5498 tree_view->priv->dy,
5503 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5507 path = _pspp_sheet_view_find_path (tree_view, node);
5508 pspp_sheet_view_set_top_row (tree_view, path, offset);
5509 gtk_tree_path_free (path);
5515 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5521 /* Avoid recursive calls */
5522 if (tree_view->priv->in_top_row_to_dy)
5525 if (tree_view->priv->top_row)
5526 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5533 _pspp_sheet_view_find_node (tree_view, path, &node);
5536 gtk_tree_path_free (path);
5540 /* keep dy and set new toprow */
5541 gtk_tree_row_reference_free (tree_view->priv->top_row);
5542 tree_view->priv->top_row = NULL;
5543 tree_view->priv->top_row_dy = 0;
5544 /* DO NOT install the idle handler */
5545 pspp_sheet_view_dy_to_top_row (tree_view);
5549 if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5551 /* new top row -- do NOT install the idle handler */
5552 pspp_sheet_view_dy_to_top_row (tree_view);
5556 new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5557 new_dy += tree_view->priv->top_row_dy;
5559 if (new_dy + tree_view->priv->vadjustment->page_size > tree_view->priv->height)
5560 new_dy = tree_view->priv->height - tree_view->priv->vadjustment->page_size;
5562 new_dy = MAX (0, new_dy);
5564 tree_view->priv->in_top_row_to_dy = TRUE;
5565 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5566 tree_view->priv->in_top_row_to_dy = FALSE;
5571 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5573 install_presize_handler (tree_view);
5579 set_source_row (GdkDragContext *context,
5580 GtkTreeModel *model,
5581 GtkTreePath *source_row)
5583 g_object_set_data_full (G_OBJECT (context),
5584 "gtk-tree-view-source-row",
5585 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5586 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5590 get_source_row (GdkDragContext *context)
5592 GtkTreeRowReference *ref =
5593 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5596 return gtk_tree_row_reference_get_path (ref);
5603 GtkTreeRowReference *dest_row;
5604 guint path_down_mode : 1;
5605 guint empty_view_drop : 1;
5606 guint drop_append_mode : 1;
5611 dest_row_free (gpointer data)
5613 DestRow *dr = (DestRow *)data;
5615 gtk_tree_row_reference_free (dr->dest_row);
5616 g_slice_free (DestRow, dr);
5620 set_dest_row (GdkDragContext *context,
5621 GtkTreeModel *model,
5622 GtkTreePath *dest_row,
5623 gboolean path_down_mode,
5624 gboolean empty_view_drop,
5625 gboolean drop_append_mode)
5631 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5636 dr = g_slice_new (DestRow);
5638 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5639 dr->path_down_mode = path_down_mode != FALSE;
5640 dr->empty_view_drop = empty_view_drop != FALSE;
5641 dr->drop_append_mode = drop_append_mode != FALSE;
5643 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5644 dr, (GDestroyNotify) dest_row_free);
5648 get_dest_row (GdkDragContext *context,
5649 gboolean *path_down_mode)
5652 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5656 GtkTreePath *path = NULL;
5659 *path_down_mode = dr->path_down_mode;
5662 path = gtk_tree_row_reference_get_path (dr->dest_row);
5663 else if (dr->empty_view_drop)
5664 path = gtk_tree_path_new_from_indices (0, -1);
5668 if (path && dr->drop_append_mode)
5669 gtk_tree_path_next (path);
5677 /* Get/set whether drag_motion requested the drag data and
5678 * drag_data_received should thus not actually insert the data,
5679 * since the data doesn't result from a drop.
5682 set_status_pending (GdkDragContext *context,
5683 GdkDragAction suggested_action)
5685 g_object_set_data (G_OBJECT (context),
5686 "gtk-tree-view-status-pending",
5687 GINT_TO_POINTER (suggested_action));
5690 static GdkDragAction
5691 get_status_pending (GdkDragContext *context)
5693 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5694 "gtk-tree-view-status-pending"));
5697 static TreeViewDragInfo*
5698 get_info (PsppSheetView *tree_view)
5700 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5704 destroy_info (TreeViewDragInfo *di)
5706 g_slice_free (TreeViewDragInfo, di);
5709 static TreeViewDragInfo*
5710 ensure_info (PsppSheetView *tree_view)
5712 TreeViewDragInfo *di;
5714 di = get_info (tree_view);
5718 di = g_slice_new0 (TreeViewDragInfo);
5720 g_object_set_data_full (G_OBJECT (tree_view),
5721 "gtk-tree-view-drag-info",
5723 (GDestroyNotify) destroy_info);
5730 remove_info (PsppSheetView *tree_view)
5732 g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5737 drag_scan_timeout (gpointer data)
5739 PsppSheetView *tree_view;
5741 GdkModifierType state;
5742 GtkTreePath *path = NULL;
5743 PsppSheetViewColumn *column = NULL;
5744 GdkRectangle visible_rect;
5746 GDK_THREADS_ENTER ();
5748 tree_view = PSPP_SHEET_VIEW (data);
5750 gdk_window_get_pointer (tree_view->priv->bin_window,
5753 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5755 /* See if we are near the edge. */
5756 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5757 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5758 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5759 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5761 pspp_sheet_view_get_path_at_pos (tree_view,
5762 tree_view->priv->bin_window,
5771 pspp_sheet_view_scroll_to_cell (tree_view,
5777 gtk_tree_path_free (path);
5781 GDK_THREADS_LEAVE ();
5788 add_scroll_timeout (PsppSheetView *tree_view)
5790 if (tree_view->priv->scroll_timeout == 0)
5792 tree_view->priv->scroll_timeout =
5793 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5798 remove_scroll_timeout (PsppSheetView *tree_view)
5800 if (tree_view->priv->scroll_timeout != 0)
5802 g_source_remove (tree_view->priv->scroll_timeout);
5803 tree_view->priv->scroll_timeout = 0;
5808 check_model_dnd (GtkTreeModel *model,
5809 GType required_iface,
5810 const gchar *signal)
5812 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5814 g_warning ("You must override the default '%s' handler "
5815 "on PsppSheetView when using models that don't support "
5816 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5817 "is to connect to '%s' and call "
5818 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5819 "the default handler from running. Look at the source code "
5820 "for the default handler in gtktreeview.c to get an idea what "
5821 "your handler should do. (gtktreeview.c is in the GTK source "
5822 "code.) If you're using GTK from a language other than C, "
5823 "there may be a more natural way to override default handlers, e.g. via derivation.",
5824 signal, g_type_name (required_iface), signal);
5832 scroll_row_timeout (gpointer data)
5834 PsppSheetView *tree_view = data;
5836 pspp_sheet_view_horizontal_autoscroll (tree_view);
5837 pspp_sheet_view_vertical_autoscroll (tree_view);
5839 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5840 pspp_sheet_view_update_rubber_band (tree_view);
5845 /* Returns TRUE if event should not be propagated to parent widgets */
5847 set_destination_row (PsppSheetView *tree_view,
5848 GdkDragContext *context,
5849 /* coordinates relative to the widget */
5852 GdkDragAction *suggested_action,
5855 GtkTreePath *path = NULL;
5856 PsppSheetViewDropPosition pos;
5857 PsppSheetViewDropPosition old_pos;
5858 TreeViewDragInfo *di;
5860 GtkTreePath *old_dest_path = NULL;
5861 gboolean can_drop = FALSE;
5863 *suggested_action = 0;
5866 widget = GTK_WIDGET (tree_view);
5868 di = get_info (tree_view);
5870 if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5872 /* someone unset us as a drag dest, note that if
5873 * we return FALSE drag_leave isn't called
5876 pspp_sheet_view_set_drag_dest_row (tree_view,
5878 PSPP_SHEET_VIEW_DROP_BEFORE);
5880 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5882 return FALSE; /* no longer a drop site */
5885 *target = gtk_drag_dest_find_target (widget, context,
5886 gtk_drag_dest_get_target_list (widget));
5887 if (*target == GDK_NONE)
5892 if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
5898 GtkTreeModel *model;
5900 /* the row got dropped on empty space, let's setup a special case
5904 gtk_tree_path_free (path);
5906 model = pspp_sheet_view_get_model (tree_view);
5908 n_children = gtk_tree_model_iter_n_children (model, NULL);
5911 pos = PSPP_SHEET_VIEW_DROP_AFTER;
5912 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
5916 pos = PSPP_SHEET_VIEW_DROP_BEFORE;
5917 path = gtk_tree_path_new_from_indices (0, -1);
5927 /* If we left the current row's "open" zone, unset the timeout for
5930 pspp_sheet_view_get_drag_dest_row (tree_view,
5935 gtk_tree_path_free (old_dest_path);
5937 if (TRUE /* FIXME if the location droppable predicate */)
5945 GtkWidget *source_widget;
5947 *suggested_action = context->suggested_action;
5948 source_widget = gtk_drag_get_source_widget (context);
5950 if (source_widget == widget)
5952 /* Default to MOVE, unless the user has
5953 * pressed ctrl or shift to affect available actions
5955 if ((context->actions & GDK_ACTION_MOVE) != 0)
5956 *suggested_action = GDK_ACTION_MOVE;
5959 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5964 /* can't drop here */
5965 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5967 PSPP_SHEET_VIEW_DROP_BEFORE);
5971 gtk_tree_path_free (path);
5977 get_logical_dest_row (PsppSheetView *tree_view,
5978 gboolean *path_down_mode,
5979 gboolean *drop_append_mode)
5981 /* adjust path to point to the row the drop goes in front of */
5982 GtkTreePath *path = NULL;
5983 PsppSheetViewDropPosition pos;
5985 g_return_val_if_fail (path_down_mode != NULL, NULL);
5986 g_return_val_if_fail (drop_append_mode != NULL, NULL);
5988 *path_down_mode = FALSE;
5989 *drop_append_mode = 0;
5991 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
5996 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
5998 else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
5999 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
6000 *path_down_mode = TRUE;
6004 GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
6006 g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
6008 if (!gtk_tree_model_get_iter (model, &iter, path) ||
6009 !gtk_tree_model_iter_next (model, &iter))
6010 *drop_append_mode = 1;
6013 *drop_append_mode = 0;
6014 gtk_tree_path_next (path);
6022 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
6023 GdkEventMotion *event)
6025 GtkWidget *widget = GTK_WIDGET (tree_view);
6026 GdkDragContext *context;
6027 TreeViewDragInfo *di;
6028 GtkTreePath *path = NULL;
6030 gint cell_x, cell_y;
6031 GtkTreeModel *model;
6032 gboolean retval = FALSE;
6034 di = get_info (tree_view);
6036 if (di == NULL || !di->source_set)
6039 if (tree_view->priv->pressed_button < 0)
6042 if (!gtk_drag_check_threshold (widget,
6043 tree_view->priv->press_start_x,
6044 tree_view->priv->press_start_y,
6045 event->x, event->y))
6048 model = pspp_sheet_view_get_model (tree_view);
6053 button = tree_view->priv->pressed_button;
6054 tree_view->priv->pressed_button = -1;
6056 pspp_sheet_view_get_path_at_pos (tree_view,
6057 tree_view->priv->press_start_x,
6058 tree_view->priv->press_start_y,
6067 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6068 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6072 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6075 /* Now we can begin the drag */
6079 context = gtk_drag_begin (widget,
6080 gtk_drag_source_get_target_list (widget),
6085 set_source_row (context, model, path);
6089 gtk_tree_path_free (path);
6096 pspp_sheet_view_drag_begin (GtkWidget *widget,
6097 GdkDragContext *context)
6099 PsppSheetView *tree_view;
6100 GtkTreePath *path = NULL;
6101 gint cell_x, cell_y;
6103 TreeViewDragInfo *di;
6105 tree_view = PSPP_SHEET_VIEW (widget);
6107 /* if the user uses a custom DND source impl, we don't set the icon here */
6108 di = get_info (tree_view);
6110 if (di == NULL || !di->source_set)
6113 pspp_sheet_view_get_path_at_pos (tree_view,
6114 tree_view->priv->press_start_x,
6115 tree_view->priv->press_start_y,
6121 g_return_if_fail (path != NULL);
6123 row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6126 gtk_drag_set_icon_pixmap (context,
6127 gdk_drawable_get_colormap (row_pix),
6130 /* the + 1 is for the black border in the icon */
6131 tree_view->priv->press_start_x + 1,
6134 g_object_unref (row_pix);
6135 gtk_tree_path_free (path);
6139 pspp_sheet_view_drag_end (GtkWidget *widget,
6140 GdkDragContext *context)
6145 /* Default signal implementations for the drag signals */
6147 pspp_sheet_view_drag_data_get (GtkWidget *widget,
6148 GdkDragContext *context,
6149 GtkSelectionData *selection_data,
6153 PsppSheetView *tree_view;
6154 GtkTreeModel *model;
6155 TreeViewDragInfo *di;
6156 GtkTreePath *source_row;
6158 tree_view = PSPP_SHEET_VIEW (widget);
6160 model = pspp_sheet_view_get_model (tree_view);
6165 di = get_info (PSPP_SHEET_VIEW (widget));
6170 source_row = get_source_row (context);
6172 if (source_row == NULL)
6175 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6176 * any model; for DragSource models there are some other targets
6180 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6181 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6186 /* If drag_data_get does nothing, try providing row data. */
6187 if (selection_data->target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6189 gtk_tree_set_row_drag_data (selection_data,
6195 gtk_tree_path_free (source_row);
6200 pspp_sheet_view_drag_data_delete (GtkWidget *widget,
6201 GdkDragContext *context)
6203 TreeViewDragInfo *di;
6204 GtkTreeModel *model;
6205 PsppSheetView *tree_view;
6206 GtkTreePath *source_row;
6208 tree_view = PSPP_SHEET_VIEW (widget);
6209 model = pspp_sheet_view_get_model (tree_view);
6211 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6214 di = get_info (tree_view);
6219 source_row = get_source_row (context);
6221 if (source_row == NULL)
6224 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6227 gtk_tree_path_free (source_row);
6229 set_source_row (context, NULL, NULL);
6233 pspp_sheet_view_drag_leave (GtkWidget *widget,
6234 GdkDragContext *context,
6237 /* unset any highlight row */
6238 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6240 PSPP_SHEET_VIEW_DROP_BEFORE);
6242 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6247 pspp_sheet_view_drag_motion (GtkWidget *widget,
6248 GdkDragContext *context,
6249 /* coordinates relative to the widget */
6255 GtkTreePath *path = NULL;
6256 PsppSheetViewDropPosition pos;
6257 PsppSheetView *tree_view;
6258 GdkDragAction suggested_action = 0;
6261 tree_view = PSPP_SHEET_VIEW (widget);
6263 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6266 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6268 /* we only know this *after* set_desination_row */
6269 empty = tree_view->priv->empty_view_drop;
6271 if (path == NULL && !empty)
6273 /* Can't drop here. */
6274 gdk_drag_status (context, 0, time);
6278 if (tree_view->priv->open_dest_timeout == 0 &&
6279 (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6280 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6286 add_scroll_timeout (tree_view);
6289 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6291 /* Request data so we can use the source row when
6292 * determining whether to accept the drop
6294 set_status_pending (context, suggested_action);
6295 gtk_drag_get_data (widget, context, target, time);
6299 set_status_pending (context, 0);
6300 gdk_drag_status (context, suggested_action, time);
6305 gtk_tree_path_free (path);
6312 pspp_sheet_view_drag_drop (GtkWidget *widget,
6313 GdkDragContext *context,
6314 /* coordinates relative to the widget */
6319 PsppSheetView *tree_view;
6321 GdkDragAction suggested_action = 0;
6322 GdkAtom target = GDK_NONE;
6323 TreeViewDragInfo *di;
6324 GtkTreeModel *model;
6325 gboolean path_down_mode;
6326 gboolean drop_append_mode;
6328 tree_view = PSPP_SHEET_VIEW (widget);
6330 model = pspp_sheet_view_get_model (tree_view);
6332 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6334 di = get_info (tree_view);
6339 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6342 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6345 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6347 if (target != GDK_NONE && path != NULL)
6349 /* in case a motion had requested drag data, change things so we
6350 * treat drag data receives as a drop.
6352 set_status_pending (context, 0);
6353 set_dest_row (context, model, path,
6354 path_down_mode, tree_view->priv->empty_view_drop,
6359 gtk_tree_path_free (path);
6361 /* Unset this thing */
6362 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6364 PSPP_SHEET_VIEW_DROP_BEFORE);
6366 if (target != GDK_NONE)
6368 gtk_drag_get_data (widget, context, target, time);
6376 pspp_sheet_view_drag_data_received (GtkWidget *widget,
6377 GdkDragContext *context,
6378 /* coordinates relative to the widget */
6381 GtkSelectionData *selection_data,
6386 TreeViewDragInfo *di;
6387 gboolean accepted = FALSE;
6388 GtkTreeModel *model;
6389 PsppSheetView *tree_view;
6390 GtkTreePath *dest_row;
6391 GdkDragAction suggested_action;
6392 gboolean path_down_mode;
6393 gboolean drop_append_mode;
6395 tree_view = PSPP_SHEET_VIEW (widget);
6397 model = pspp_sheet_view_get_model (tree_view);
6399 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6402 di = get_info (tree_view);
6407 suggested_action = get_status_pending (context);
6409 if (suggested_action)
6411 /* We are getting this data due to a request in drag_motion,
6412 * rather than due to a request in drag_drop, so we are just
6413 * supposed to call drag_status, not actually paste in the
6416 path = get_logical_dest_row (tree_view, &path_down_mode,
6420 suggested_action = 0;
6421 else if (path_down_mode)
6422 gtk_tree_path_down (path);
6424 if (suggested_action)
6426 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6432 path_down_mode = FALSE;
6433 gtk_tree_path_up (path);
6435 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6438 suggested_action = 0;
6441 suggested_action = 0;
6445 gdk_drag_status (context, suggested_action, time);
6448 gtk_tree_path_free (path);
6450 /* If you can't drop, remove user drop indicator until the next motion */
6451 if (suggested_action == 0)
6452 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6454 PSPP_SHEET_VIEW_DROP_BEFORE);
6459 dest_row = get_dest_row (context, &path_down_mode);
6461 if (dest_row == NULL)
6464 if (selection_data->length >= 0)
6468 gtk_tree_path_down (dest_row);
6469 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6470 dest_row, selection_data))
6471 gtk_tree_path_up (dest_row);
6475 if (selection_data->length >= 0)
6477 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6483 gtk_drag_finish (context,
6485 (context->action == GDK_ACTION_MOVE),
6488 if (gtk_tree_path_get_depth (dest_row) == 1
6489 && gtk_tree_path_get_indices (dest_row)[0] == 0)
6491 /* special special case drag to "0", scroll to first item */
6492 if (!tree_view->priv->scroll_to_path)
6493 pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6496 gtk_tree_path_free (dest_row);
6499 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6504 /* GtkContainer Methods
6509 pspp_sheet_view_remove (GtkContainer *container,
6512 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6513 PsppSheetViewChild *child = NULL;
6516 tmp_list = tree_view->priv->children;
6519 child = tmp_list->data;
6520 if (child->widget == widget)
6522 gtk_widget_unparent (widget);
6524 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6525 g_list_free_1 (tmp_list);
6526 g_slice_free (PsppSheetViewChild, child);
6530 tmp_list = tmp_list->next;
6533 tmp_list = tree_view->priv->columns;
6537 PsppSheetViewColumn *column;
6538 column = tmp_list->data;
6539 if (column->button == widget)
6541 gtk_widget_unparent (widget);
6544 tmp_list = tmp_list->next;
6549 pspp_sheet_view_forall (GtkContainer *container,
6550 gboolean include_internals,
6551 GtkCallback callback,
6552 gpointer callback_data)
6554 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6555 PsppSheetViewChild *child = NULL;
6556 PsppSheetViewColumn *column;
6559 tmp_list = tree_view->priv->children;
6562 child = tmp_list->data;
6563 tmp_list = tmp_list->next;
6565 (* callback) (child->widget, callback_data);
6567 if (include_internals == FALSE)
6570 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6572 column = tmp_list->data;
6575 (* callback) (column->button, callback_data);
6579 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6580 * cells. If so we draw one big row-spanning focus rectangle.
6583 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6587 if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6588 return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6590 for (list = tree_view->priv->columns; list; list = list->next)
6592 if (!((PsppSheetViewColumn *)list->data)->visible)
6594 if (_pspp_sheet_view_column_count_special_cells (list->data))
6602 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6603 PsppSheetViewColumn *focus_column,
6604 gboolean clamp_column_visible)
6606 g_return_if_fail (focus_column != NULL);
6608 tree_view->priv->focus_column = focus_column;
6609 if (!focus_column->button)
6611 pspp_sheet_view_column_set_need_button (focus_column, TRUE);
6612 // g_return_if_fail (focus_column->button != NULL);
6613 if (focus_column->button == NULL)
6617 if (GTK_CONTAINER (tree_view)->focus_child != focus_column->button)
6618 gtk_widget_grab_focus (focus_column->button);
6620 if (clamp_column_visible)
6621 pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6624 /* Returns TRUE if the focus is within the headers, after the focus operation is
6628 pspp_sheet_view_header_focus (PsppSheetView *tree_view,
6629 GtkDirectionType dir,
6630 gboolean clamp_column_visible)
6632 GtkWidget *focus_child;
6633 PsppSheetViewColumn *focus_column;
6634 GList *last_column, *first_column;
6638 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6641 focus_child = GTK_CONTAINER (tree_view)->focus_child;
6643 first_column = tree_view->priv->columns;
6644 while (first_column)
6646 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6648 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6650 first_column = first_column->next;
6653 /* No headers are visible, or are focusable. We can't focus in or out.
6655 if (first_column == NULL)
6658 last_column = g_list_last (tree_view->priv->columns);
6661 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6663 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6665 last_column = last_column->prev;
6669 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6673 case GTK_DIR_TAB_BACKWARD:
6674 case GTK_DIR_TAB_FORWARD:
6677 if (focus_child == NULL)
6679 if (tree_view->priv->focus_column != NULL &&
6680 pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6681 focus_column = tree_view->priv->focus_column;
6683 focus_column = first_column->data;
6684 pspp_sheet_view_focus_column (tree_view, focus_column,
6685 clamp_column_visible);
6692 if (focus_child == NULL)
6694 if (tree_view->priv->focus_column != NULL)
6695 focus_column = tree_view->priv->focus_column;
6696 else if (dir == GTK_DIR_LEFT)
6697 focus_column = last_column->data;
6699 focus_column = first_column->data;
6700 pspp_sheet_view_focus_column (tree_view, focus_column,
6701 clamp_column_visible);
6705 if (gtk_widget_child_focus (focus_child, dir))
6707 /* The focus moves inside the button. */
6708 /* This is probably a great example of bad UI */
6709 if (clamp_column_visible)
6710 pspp_sheet_view_clamp_column_visible (tree_view,
6711 tree_view->priv->focus_column,
6716 /* We need to move the focus among the row of buttons. */
6717 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6718 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6721 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6722 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6724 gtk_widget_error_bell (GTK_WIDGET (tree_view));
6730 PsppSheetViewColumn *column;
6732 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6733 tmp_list = tmp_list->next;
6735 tmp_list = tmp_list->prev;
6737 if (tmp_list == NULL)
6739 g_warning ("Internal button not found");
6742 column = tmp_list->data;
6743 if (column->visible &&
6744 pspp_sheet_view_column_can_focus (column))
6746 pspp_sheet_view_column_set_need_button (column, TRUE);
6749 pspp_sheet_view_focus_column (tree_view, column,
6750 clamp_column_visible);
6758 g_assert_not_reached ();
6765 /* This function returns in 'path' the first focusable path, if the given path
6766 * is already focusable, it's the returned one.
6770 search_first_focusable_path (PsppSheetView *tree_view,
6772 gboolean search_forward,
6775 /* XXX this function is trivial given that the sheetview doesn't support
6779 if (!path || !*path)
6782 _pspp_sheet_view_find_node (tree_view, *path, &node);
6790 return (*path != NULL);
6794 pspp_sheet_view_focus (GtkWidget *widget,
6795 GtkDirectionType direction)
6797 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6798 GtkContainer *container = GTK_CONTAINER (widget);
6799 GtkWidget *focus_child;
6801 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6804 focus_child = container->focus_child;
6806 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6807 /* Case 1. Headers currently have focus. */
6814 pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6816 case GTK_DIR_TAB_BACKWARD:
6819 case GTK_DIR_TAB_FORWARD:
6821 gtk_widget_grab_focus (widget);
6824 g_assert_not_reached ();
6829 /* Case 2. We don't have focus at all. */
6830 if (!gtk_widget_has_focus (widget))
6832 if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6833 gtk_widget_grab_focus (widget);
6837 /* Case 3. We have focus already. */
6838 if (direction == GTK_DIR_TAB_BACKWARD)
6839 return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6840 else if (direction == GTK_DIR_TAB_FORWARD)
6843 /* Other directions caught by the keybindings */
6844 gtk_widget_grab_focus (widget);
6849 pspp_sheet_view_grab_focus (GtkWidget *widget)
6851 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6853 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6857 pspp_sheet_view_style_set (GtkWidget *widget,
6858 GtkStyle *previous_style)
6860 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6862 PsppSheetViewColumn *column;
6864 if (gtk_widget_get_realized (widget))
6867 PsppSheetViewPrivate *priv = PSPP_SHEET_VIEW (widget)->priv;
6869 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
6870 gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
6871 gtk_style_set_background (widget->style, tree_view->priv->header_window, GTK_STATE_NORMAL);
6872 for (i = 0; i < 5 ; ++i)
6874 g_object_unref (priv->grid_line_gc[i]);
6875 priv->grid_line_gc[i] = gdk_gc_new (widget->window);
6876 gdk_gc_copy (priv->grid_line_gc[i], widget->style->text_aa_gc[i]);
6879 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6882 gtk_widget_style_get (widget,
6883 "expander-size", &tree_view->priv->expander_size,
6885 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6887 for (list = tree_view->priv->columns; list; list = list->next)
6889 column = list->data;
6890 _pspp_sheet_view_column_cell_set_dirty (column);
6893 tree_view->priv->fixed_height = -1;
6895 /* Invalidate cached button style. */
6896 if (tree_view->priv->button_style)
6898 g_object_unref (tree_view->priv->button_style);
6899 tree_view->priv->button_style = NULL;
6902 gtk_widget_queue_resize (widget);
6907 pspp_sheet_view_set_focus_child (GtkContainer *container,
6910 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6913 for (list = tree_view->priv->columns; list; list = list->next)
6915 if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
6917 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
6922 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
6926 pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
6927 GtkAdjustment *hadj,
6928 GtkAdjustment *vadj)
6930 gboolean need_adjust = FALSE;
6932 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
6935 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
6937 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6939 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
6941 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6943 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
6945 g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
6946 pspp_sheet_view_adjustment_changed,
6948 g_object_unref (tree_view->priv->hadjustment);
6951 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
6953 g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
6954 pspp_sheet_view_adjustment_changed,
6956 g_object_unref (tree_view->priv->vadjustment);
6959 if (tree_view->priv->hadjustment != hadj)
6961 tree_view->priv->hadjustment = hadj;
6962 g_object_ref_sink (tree_view->priv->hadjustment);
6964 g_signal_connect (tree_view->priv->hadjustment, "value-changed",
6965 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6970 if (tree_view->priv->vadjustment != vadj)
6972 tree_view->priv->vadjustment = vadj;
6973 g_object_ref_sink (tree_view->priv->vadjustment);
6975 g_signal_connect (tree_view->priv->vadjustment, "value-changed",
6976 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6982 pspp_sheet_view_adjustment_changed (NULL, tree_view);
6987 pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
6988 GtkMovementStep step,
6991 PsppSheetSelectMode mode;
6992 GdkModifierType state;
6994 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
6995 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
6996 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
6997 step == GTK_MOVEMENT_DISPLAY_LINES ||
6998 step == GTK_MOVEMENT_PAGES ||
6999 step == GTK_MOVEMENT_BUFFER_ENDS ||
7000 step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
7002 if (tree_view->priv->row_count == 0)
7004 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7007 pspp_sheet_view_stop_editing (tree_view, FALSE);
7008 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7009 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7012 if (gtk_get_current_event_state (&state))
7014 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7015 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
7016 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
7017 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
7019 /* else we assume not pressed */
7023 case GTK_MOVEMENT_LOGICAL_POSITIONS:
7024 pspp_sheet_view_move_cursor_tab (tree_view, count);
7026 case GTK_MOVEMENT_VISUAL_POSITIONS:
7027 pspp_sheet_view_move_cursor_left_right (tree_view, count, mode);
7029 case GTK_MOVEMENT_DISPLAY_LINES:
7030 pspp_sheet_view_move_cursor_up_down (tree_view, count, mode);
7032 case GTK_MOVEMENT_PAGES:
7033 pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode);
7035 case GTK_MOVEMENT_BUFFER_ENDS:
7036 pspp_sheet_view_move_cursor_start_end (tree_view, count, mode);
7038 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7039 pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode);
7042 g_assert_not_reached ();
7049 pspp_sheet_view_put (PsppSheetView *tree_view,
7050 GtkWidget *child_widget,
7051 /* in bin_window coordinates */
7057 PsppSheetViewChild *child;
7059 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7060 g_return_if_fail (GTK_IS_WIDGET (child_widget));
7062 child = g_slice_new (PsppSheetViewChild);
7064 child->widget = child_widget;
7067 child->width = width;
7068 child->height = height;
7070 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7072 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7073 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7075 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7079 _pspp_sheet_view_child_move_resize (PsppSheetView *tree_view,
7081 /* in tree coordinates */
7087 PsppSheetViewChild *child = NULL;
7089 GdkRectangle allocation;
7091 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7092 g_return_if_fail (GTK_IS_WIDGET (widget));
7094 for (list = tree_view->priv->children; list; list = list->next)
7096 if (((PsppSheetViewChild *)list->data)->widget == widget)
7105 allocation.x = child->x = x;
7106 allocation.y = child->y = y;
7107 allocation.width = child->width = width;
7108 allocation.height = child->height = height;
7110 if (gtk_widget_get_realized (widget))
7111 gtk_widget_size_allocate (widget, &allocation);
7115 /* TreeModel Callbacks
7119 pspp_sheet_view_row_changed (GtkTreeModel *model,
7124 PsppSheetView *tree_view = (PsppSheetView *)data;
7126 gboolean free_path = FALSE;
7127 GtkTreePath *cursor_path;
7129 g_return_if_fail (path != NULL || iter != NULL);
7131 if (tree_view->priv->cursor != NULL)
7132 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7136 if (tree_view->priv->edited_column &&
7137 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7138 pspp_sheet_view_stop_editing (tree_view, TRUE);
7140 if (cursor_path != NULL)
7141 gtk_tree_path_free (cursor_path);
7145 path = gtk_tree_model_get_path (model, iter);
7148 else if (iter == NULL)
7149 gtk_tree_model_get_iter (model, iter, path);
7151 _pspp_sheet_view_find_node (tree_view,
7157 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7158 pspp_sheet_view_node_queue_redraw (tree_view, node);
7162 gtk_tree_path_free (path);
7166 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7171 PsppSheetView *tree_view = (PsppSheetView *) data;
7174 gint height = tree_view->priv->fixed_height;
7175 gboolean free_path = FALSE;
7176 gboolean node_visible = TRUE;
7178 g_return_if_fail (path != NULL || iter != NULL);
7182 path = gtk_tree_model_get_path (model, iter);
7185 else if (iter == NULL)
7186 gtk_tree_model_get_iter (model, iter, path);
7188 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7190 /* Update all row-references */
7191 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7192 indices = gtk_tree_path_get_indices (path);
7193 tmpnode = indices[0];
7195 range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7199 if (node_visible && node_is_visible (tree_view, tmpnode))
7200 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7202 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7205 install_presize_handler (tree_view);
7207 gtk_tree_path_free (path);
7211 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7215 PsppSheetView *tree_view = (PsppSheetView *)data;
7218 g_return_if_fail (path != NULL);
7220 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7222 _pspp_sheet_view_find_node (tree_view, path, &node);
7227 range_tower_delete (tree_view->priv->selected, node, 1);
7229 /* Ensure we don't have a dangling pointer to a dead node */
7230 ensure_unprelighted (tree_view);
7232 /* Cancel editting if we've started */
7233 pspp_sheet_view_stop_editing (tree_view, TRUE);
7235 if (tree_view->priv->destroy_count_func)
7237 gint child_count = 0;
7238 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7241 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7243 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7245 gtk_tree_row_reference_free (tree_view->priv->top_row);
7246 tree_view->priv->top_row = NULL;
7249 install_scroll_sync_handler (tree_view);
7251 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7254 if (helper_data.changed)
7255 g_signal_emit_by_name (tree_view->priv->selection, "changed");
7260 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7261 GtkTreePath *parent,
7266 PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7269 /* XXX need to adjust selection */
7270 len = gtk_tree_model_iter_n_children (model, iter);
7275 gtk_tree_row_reference_reordered (G_OBJECT (data),
7280 if (gtk_tree_path_get_depth (parent) != 0)
7283 if (tree_view->priv->edited_column)
7284 pspp_sheet_view_stop_editing (tree_view, TRUE);
7286 /* we need to be unprelighted */
7287 ensure_unprelighted (tree_view);
7289 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7291 pspp_sheet_view_dy_to_top_row (tree_view);
7295 /* Internal tree functions
7300 pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view,
7301 PsppSheetViewColumn *column,
7305 PsppSheetViewColumn *tmp_column = NULL;
7316 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7319 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7321 list = (rtl ? list->prev : list->next))
7323 tmp_column = list->data;
7325 if (tmp_column == column)
7328 if (tmp_column->visible)
7329 total_width += tmp_column->width;
7332 if (tmp_column != column)
7334 g_warning (G_STRLOC": passed-in column isn't in the tree");
7343 if (column->visible)
7344 *x2 = total_width + column->width;
7346 *x2 = total_width; /* width of 0 */
7350 /* Make sure the node is visible vertically */
7352 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7355 gint node_dy, height;
7356 GtkTreePath *path = NULL;
7358 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7361 /* just return if the node is visible, avoiding a costly expose */
7362 node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7363 height = ROW_HEIGHT (tree_view);
7364 if (node_dy >= tree_view->priv->vadjustment->value
7365 && node_dy + height <= (tree_view->priv->vadjustment->value
7366 + tree_view->priv->vadjustment->page_size))
7369 path = _pspp_sheet_view_find_path (tree_view, node);
7372 /* We process updates because we want to clear old selected items when we scroll.
7373 * if this is removed, we get a "selection streak" at the bottom. */
7374 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7375 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7376 gtk_tree_path_free (path);
7381 pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
7382 PsppSheetViewColumn *column,
7383 gboolean focus_to_cell)
7390 x = column->allocation.x;
7391 width = column->allocation.width;
7393 if (width > tree_view->priv->hadjustment->page_size)
7395 /* The column is larger than the horizontal page size. If the
7396 * column has cells which can be focussed individually, then we make
7397 * sure the cell which gets focus is fully visible (if even the
7398 * focus cell is bigger than the page size, we make sure the
7399 * left-hand side of the cell is visible).
7401 * If the column does not have those so-called special cells, we
7402 * make sure the left-hand side of the column is visible.
7405 if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7407 GtkTreePath *cursor_path;
7408 GdkRectangle background_area, cell_area, focus_area;
7410 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7412 pspp_sheet_view_get_cell_area (tree_view,
7413 cursor_path, column, &cell_area);
7414 pspp_sheet_view_get_background_area (tree_view,
7415 cursor_path, column,
7418 gtk_tree_path_free (cursor_path);
7420 _pspp_sheet_view_column_get_focus_area (column,
7426 width = focus_area.width;
7428 if (width < tree_view->priv->hadjustment->page_size)
7430 if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) < (x + width))
7431 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7432 x + width - tree_view->priv->hadjustment->page_size);
7433 else if (tree_view->priv->hadjustment->value > x)
7434 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7438 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7440 tree_view->priv->hadjustment->lower,
7441 tree_view->priv->hadjustment->upper
7442 - tree_view->priv->hadjustment->page_size));
7446 if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) < (x + width))
7447 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7448 x + width - tree_view->priv->hadjustment->page_size);
7449 else if (tree_view->priv->hadjustment->value > x)
7450 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7455 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7460 path = gtk_tree_path_new ();
7462 gtk_tree_path_append_index (path, node);
7467 _pspp_sheet_view_find_node (PsppSheetView *tree_view,
7471 gint *indices = gtk_tree_path_get_indices (path);
7472 gint depth = gtk_tree_path_get_depth (path);
7475 if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7481 pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
7484 gboolean add_shifted_binding,
7485 GtkMovementStep step,
7489 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7494 if (add_shifted_binding)
7495 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7500 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7503 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7508 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7515 pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view,
7516 PsppSheetViewColumn *column)
7518 PsppSheetViewColumn *left_column;
7519 PsppSheetViewColumn *cur_column = NULL;
7520 PsppSheetViewColumnReorder *reorder;
7525 /* We want to precalculate the motion list such that we know what column slots
7529 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7531 /* First, identify all possible drop spots */
7533 tmp_list = g_list_last (tree_view->priv->columns);
7535 tmp_list = g_list_first (tree_view->priv->columns);
7539 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7540 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7542 if (cur_column->visible == FALSE)
7545 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7546 if (left_column != column && cur_column != column &&
7547 tree_view->priv->column_drop_func &&
7548 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7550 left_column = cur_column;
7553 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7554 reorder->left_column = left_column;
7555 left_column = reorder->right_column = cur_column;
7557 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7560 /* Add the last one */
7561 if (tree_view->priv->column_drop_func == NULL ||
7562 ((left_column != column) &&
7563 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7565 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7566 reorder->left_column = left_column;
7567 reorder->right_column = NULL;
7568 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7571 /* We quickly check to see if it even makes sense to reorder columns. */
7572 /* If there is nothing that can be moved, then we return */
7574 if (tree_view->priv->column_drag_info == NULL)
7577 /* We know there are always 2 slots possbile, as you can always return column. */
7578 /* If that's all there is, return */
7579 if (tree_view->priv->column_drag_info->next == NULL ||
7580 (tree_view->priv->column_drag_info->next->next == NULL &&
7581 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7582 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7584 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7585 g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7586 g_list_free (tree_view->priv->column_drag_info);
7587 tree_view->priv->column_drag_info = NULL;
7590 /* We fill in the ranges for the columns, now that we've isolated them */
7591 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7593 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7595 reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7597 reorder->left_align = left;
7598 if (tmp_list->next != NULL)
7600 g_assert (tmp_list->next->data);
7601 left = reorder->right_align = (reorder->right_column->allocation.x +
7602 reorder->right_column->allocation.width +
7603 ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7609 gdk_drawable_get_size (tree_view->priv->header_window, &width, NULL);
7610 reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7616 _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view,
7617 PsppSheetViewColumn *column)
7619 GdkEvent *send_event;
7620 GtkAllocation allocation;
7621 gint x, y, width, height;
7622 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7623 GdkDisplay *display = gdk_screen_get_display (screen);
7625 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7626 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7627 g_return_if_fail (column->button);
7629 pspp_sheet_view_set_column_drag_info (tree_view, column);
7631 if (tree_view->priv->column_drag_info == NULL)
7634 if (tree_view->priv->drag_window == NULL)
7636 GdkWindowAttr attributes;
7637 guint attributes_mask;
7639 attributes.window_type = GDK_WINDOW_CHILD;
7640 attributes.wclass = GDK_INPUT_OUTPUT;
7641 attributes.x = column->allocation.x;
7643 attributes.width = column->allocation.width;
7644 attributes.height = column->allocation.height;
7645 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7646 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
7647 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7648 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
7650 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7653 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7656 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7657 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7659 gtk_grab_remove (column->button);
7661 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7662 send_event->crossing.send_event = TRUE;
7663 send_event->crossing.window = g_object_ref (GTK_BUTTON (column->button)->event_window);
7664 send_event->crossing.subwindow = NULL;
7665 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7666 send_event->crossing.time = GDK_CURRENT_TIME;
7668 gtk_propagate_event (column->button, send_event);
7669 gdk_event_free (send_event);
7671 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7672 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7673 send_event->button.send_event = TRUE;
7674 send_event->button.time = GDK_CURRENT_TIME;
7675 send_event->button.x = -1;
7676 send_event->button.y = -1;
7677 send_event->button.axes = NULL;
7678 send_event->button.state = 0;
7679 send_event->button.button = 1;
7680 send_event->button.device = gdk_display_get_core_pointer (display);
7681 send_event->button.x_root = 0;
7682 send_event->button.y_root = 0;
7684 gtk_propagate_event (column->button, send_event);
7685 gdk_event_free (send_event);
7687 /* Kids, don't try this at home */
7688 g_object_ref (column->button);
7689 gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7690 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7691 gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7692 g_object_unref (column->button);
7694 tree_view->priv->drag_column_x = column->allocation.x;
7695 allocation = column->allocation;
7697 gtk_widget_size_allocate (column->button, &allocation);
7698 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7700 tree_view->priv->drag_column = column;
7701 gdk_window_show (tree_view->priv->drag_window);
7703 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7704 gdk_drawable_get_size (tree_view->priv->header_window, &width, &height);
7706 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7707 while (gtk_events_pending ())
7708 gtk_main_iteration ();
7710 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7711 gdk_pointer_grab (tree_view->priv->drag_window,
7713 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7714 NULL, NULL, GDK_CURRENT_TIME);
7715 gdk_keyboard_grab (tree_view->priv->drag_window,
7721 _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view,
7723 const GdkRectangle *clip_rect)
7727 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7731 rect.width = MAX (tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.width);
7733 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7734 rect.height = ROW_HEIGHT (tree_view);
7738 GdkRectangle new_rect;
7740 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7742 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7746 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7751 pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
7753 const GdkRectangle *clip_rect)
7757 _pspp_sheet_view_find_node (tree_view, path, &node);
7760 _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7764 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7767 GtkTreePath *cursor_path;
7769 if ((tree_view->priv->row_count == 0) ||
7770 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7774 if (tree_view->priv->cursor)
7775 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7777 if (cursor_path == NULL)
7779 /* There's no cursor. Move the cursor to the first selected row, if any
7780 * are selected, otherwise to the first row in the sheetview.
7782 GList *selected_rows;
7783 GtkTreeModel *model;
7784 PsppSheetSelection *selection;
7786 selection = pspp_sheet_view_get_selection (tree_view);
7787 selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7791 /* XXX we could avoid doing O(n) work to get this result */
7792 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7793 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7794 g_list_free (selected_rows);
7798 cursor_path = gtk_tree_path_new_first ();
7799 search_first_focusable_path (tree_view, &cursor_path,
7803 gtk_tree_row_reference_free (tree_view->priv->cursor);
7804 tree_view->priv->cursor = NULL;
7808 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7809 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7810 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0);
7812 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0);
7818 /* Now find a column for the cursor. */
7819 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7821 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7822 gtk_tree_path_free (cursor_path);
7824 if (tree_view->priv->focus_column == NULL)
7827 for (list = tree_view->priv->columns; list; list = list->next)
7829 if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7831 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7832 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7833 pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7843 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7845 PsppSheetSelectMode mode)
7847 gint selection_count;
7848 int cursor_node = -1;
7849 int new_cursor_node = -1;
7850 GtkTreePath *cursor_path = NULL;
7851 gboolean grab_focus = TRUE;
7853 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7857 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7858 /* FIXME: we lost the cursor; should we get the first? */
7861 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7862 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7864 if (cursor_node < 0)
7865 /* FIXME: we lost the cursor; should we get the first? */
7868 selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7870 if (selection_count == 0
7871 && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7872 && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
7874 /* Don't move the cursor, but just select the current node */
7875 new_cursor_node = cursor_node;
7880 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7882 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7885 gtk_tree_path_free (cursor_path);
7887 if (new_cursor_node)
7889 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7891 search_first_focusable_path (tree_view, &cursor_path,
7896 gtk_tree_path_free (cursor_path);
7900 * If the list has only one item and multi-selection is set then select
7901 * the row (if not yet selected).
7903 if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7904 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7905 new_cursor_node < 0)
7908 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7910 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7912 if (new_cursor_node < 0
7913 && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
7915 new_cursor_node = cursor_node;
7919 new_cursor_node = -1;
7923 if (new_cursor_node >= 0)
7925 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7926 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode);
7927 gtk_tree_path_free (cursor_path);
7931 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7933 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
7935 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
7937 GTK_DIR_UP : GTK_DIR_DOWN))
7939 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
7942 gtk_widget_child_focus (toplevel,
7944 GTK_DIR_TAB_BACKWARD :
7945 GTK_DIR_TAB_FORWARD);
7952 gtk_widget_error_bell (GTK_WIDGET (tree_view));
7957 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7959 return new_cursor_node >= 0;
7963 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
7965 PsppSheetSelectMode mode)
7967 int cursor_node = -1;
7968 GtkTreePath *old_cursor_path = NULL;
7969 GtkTreePath *cursor_path = NULL;
7970 int start_cursor_node = -1;
7973 gint vertical_separator;
7975 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7978 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
7979 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7981 /* This is sorta weird. Focus in should give us a cursor */
7984 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
7985 _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
7987 if (cursor_node < 0)
7989 /* FIXME: we lost the cursor. Should we try to get one? */
7990 gtk_tree_path_free (old_cursor_path);
7994 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7995 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
7996 y += tree_view->priv->cursor_offset;
7997 y += count * (int)tree_view->priv->vadjustment->page_increment;
7998 y = CLAMP (y, (gint)tree_view->priv->vadjustment->lower, (gint)tree_view->priv->vadjustment->upper - vertical_separator);
8000 if (y >= tree_view->priv->height)
8001 y = tree_view->priv->height - 1;
8003 tree_view->priv->cursor_offset =
8004 pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
8006 if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
8008 cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
8009 tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
8012 y -= tree_view->priv->cursor_offset;
8013 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8015 start_cursor_node = cursor_node;
8017 if (! search_first_focusable_path (tree_view, &cursor_path,
8021 /* It looks like we reached the end of the view without finding
8022 * a focusable row. We will step backwards to find the last
8025 cursor_node = start_cursor_node;
8026 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8028 search_first_focusable_path (tree_view, &cursor_path,
8037 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8039 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode);
8042 pspp_sheet_view_scroll_to_point (tree_view, -1, y);
8043 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8044 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8046 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
8047 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8049 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8052 gtk_tree_path_free (old_cursor_path);
8053 gtk_tree_path_free (cursor_path);
8057 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8059 PsppSheetSelectMode mode)
8061 int cursor_node = -1;
8062 GtkTreePath *cursor_path = NULL;
8063 PsppSheetViewColumn *column;
8066 gboolean found_column = FALSE;
8069 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8071 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8074 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8075 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8079 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8080 if (cursor_node < 0)
8082 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8084 gtk_tree_path_free (cursor_path);
8087 gtk_tree_path_free (cursor_path);
8089 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8090 if (tree_view->priv->focus_column)
8092 for (; list; list = (rtl ? list->prev : list->next))
8094 if (list->data == tree_view->priv->focus_column)
8101 gboolean left, right;
8103 column = list->data;
8104 if (column->visible == FALSE || column->row_head)
8107 pspp_sheet_view_column_cell_set_cell_data (column,
8108 tree_view->priv->model,
8113 right = list->prev ? TRUE : FALSE;
8114 left = list->next ? TRUE : FALSE;
8118 left = list->prev ? TRUE : FALSE;
8119 right = list->next ? TRUE : FALSE;
8122 if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8124 tree_view->priv->focus_column = column;
8125 found_column = TRUE;
8130 list = rtl ? list->prev : list->next;
8132 list = rtl ? list->next : list->prev;
8137 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8138 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8139 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8143 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8146 pspp_sheet_view_clamp_column_visible (tree_view,
8147 tree_view->priv->focus_column, TRUE);
8151 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8153 PsppSheetSelectMode mode)
8155 int cursor_node = -1;
8156 GtkTreePath *cursor_path = NULL;
8157 PsppSheetViewColumn *column;
8158 PsppSheetViewColumn *found_column;
8163 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8165 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8168 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8169 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8173 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8174 if (cursor_node < 0)
8176 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8178 gtk_tree_path_free (cursor_path);
8181 gtk_tree_path_free (cursor_path);
8183 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8184 if (tree_view->priv->focus_column)
8186 for (; list; list = (rtl ? list->prev : list->next))
8188 if (list->data == tree_view->priv->focus_column)
8193 found_column = NULL;
8196 gboolean left, right;
8198 column = list->data;
8199 if (column->visible == FALSE || column->row_head)
8202 pspp_sheet_view_column_cell_set_cell_data (column,
8203 tree_view->priv->model,
8208 right = list->prev ? TRUE : FALSE;
8209 left = list->next ? TRUE : FALSE;
8213 left = list->prev ? TRUE : FALSE;
8214 right = list->next ? TRUE : FALSE;
8217 if (column->tabbable
8218 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8219 found_column = column;
8223 list = rtl ? list->prev : list->next;
8225 list = rtl ? list->next : list->prev;
8230 tree_view->priv->focus_column = found_column;
8231 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8232 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8233 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8236 pspp_sheet_view_clamp_column_visible (tree_view,
8237 tree_view->priv->focus_column, TRUE);
8241 try_move_cursor_tab (PsppSheetView *tree_view,
8242 gboolean start_at_focus_column,
8245 PsppSheetViewColumn *column;
8247 int cursor_node = -1;
8248 GtkTreePath *cursor_path = NULL;
8252 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8253 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8257 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8258 if (cursor_node < 0)
8260 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8262 gtk_tree_path_free (cursor_path);
8265 gtk_tree_path_free (cursor_path);
8267 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8268 if (start_at_focus_column)
8271 ? g_list_last (tree_view->priv->columns)
8272 : g_list_first (tree_view->priv->columns));
8273 if (tree_view->priv->focus_column)
8275 for (; list; list = (rtl ? list->prev : list->next))
8277 if (list->data == tree_view->priv->focus_column)
8284 list = (rtl ^ (count == 1)
8285 ? g_list_first (tree_view->priv->columns)
8286 : g_list_last (tree_view->priv->columns));
8291 gboolean left, right;
8293 column = list->data;
8294 if (column->visible == FALSE || !column->tabbable)
8297 pspp_sheet_view_column_cell_set_cell_data (column,
8298 tree_view->priv->model,
8303 right = list->prev ? TRUE : FALSE;
8304 left = list->next ? TRUE : FALSE;
8308 left = list->prev ? TRUE : FALSE;
8309 right = list->next ? TRUE : FALSE;
8312 if (column->tabbable
8313 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8315 tree_view->priv->focus_column = column;
8316 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8317 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8318 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8323 list = rtl ? list->prev : list->next;
8325 list = rtl ? list->next : list->prev;
8332 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8335 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8338 if (!try_move_cursor_tab (tree_view, TRUE, count))
8340 if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0)
8341 && !try_move_cursor_tab (tree_view, FALSE, count))
8342 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8345 pspp_sheet_view_clamp_column_visible (tree_view,
8346 tree_view->priv->focus_column, TRUE);
8350 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8352 PsppSheetSelectMode mode)
8356 GtkTreePath *old_path;
8358 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8361 g_return_if_fail (tree_view->priv->row_count > 0);
8363 pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8367 /* Now go forward to find the first focusable row. */
8368 path = _pspp_sheet_view_find_path (tree_view, 0);
8369 search_first_focusable_path (tree_view, &path,
8370 TRUE, &cursor_node);
8374 /* Now go backwards to find last focusable row. */
8375 path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8376 search_first_focusable_path (tree_view, &path,
8377 FALSE, &cursor_node);
8383 if (gtk_tree_path_compare (old_path, path))
8385 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
8386 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8390 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8394 gtk_tree_path_free (old_path);
8395 gtk_tree_path_free (path);
8399 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8401 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8404 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8405 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8408 pspp_sheet_selection_select_all (tree_view->priv->selection);
8414 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8416 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8419 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8420 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8423 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8429 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8430 gboolean start_editing,
8431 PsppSheetSelectMode mode)
8434 int cursor_node = -1;
8435 GtkTreePath *cursor_path = NULL;
8437 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8440 if (tree_view->priv->cursor)
8441 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8443 if (cursor_path == NULL)
8446 _pspp_sheet_view_find_node (tree_view, cursor_path,
8449 if (cursor_node < 0)
8451 gtk_tree_path_free (cursor_path);
8455 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing &&
8456 tree_view->priv->focus_column)
8458 if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8460 gtk_tree_path_free (cursor_path);
8465 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8471 /* We bail out if the original (tree, node) don't exist anymore after
8472 * handling the selection-changed callback. We do return TRUE because
8473 * the key press has been handled at this point.
8475 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8477 if (cursor_node != new_node)
8480 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8482 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8483 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8485 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8486 pspp_sheet_view_row_activated (tree_view, cursor_path,
8487 tree_view->priv->focus_column);
8489 gtk_tree_path_free (cursor_path);
8495 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8498 int cursor_node = -1;
8499 GtkTreePath *cursor_path = NULL;
8501 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8505 if (tree_view->priv->cursor)
8506 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8508 if (cursor_path == NULL)
8511 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8512 if (cursor_node < 0)
8514 gtk_tree_path_free (cursor_path);
8518 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8521 PSPP_SHEET_SELECT_MODE_TOGGLE,
8524 /* We bail out if the original (tree, node) don't exist anymore after
8525 * handling the selection-changed callback. We do return TRUE because
8526 * the key press has been handled at this point.
8528 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8530 if (cursor_node != new_node)
8533 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8535 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8536 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8537 gtk_tree_path_free (cursor_path);
8543 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8545 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8546 tree_view->priv->typeselect_flush_timeout = 0;
8551 /* Cut and paste from gtkwindow.c */
8553 send_focus_change (GtkWidget *widget,
8556 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8558 g_object_ref (widget);
8561 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
8563 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
8565 fevent->focus_change.type = GDK_FOCUS_CHANGE;
8566 fevent->focus_change.window = g_object_ref (widget->window);
8567 fevent->focus_change.in = in;
8569 gtk_widget_event (widget, fevent);
8571 g_object_notify (G_OBJECT (widget), "has-focus");
8573 g_object_unref (widget);
8574 gdk_event_free (fevent);
8578 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8580 GtkWidget *frame, *vbox, *toplevel;
8583 if (tree_view->priv->search_custom_entry_set)
8586 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8587 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8589 if (tree_view->priv->search_window != NULL)
8591 if (GTK_WINDOW (toplevel)->group)
8592 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
8593 GTK_WINDOW (tree_view->priv->search_window));
8594 else if (GTK_WINDOW (tree_view->priv->search_window)->group)
8595 gtk_window_group_remove_window (GTK_WINDOW (tree_view->priv->search_window)->group,
8596 GTK_WINDOW (tree_view->priv->search_window));
8597 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8601 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8602 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8604 if (GTK_WINDOW (toplevel)->group)
8605 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
8606 GTK_WINDOW (tree_view->priv->search_window));
8608 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8609 GDK_WINDOW_TYPE_HINT_UTILITY);
8610 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8611 g_signal_connect (tree_view->priv->search_window, "delete-event",
8612 G_CALLBACK (pspp_sheet_view_search_delete_event),
8614 g_signal_connect (tree_view->priv->search_window, "key-press-event",
8615 G_CALLBACK (pspp_sheet_view_search_key_press_event),
8617 g_signal_connect (tree_view->priv->search_window, "button-press-event",
8618 G_CALLBACK (pspp_sheet_view_search_button_press_event),
8620 g_signal_connect (tree_view->priv->search_window, "scroll-event",
8621 G_CALLBACK (pspp_sheet_view_search_scroll_event),
8624 frame = gtk_frame_new (NULL);
8625 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8626 gtk_widget_show (frame);
8627 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8629 vbox = gtk_vbox_new (FALSE, 0);
8630 gtk_widget_show (vbox);
8631 gtk_container_add (GTK_CONTAINER (frame), vbox);
8632 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8635 tree_view->priv->search_entry = gtk_entry_new ();
8636 gtk_widget_show (tree_view->priv->search_entry);
8637 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8638 G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8640 g_signal_connect (tree_view->priv->search_entry,
8641 "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8643 g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8645 G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8647 gtk_container_add (GTK_CONTAINER (vbox),
8648 tree_view->priv->search_entry);
8650 gtk_widget_realize (tree_view->priv->search_entry);
8653 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8654 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8657 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8658 gboolean keybinding)
8660 /* We only start interactive search if we have focus or the columns
8661 * have focus. If one of our children have focus, we don't want to
8665 gboolean found_focus = FALSE;
8666 GtkWidgetClass *entry_parent_class;
8668 if (!tree_view->priv->enable_search && !keybinding)
8671 if (tree_view->priv->search_custom_entry_set)
8674 if (tree_view->priv->search_window != NULL &&
8675 gtk_widget_get_visible (tree_view->priv->search_window))
8678 for (list = tree_view->priv->columns; list; list = list->next)
8680 PsppSheetViewColumn *column;
8682 column = list->data;
8683 if (! column->visible)
8686 if (column->button && gtk_widget_has_focus (column->button))
8693 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8699 if (tree_view->priv->search_column < 0)
8702 pspp_sheet_view_ensure_interactive_directory (tree_view);
8705 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8708 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8709 gtk_widget_show (tree_view->priv->search_window);
8710 if (tree_view->priv->search_entry_changed_id == 0)
8712 tree_view->priv->search_entry_changed_id =
8713 g_signal_connect (tree_view->priv->search_entry, "changed",
8714 G_CALLBACK (pspp_sheet_view_search_init),
8718 tree_view->priv->typeselect_flush_timeout =
8719 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8720 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8723 /* Grab focus will select all the text. We don't want that to happen, so we
8724 * call the parent instance and bypass the selection change. This is probably
8725 * really non-kosher. */
8726 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8727 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8729 /* send focus-in event */
8730 send_focus_change (tree_view->priv->search_entry, TRUE);
8732 /* search first matching iter */
8733 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8739 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8741 return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8744 /* this function returns the new width of the column being resized given
8745 * the column and x position of the cursor; the x cursor position is passed
8746 * in as a pointer and automagicly corrected if it's beyond min/max limits
8749 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8753 PsppSheetViewColumn *column;
8757 /* first translate the x position from widget->window
8758 * to clist->clist_window
8760 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8761 column = g_list_nth (tree_view->priv->columns, i)->data;
8762 width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8764 /* Clamp down the value */
8765 if (column->min_width == -1)
8766 width = MAX (column->button_request, width);
8768 width = MAX (column->min_width, width);
8769 if (column->max_width != -1)
8770 width = MIN (width, column->max_width);
8772 *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8778 /* FIXME this adjust_allocation is a big cut-and-paste from
8779 * GtkCList, needs to be some "official" way to do this
8789 /* The window to which widget->window is relative */
8790 #define ALLOCATION_WINDOW(widget) \
8791 (!gtk_widget_get_has_window (widget) ? \
8792 (widget)->window : \
8793 gdk_window_get_parent ((widget)->window))
8796 adjust_allocation_recurse (GtkWidget *widget,
8799 ScrollData *scroll_data = data;
8801 /* Need to really size allocate instead of just poking
8802 * into widget->allocation if the widget is not realized.
8803 * FIXME someone figure out why this was.
8805 if (!gtk_widget_get_realized (widget))
8807 if (gtk_widget_get_visible (widget))
8809 GdkRectangle tmp_rectangle = widget->allocation;
8810 tmp_rectangle.x += scroll_data->dx;
8811 tmp_rectangle.y += scroll_data->dy;
8813 gtk_widget_size_allocate (widget, &tmp_rectangle);
8818 if (ALLOCATION_WINDOW (widget) == scroll_data->window)
8820 widget->allocation.x += scroll_data->dx;
8821 widget->allocation.y += scroll_data->dy;
8823 if (GTK_IS_CONTAINER (widget))
8824 gtk_container_forall (GTK_CONTAINER (widget),
8825 adjust_allocation_recurse,
8832 adjust_allocation (GtkWidget *widget,
8836 ScrollData scroll_data;
8838 if (gtk_widget_get_realized (widget))
8839 scroll_data.window = ALLOCATION_WINDOW (widget);
8841 scroll_data.window = NULL;
8843 scroll_data.dx = dx;
8844 scroll_data.dy = dy;
8846 adjust_allocation_recurse (widget, &scroll_data);
8850 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8854 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8855 PsppSheetView *tree_view)
8857 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8862 gdk_window_move (tree_view->priv->bin_window,
8863 - tree_view->priv->hadjustment->value,
8864 TREE_VIEW_HEADER_HEIGHT (tree_view));
8865 gdk_window_move (tree_view->priv->header_window,
8866 - tree_view->priv->hadjustment->value,
8868 dy = tree_view->priv->dy - (int) tree_view->priv->vadjustment->value;
8871 update_prelight (tree_view,
8872 tree_view->priv->event_last_x,
8873 tree_view->priv->event_last_y - dy);
8875 if (tree_view->priv->edited_column &&
8876 GTK_IS_WIDGET (tree_view->priv->edited_column->editable_widget))
8880 PsppSheetViewChild *child = NULL;
8882 widget = GTK_WIDGET (tree_view->priv->edited_column->editable_widget);
8883 adjust_allocation (widget, 0, dy);
8885 for (list = tree_view->priv->children; list; list = list->next)
8887 child = (PsppSheetViewChild *)list->data;
8888 if (child->widget == widget)
8896 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8898 if (tree_view->priv->dy != (int) tree_view->priv->vadjustment->value)
8900 /* update our dy and top_row */
8901 tree_view->priv->dy = (int) tree_view->priv->vadjustment->value;
8903 if (!tree_view->priv->in_top_row_to_dy)
8904 pspp_sheet_view_dy_to_top_row (tree_view);
8907 for (list = tree_view->priv->columns; list; list = list->next)
8909 PsppSheetViewColumn *column = list->data;
8910 GtkAllocation *allocation = &column->allocation;
8912 if (span_intersects (allocation->x, allocation->width,
8913 tree_view->priv->hadjustment->value,
8914 GTK_WIDGET (tree_view)->allocation.width))
8916 pspp_sheet_view_column_set_need_button (column, TRUE);
8917 if (!column->button)
8918 pspp_sheet_view_column_update_button (column);
8930 * pspp_sheet_view_new:
8932 * Creates a new #PsppSheetView widget.
8934 * Return value: A newly created #PsppSheetView widget.
8937 pspp_sheet_view_new (void)
8939 return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
8943 * pspp_sheet_view_new_with_model:
8944 * @model: the model.
8946 * Creates a new #PsppSheetView widget with the model initialized to @model.
8948 * Return value: A newly created #PsppSheetView widget.
8951 pspp_sheet_view_new_with_model (GtkTreeModel *model)
8953 return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
8960 * pspp_sheet_view_get_model:
8961 * @tree_view: a #PsppSheetView
8963 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
8966 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
8969 pspp_sheet_view_get_model (PsppSheetView *tree_view)
8971 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8973 return tree_view->priv->model;
8977 * pspp_sheet_view_set_model:
8978 * @tree_view: A #GtkTreeNode.
8979 * @model: (allow-none): The model.
8981 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
8982 * set, it will remove it before setting the new model. If @model is %NULL,
8983 * then it will unset the old model.
8986 pspp_sheet_view_set_model (PsppSheetView *tree_view,
8987 GtkTreeModel *model)
8989 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8990 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
8992 if (model == tree_view->priv->model)
8995 if (tree_view->priv->scroll_to_path)
8997 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8998 tree_view->priv->scroll_to_path = NULL;
9001 if (tree_view->priv->model)
9003 GList *tmplist = tree_view->priv->columns;
9005 if (tree_view->priv->selected)
9006 range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
9007 pspp_sheet_view_stop_editing (tree_view, TRUE);
9009 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9010 pspp_sheet_view_row_changed,
9012 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9013 pspp_sheet_view_row_inserted,
9015 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9016 pspp_sheet_view_row_deleted,
9018 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9019 pspp_sheet_view_rows_reordered,
9022 for (; tmplist; tmplist = tmplist->next)
9023 _pspp_sheet_view_column_unset_model (tmplist->data,
9024 tree_view->priv->model);
9026 tree_view->priv->prelight_node = -1;
9028 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
9029 tree_view->priv->drag_dest_row = NULL;
9030 gtk_tree_row_reference_free (tree_view->priv->cursor);
9031 tree_view->priv->cursor = NULL;
9032 gtk_tree_row_reference_free (tree_view->priv->anchor);
9033 tree_view->priv->anchor = NULL;
9034 gtk_tree_row_reference_free (tree_view->priv->top_row);
9035 tree_view->priv->top_row = NULL;
9036 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9037 tree_view->priv->scroll_to_path = NULL;
9039 tree_view->priv->scroll_to_column = NULL;
9041 g_object_unref (tree_view->priv->model);
9043 tree_view->priv->search_column = -1;
9044 tree_view->priv->fixed_height = -1;
9045 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
9046 tree_view->priv->last_button_x = -1;
9047 tree_view->priv->last_button_y = -1;
9050 tree_view->priv->model = model;
9052 if (tree_view->priv->model)
9056 if (tree_view->priv->search_column == -1)
9058 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
9060 GType type = gtk_tree_model_get_column_type (model, i);
9062 if (g_value_type_transformable (type, G_TYPE_STRING))
9064 tree_view->priv->search_column = i;
9070 g_object_ref (tree_view->priv->model);
9071 g_signal_connect (tree_view->priv->model,
9073 G_CALLBACK (pspp_sheet_view_row_changed),
9075 g_signal_connect (tree_view->priv->model,
9077 G_CALLBACK (pspp_sheet_view_row_inserted),
9079 g_signal_connect (tree_view->priv->model,
9081 G_CALLBACK (pspp_sheet_view_row_deleted),
9083 g_signal_connect (tree_view->priv->model,
9085 G_CALLBACK (pspp_sheet_view_rows_reordered),
9088 tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
9090 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
9091 install_presize_handler (tree_view);
9094 g_object_notify (G_OBJECT (tree_view), "model");
9096 if (tree_view->priv->selection)
9097 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
9099 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9100 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9104 * pspp_sheet_view_get_selection:
9105 * @tree_view: A #PsppSheetView.
9107 * Gets the #PsppSheetSelection associated with @tree_view.
9109 * Return value: A #PsppSheetSelection object.
9111 PsppSheetSelection *
9112 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
9114 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9116 return tree_view->priv->selection;
9120 * pspp_sheet_view_get_hadjustment:
9121 * @tree_view: A #PsppSheetView
9123 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
9125 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9129 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
9131 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9133 if (tree_view->priv->hadjustment == NULL)
9134 pspp_sheet_view_set_hadjustment (tree_view, NULL);
9136 return tree_view->priv->hadjustment;
9140 * pspp_sheet_view_set_hadjustment:
9141 * @tree_view: A #PsppSheetView
9142 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9144 * Sets the #GtkAdjustment for the current horizontal aspect.
9147 pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view,
9148 GtkAdjustment *adjustment)
9150 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9152 pspp_sheet_view_set_adjustments (tree_view,
9154 tree_view->priv->vadjustment);
9156 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9160 * pspp_sheet_view_get_vadjustment:
9161 * @tree_view: A #PsppSheetView
9163 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9165 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9169 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9171 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9173 if (tree_view->priv->vadjustment == NULL)
9174 pspp_sheet_view_set_vadjustment (tree_view, NULL);
9176 return tree_view->priv->vadjustment;
9180 * pspp_sheet_view_set_vadjustment:
9181 * @tree_view: A #PsppSheetView
9182 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9184 * Sets the #GtkAdjustment for the current vertical aspect.
9187 pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view,
9188 GtkAdjustment *adjustment)
9190 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9192 pspp_sheet_view_set_adjustments (tree_view,
9193 tree_view->priv->hadjustment,
9196 g_object_notify (G_OBJECT (tree_view), "vadjustment");
9199 /* Column and header operations */
9202 * pspp_sheet_view_get_headers_visible:
9203 * @tree_view: A #PsppSheetView.
9205 * Returns %TRUE if the headers on the @tree_view are visible.
9207 * Return value: Whether the headers are visible or not.
9210 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9212 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9214 return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9218 * pspp_sheet_view_set_headers_visible:
9219 * @tree_view: A #PsppSheetView.
9220 * @headers_visible: %TRUE if the headers are visible
9222 * Sets the visibility state of the headers.
9225 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9226 gboolean headers_visible)
9230 PsppSheetViewColumn *column;
9232 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9234 headers_visible = !! headers_visible;
9236 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9239 if (headers_visible)
9240 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9242 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9244 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9246 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9247 if (headers_visible)
9249 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));
9251 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9252 pspp_sheet_view_map_buttons (tree_view);
9256 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9258 for (list = tree_view->priv->columns; list; list = list->next)
9260 column = list->data;
9262 gtk_widget_unmap (column->button);
9264 gdk_window_hide (tree_view->priv->header_window);
9268 tree_view->priv->vadjustment->page_size = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
9269 tree_view->priv->vadjustment->page_increment = (GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2;
9270 tree_view->priv->vadjustment->lower = 0;
9271 tree_view->priv->vadjustment->upper = tree_view->priv->height;
9272 gtk_adjustment_changed (tree_view->priv->vadjustment);
9274 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9276 g_object_notify (G_OBJECT (tree_view), "headers-visible");
9280 * pspp_sheet_view_columns_autosize:
9281 * @tree_view: A #PsppSheetView.
9283 * Resizes all columns to their optimal width. Only works after the
9284 * treeview has been realized.
9287 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9289 gboolean dirty = FALSE;
9291 PsppSheetViewColumn *column;
9293 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9295 for (list = tree_view->priv->columns; list; list = list->next)
9297 column = list->data;
9298 _pspp_sheet_view_column_cell_set_dirty (column);
9303 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9307 * pspp_sheet_view_set_headers_clickable:
9308 * @tree_view: A #PsppSheetView.
9309 * @setting: %TRUE if the columns are clickable.
9311 * Allow the column title buttons to be clicked.
9314 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9319 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9321 for (list = tree_view->priv->columns; list; list = list->next)
9322 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9324 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9329 * pspp_sheet_view_get_headers_clickable:
9330 * @tree_view: A #PsppSheetView.
9332 * Returns whether all header columns are clickable.
9334 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9339 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9343 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9345 for (list = tree_view->priv->columns; list; list = list->next)
9346 if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9353 * pspp_sheet_view_set_rules_hint
9354 * @tree_view: a #PsppSheetView
9355 * @setting: %TRUE if the tree requires reading across rows
9357 * This function tells GTK+ that the user interface for your
9358 * application requires users to read across tree rows and associate
9359 * cells with one another. By default, GTK+ will then render the tree
9360 * with alternating row colors. Do <emphasis>not</emphasis> use it
9361 * just because you prefer the appearance of the ruled tree; that's a
9362 * question for the theme. Some themes will draw tree rows in
9363 * alternating colors even when rules are turned off, and users who
9364 * prefer that appearance all the time can choose those themes. You
9365 * should call this function only as a <emphasis>semantic</emphasis>
9366 * hint to the theme engine that your tree makes alternating colors
9367 * useful from a functional standpoint (since it has lots of columns,
9372 pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view,
9375 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9377 setting = setting != FALSE;
9379 if (tree_view->priv->has_rules != setting)
9381 tree_view->priv->has_rules = setting;
9382 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9385 g_object_notify (G_OBJECT (tree_view), "rules-hint");
9389 * pspp_sheet_view_get_rules_hint
9390 * @tree_view: a #PsppSheetView
9392 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9394 * Return value: %TRUE if rules are useful for the user of this tree
9397 pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view)
9399 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9401 return tree_view->priv->has_rules;
9404 /* Public Column functions
9408 * pspp_sheet_view_append_column:
9409 * @tree_view: A #PsppSheetView.
9410 * @column: The #PsppSheetViewColumn to add.
9412 * Appends @column to the list of columns.
9414 * Return value: The number of columns in @tree_view after appending.
9417 pspp_sheet_view_append_column (PsppSheetView *tree_view,
9418 PsppSheetViewColumn *column)
9420 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9421 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9422 g_return_val_if_fail (column->tree_view == NULL, -1);
9424 return pspp_sheet_view_insert_column (tree_view, column, -1);
9429 * pspp_sheet_view_remove_column:
9430 * @tree_view: A #PsppSheetView.
9431 * @column: The #PsppSheetViewColumn to remove.
9433 * Removes @column from @tree_view.
9435 * Return value: The number of columns in @tree_view after removing.
9438 pspp_sheet_view_remove_column (PsppSheetView *tree_view,
9439 PsppSheetViewColumn *column)
9441 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9442 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9443 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9445 if (tree_view->priv->focus_column == column)
9446 tree_view->priv->focus_column = NULL;
9448 if (tree_view->priv->edited_column == column)
9450 pspp_sheet_view_stop_editing (tree_view, TRUE);
9452 /* no need to, but just to be sure ... */
9453 tree_view->priv->edited_column = NULL;
9456 _pspp_sheet_view_column_unset_tree_view (column);
9458 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9459 tree_view->priv->n_columns--;
9461 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9465 _pspp_sheet_view_column_unrealize_button (column);
9466 for (list = tree_view->priv->columns; list; list = list->next)
9468 PsppSheetViewColumn *tmp_column;
9470 tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9471 if (tmp_column->visible)
9472 _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9475 if (tree_view->priv->n_columns == 0 &&
9476 pspp_sheet_view_get_headers_visible (tree_view) &&
9477 tree_view->priv->header_window)
9478 gdk_window_hide (tree_view->priv->header_window);
9480 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9483 g_object_unref (column);
9484 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9486 return tree_view->priv->n_columns;
9490 * pspp_sheet_view_insert_column:
9491 * @tree_view: A #PsppSheetView.
9492 * @column: The #PsppSheetViewColumn to be inserted.
9493 * @position: The position to insert @column in.
9495 * This inserts the @column into the @tree_view at @position. If @position is
9496 * -1, then the column is inserted at the end.
9498 * Return value: The number of columns in @tree_view after insertion.
9501 pspp_sheet_view_insert_column (PsppSheetView *tree_view,
9502 PsppSheetViewColumn *column,
9505 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9506 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9507 g_return_val_if_fail (column->tree_view == NULL, -1);
9509 g_object_ref_sink (column);
9511 if (tree_view->priv->n_columns == 0 &&
9512 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9513 pspp_sheet_view_get_headers_visible (tree_view))
9515 gdk_window_show (tree_view->priv->header_window);
9518 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9520 tree_view->priv->n_columns++;
9522 _pspp_sheet_view_column_set_tree_view (column, tree_view);
9524 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9528 _pspp_sheet_view_column_realize_button (column);
9530 for (list = tree_view->priv->columns; list; list = list->next)
9532 column = PSPP_SHEET_VIEW_COLUMN (list->data);
9533 if (column->visible)
9534 _pspp_sheet_view_column_cell_set_dirty (column);
9536 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9539 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9541 return tree_view->priv->n_columns;
9545 * pspp_sheet_view_insert_column_with_attributes:
9546 * @tree_view: A #PsppSheetView
9547 * @position: The position to insert the new column in.
9548 * @title: The title to set the header to.
9549 * @cell: The #GtkCellRenderer.
9550 * @Varargs: A %NULL-terminated list of attributes.
9552 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9553 * @position. If @position is -1, then the newly created column is inserted at
9554 * the end. The column is initialized with the attributes given.
9556 * Return value: The number of columns in @tree_view after insertion.
9559 pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view,
9562 GtkCellRenderer *cell,
9565 PsppSheetViewColumn *column;
9570 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9572 column = pspp_sheet_view_column_new ();
9573 pspp_sheet_view_column_set_title (column, title);
9574 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9576 va_start (args, cell);
9578 attribute = va_arg (args, gchar *);
9580 while (attribute != NULL)
9582 column_id = va_arg (args, gint);
9583 pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9584 attribute = va_arg (args, gchar *);
9589 pspp_sheet_view_insert_column (tree_view, column, position);
9591 return tree_view->priv->n_columns;
9595 * pspp_sheet_view_insert_column_with_data_func:
9596 * @tree_view: a #PsppSheetView
9597 * @position: Position to insert, -1 for append
9598 * @title: column title
9599 * @cell: cell renderer for column
9600 * @func: function to set attributes of cell renderer
9601 * @data: data for @func
9602 * @dnotify: destroy notifier for @data
9604 * Convenience function that inserts a new column into the #PsppSheetView
9605 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9606 * attributes (normally using data from the model). See also
9607 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9609 * Return value: number of columns in the tree view post-insert
9612 pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view,
9615 GtkCellRenderer *cell,
9616 PsppSheetCellDataFunc func,
9618 GDestroyNotify dnotify)
9620 PsppSheetViewColumn *column;
9622 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9624 column = pspp_sheet_view_column_new ();
9625 pspp_sheet_view_column_set_title (column, title);
9626 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9627 pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9629 pspp_sheet_view_insert_column (tree_view, column, position);
9631 return tree_view->priv->n_columns;
9635 * pspp_sheet_view_get_column:
9636 * @tree_view: A #PsppSheetView.
9637 * @n: The position of the column, counting from 0.
9639 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9641 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9644 PsppSheetViewColumn *
9645 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9648 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9650 if (n < 0 || n >= tree_view->priv->n_columns)
9653 if (tree_view->priv->columns == NULL)
9656 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9660 * pspp_sheet_view_get_columns:
9661 * @tree_view: A #PsppSheetView
9663 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9664 * The returned list must be freed with g_list_free ().
9666 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9669 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9671 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9673 return g_list_copy (tree_view->priv->columns);
9677 * pspp_sheet_view_move_column_after:
9678 * @tree_view: A #PsppSheetView
9679 * @column: The #PsppSheetViewColumn to be moved.
9680 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9682 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9683 * @column is placed in the first position.
9686 pspp_sheet_view_move_column_after (PsppSheetView *tree_view,
9687 PsppSheetViewColumn *column,
9688 PsppSheetViewColumn *base_column)
9690 GList *column_list_el, *base_el = NULL;
9692 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9694 column_list_el = g_list_find (tree_view->priv->columns, column);
9695 g_return_if_fail (column_list_el != NULL);
9699 base_el = g_list_find (tree_view->priv->columns, base_column);
9700 g_return_if_fail (base_el != NULL);
9703 if (column_list_el->prev == base_el)
9706 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9707 if (base_el == NULL)
9709 column_list_el->prev = NULL;
9710 column_list_el->next = tree_view->priv->columns;
9711 if (column_list_el->next)
9712 column_list_el->next->prev = column_list_el;
9713 tree_view->priv->columns = column_list_el;
9717 column_list_el->prev = base_el;
9718 column_list_el->next = base_el->next;
9719 if (column_list_el->next)
9720 column_list_el->next->prev = column_list_el;
9721 base_el->next = column_list_el;
9724 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9726 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9727 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9730 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9734 * pspp_sheet_view_set_column_drag_function:
9735 * @tree_view: A #PsppSheetView.
9736 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9737 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9738 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9740 * Sets a user function for determining where a column may be dropped when
9741 * dragged. This function is called on every column pair in turn at the
9742 * beginning of a column drag to determine where a drop can take place. The
9743 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9744 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9745 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9746 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9747 * @tree_view reverts to the default behavior of allowing all columns to be
9748 * dropped everywhere.
9751 pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view,
9752 PsppSheetViewColumnDropFunc func,
9754 GDestroyNotify destroy)
9756 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9758 if (tree_view->priv->column_drop_func_data_destroy)
9759 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9761 tree_view->priv->column_drop_func = func;
9762 tree_view->priv->column_drop_func_data = user_data;
9763 tree_view->priv->column_drop_func_data_destroy = destroy;
9767 * pspp_sheet_view_scroll_to_point:
9768 * @tree_view: a #PsppSheetView
9769 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9770 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9772 * Scrolls the tree view such that the top-left corner of the visible
9773 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9774 * in tree coordinates. The @tree_view must be realized before
9775 * this function is called. If it isn't, you probably want to be
9776 * using pspp_sheet_view_scroll_to_cell().
9778 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9781 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9785 GtkAdjustment *hadj;
9786 GtkAdjustment *vadj;
9788 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9789 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9791 hadj = tree_view->priv->hadjustment;
9792 vadj = tree_view->priv->vadjustment;
9795 gtk_adjustment_set_value (hadj, CLAMP (tree_x, hadj->lower, hadj->upper - hadj->page_size));
9797 gtk_adjustment_set_value (vadj, CLAMP (tree_y, vadj->lower, vadj->upper - vadj->page_size));
9801 * pspp_sheet_view_scroll_to_cell:
9802 * @tree_view: A #PsppSheetView.
9803 * @path: (allow-none): The path of the row to move to, or %NULL.
9804 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9805 * @use_align: whether to use alignment arguments, or %FALSE.
9806 * @row_align: The vertical alignment of the row specified by @path.
9807 * @col_align: The horizontal alignment of the column specified by @column.
9809 * Moves the alignments of @tree_view to the position specified by @column and
9810 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9811 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9812 * or @path need to be non-%NULL. @row_align determines where the row is
9813 * placed, and @col_align determines where @column is placed. Both are expected
9814 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9815 * right/bottom alignment, 0.5 means center.
9817 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9818 * tree does the minimum amount of work to scroll the cell onto the screen.
9819 * This means that the cell will be scrolled to the edge closest to its current
9820 * position. If the cell is currently visible on the screen, nothing is done.
9822 * This function only works if the model is set, and @path is a valid row on the
9823 * model. If the model changes before the @tree_view is realized, the centered
9824 * path will be modified to reflect this change.
9827 pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view,
9829 PsppSheetViewColumn *column,
9834 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9835 g_return_if_fail (tree_view->priv->model != NULL);
9836 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9837 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9838 g_return_if_fail (path != NULL || column != NULL);
9841 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9842 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9844 row_align = CLAMP (row_align, 0.0, 1.0);
9845 col_align = CLAMP (col_align, 0.0, 1.0);
9848 /* Note: Despite the benefits that come from having one code path for the
9849 * scrolling code, we short-circuit validate_visible_area's immplementation as
9850 * it is much slower than just going to the point.
9852 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9853 !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9854 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9856 if (tree_view->priv->scroll_to_path)
9857 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9859 tree_view->priv->scroll_to_path = NULL;
9860 tree_view->priv->scroll_to_column = NULL;
9863 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
9865 tree_view->priv->scroll_to_column = column;
9866 tree_view->priv->scroll_to_use_align = use_align;
9867 tree_view->priv->scroll_to_row_align = row_align;
9868 tree_view->priv->scroll_to_col_align = col_align;
9870 install_presize_handler (tree_view);
9874 GdkRectangle cell_rect;
9875 GdkRectangle vis_rect;
9876 gint dest_x, dest_y;
9878 pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
9879 pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
9881 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
9883 dest_x = vis_rect.x;
9884 dest_y = vis_rect.y;
9890 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
9894 if (cell_rect.x < vis_rect.x)
9895 dest_x = cell_rect.x;
9896 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
9897 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
9905 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
9906 dest_y = MAX (dest_y, 0);
9910 if (cell_rect.y < vis_rect.y)
9911 dest_y = cell_rect.y;
9912 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
9913 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
9917 pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
9922 * pspp_sheet_view_row_activated:
9923 * @tree_view: A #PsppSheetView
9924 * @path: The #GtkTreePath to be activated.
9925 * @column: The #PsppSheetViewColumn to be activated.
9927 * Activates the cell determined by @path and @column.
9930 pspp_sheet_view_row_activated (PsppSheetView *tree_view,
9932 PsppSheetViewColumn *column)
9934 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9936 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
9941 * pspp_sheet_view_get_reorderable:
9942 * @tree_view: a #PsppSheetView
9944 * Retrieves whether the user can reorder the tree via drag-and-drop. See
9945 * pspp_sheet_view_set_reorderable().
9947 * Return value: %TRUE if the tree can be reordered.
9950 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
9952 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9954 return tree_view->priv->reorderable;
9958 * pspp_sheet_view_set_reorderable:
9959 * @tree_view: A #PsppSheetView.
9960 * @reorderable: %TRUE, if the tree can be reordered.
9962 * This function is a convenience function to allow you to reorder
9963 * models that support the #GtkDragSourceIface and the
9964 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
9965 * these. If @reorderable is %TRUE, then the user can reorder the
9966 * model by dragging and dropping rows. The developer can listen to
9967 * these changes by connecting to the model's row_inserted and
9968 * row_deleted signals. The reordering is implemented by setting up
9969 * the tree view as a drag source and destination. Therefore, drag and
9970 * drop can not be used in a reorderable view for any other purpose.
9972 * This function does not give you any degree of control over the order -- any
9973 * reordering is allowed. If more control is needed, you should probably
9974 * handle drag and drop manually.
9977 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
9978 gboolean reorderable)
9980 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9982 reorderable = reorderable != FALSE;
9984 if (tree_view->priv->reorderable == reorderable)
9989 const GtkTargetEntry row_targets[] = {
9990 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
9993 pspp_sheet_view_enable_model_drag_source (tree_view,
9996 G_N_ELEMENTS (row_targets),
9998 pspp_sheet_view_enable_model_drag_dest (tree_view,
10000 G_N_ELEMENTS (row_targets),
10005 pspp_sheet_view_unset_rows_drag_source (tree_view);
10006 pspp_sheet_view_unset_rows_drag_dest (tree_view);
10009 tree_view->priv->reorderable = reorderable;
10011 g_object_notify (G_OBJECT (tree_view), "reorderable");
10014 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
10015 is pressed, other rows will be unselected.
10017 If CLAMP_NODE is true, then the sheetview will scroll to make the row
10020 pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
10022 gboolean clear_and_select,
10023 gboolean clamp_node,
10024 PsppSheetSelectMode mode)
10028 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10030 GtkTreePath *cursor_path;
10031 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10032 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
10033 gtk_tree_path_free (cursor_path);
10036 gtk_tree_row_reference_free (tree_view->priv->cursor);
10037 tree_view->priv->cursor = NULL;
10039 _pspp_sheet_view_find_node (tree_view, path, &node);
10040 tree_view->priv->cursor =
10041 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
10042 tree_view->priv->model,
10045 if (tree_view->priv->row_count > 0)
10049 if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
10050 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
10054 /* We have to re-find tree and node here again, somebody might have
10055 * cleared the node or the whole tree in the PsppSheetSelection::changed
10056 * callback. If the nodes differ we bail out here.
10058 _pspp_sheet_view_find_node (tree_view, path, &new_node);
10060 if (node != new_node)
10065 pspp_sheet_view_clamp_node_visible (tree_view, node);
10066 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
10070 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
10074 * pspp_sheet_view_get_cursor:
10075 * @tree_view: A #PsppSheetView
10076 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
10077 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
10079 * Fills in @path and @focus_column with the current path and focus column. If
10080 * the cursor isn't currently set, then *@path will be %NULL. If no column
10081 * currently has focus, then *@focus_column will be %NULL.
10083 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
10084 * you are done with it.
10087 pspp_sheet_view_get_cursor (PsppSheetView *tree_view,
10088 GtkTreePath **path,
10089 PsppSheetViewColumn **focus_column)
10091 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10095 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10096 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10103 *focus_column = tree_view->priv->focus_column;
10108 * pspp_sheet_view_set_cursor:
10109 * @tree_view: A #PsppSheetView
10110 * @path: A #GtkTreePath
10111 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10112 * @start_editing: %TRUE if the specified cell should start being edited.
10114 * Sets the current keyboard focus to be at @path, and selects it. This is
10115 * useful when you want to focus the user's attention on a particular row. If
10116 * @focus_column is not %NULL, then focus is given to the column specified by
10117 * it. Additionally, if @focus_column is specified, and @start_editing is
10118 * %TRUE, then editing should be started in the specified cell.
10119 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10120 * in order to give keyboard focus to the widget. Please note that editing
10121 * can only happen when the widget is realized.
10123 * If @path is invalid for @model, the current cursor (if any) will be unset
10124 * and the function will return without failing.
10127 pspp_sheet_view_set_cursor (PsppSheetView *tree_view,
10129 PsppSheetViewColumn *focus_column,
10130 gboolean start_editing)
10132 pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10133 NULL, start_editing);
10137 * pspp_sheet_view_set_cursor_on_cell:
10138 * @tree_view: A #PsppSheetView
10139 * @path: A #GtkTreePath
10140 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10141 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10142 * @start_editing: %TRUE if the specified cell should start being edited.
10144 * Sets the current keyboard focus to be at @path, and selects it. This is
10145 * useful when you want to focus the user's attention on a particular row. If
10146 * @focus_column is not %NULL, then focus is given to the column specified by
10147 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10148 * contains 2 or more editable or activatable cells, then focus is given to
10149 * the cell specified by @focus_cell. Additionally, if @focus_column is
10150 * specified, and @start_editing is %TRUE, then editing should be started in
10151 * the specified cell. This function is often followed by
10152 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10153 * widget. Please note that editing can only happen when the widget is
10156 * If @path is invalid for @model, the current cursor (if any) will be unset
10157 * and the function will return without failing.
10162 pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view,
10164 PsppSheetViewColumn *focus_column,
10165 GtkCellRenderer *focus_cell,
10166 gboolean start_editing)
10168 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10169 g_return_if_fail (path != NULL);
10170 g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10172 if (!tree_view->priv->model)
10177 g_return_if_fail (focus_column);
10178 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10181 /* cancel the current editing, if it exists */
10182 if (tree_view->priv->edited_column &&
10183 tree_view->priv->edited_column->editable_widget)
10184 pspp_sheet_view_stop_editing (tree_view, TRUE);
10186 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
10188 if (focus_column && focus_column->visible)
10191 gboolean column_in_tree = FALSE;
10193 for (list = tree_view->priv->columns; list; list = list->next)
10194 if (list->data == focus_column)
10196 column_in_tree = TRUE;
10199 g_return_if_fail (column_in_tree);
10200 tree_view->priv->focus_column = focus_column;
10202 pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10204 pspp_sheet_view_start_editing (tree_view, path);
10206 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10207 pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10213 * pspp_sheet_view_get_bin_window:
10214 * @tree_view: A #PsppSheetView
10216 * Returns the window that @tree_view renders to. This is used primarily to
10217 * compare to <literal>event->window</literal> to confirm that the event on
10218 * @tree_view is on the right window.
10220 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10223 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10225 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10227 return tree_view->priv->bin_window;
10231 * pspp_sheet_view_get_path_at_pos:
10232 * @tree_view: A #PsppSheetView.
10233 * @x: The x position to be identified (relative to bin_window).
10234 * @y: The y position to be identified (relative to bin_window).
10235 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10236 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10237 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10238 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10240 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10241 * (please see pspp_sheet_view_get_bin_window()).
10242 * That is, @x and @y are relative to an events coordinates. @x and @y must
10243 * come from an event on the @tree_view only where <literal>event->window ==
10244 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10245 * things like popup menus. If @path is non-%NULL, then it will be filled
10246 * with the #GtkTreePath at that point. This path should be freed with
10247 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10248 * with the column at that point. @cell_x and @cell_y return the coordinates
10249 * relative to the cell background (i.e. the @background_area passed to
10250 * gtk_cell_renderer_render()). This function is only meaningful if
10251 * @tree_view is realized. Therefore this function will always return %FALSE
10252 * if @tree_view is not realized or does not have a model.
10254 * For converting widget coordinates (eg. the ones you get from
10255 * GtkWidget::query-tooltip), please see
10256 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10258 * Return value: %TRUE if a row exists at that coordinate.
10261 pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view,
10264 GtkTreePath **path,
10265 PsppSheetViewColumn **column,
10272 g_return_val_if_fail (tree_view != NULL, FALSE);
10279 if (tree_view->priv->bin_window == NULL)
10282 if (tree_view->priv->row_count == 0)
10285 if (x > tree_view->priv->hadjustment->upper)
10288 if (x < 0 || y < 0)
10291 if (column || cell_x)
10293 PsppSheetViewColumn *tmp_column;
10294 PsppSheetViewColumn *last_column = NULL;
10296 gint remaining_x = x;
10297 gboolean found = FALSE;
10300 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10301 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10303 list = (rtl ? list->prev : list->next))
10305 tmp_column = list->data;
10307 if (tmp_column->visible == FALSE)
10310 last_column = tmp_column;
10311 if (remaining_x <= tmp_column->width)
10316 *column = tmp_column;
10319 *cell_x = remaining_x;
10323 remaining_x -= tmp_column->width;
10326 /* If found is FALSE and there is a last_column, then it the remainder
10327 * space is in that area
10334 *column = last_column;
10337 *cell_x = last_column->width + remaining_x;
10346 y_offset = pspp_sheet_view_find_offset (tree_view,
10347 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10354 *cell_y = y_offset;
10357 *path = _pspp_sheet_view_find_path (tree_view, node);
10362 /* Computes 'cell_area' from 'background_area', which must be the background
10363 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10364 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10365 the cell area as passed to _pspp_sheet_view_column_cell_render().
10367 'column' is required to properly adjust 'cell_area->x' and
10368 'cell_area->width'. It may be set to NULL if these values are not of
10369 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10372 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
10373 PsppSheetViewColumn *column,
10374 const GdkRectangle *background_area,
10375 gboolean subtract_focus_rect,
10376 GdkRectangle *cell_area)
10378 gint vertical_separator;
10379 gint horizontal_separator;
10381 *cell_area = *background_area;
10383 gtk_widget_style_get (GTK_WIDGET (tree_view),
10384 "vertical-separator", &vertical_separator,
10385 "horizontal-separator", &horizontal_separator,
10387 cell_area->x += horizontal_separator / 2;
10388 cell_area->y += vertical_separator / 2;
10389 cell_area->width -= horizontal_separator;
10390 cell_area->height -= vertical_separator;
10392 if (subtract_focus_rect)
10394 int focus_line_width;
10396 gtk_widget_style_get (GTK_WIDGET (tree_view),
10397 "focus-line-width", &focus_line_width,
10399 cell_area->x += focus_line_width;
10400 cell_area->y += focus_line_width;
10401 cell_area->width -= 2 * focus_line_width;
10402 cell_area->height -= 2 * focus_line_width;
10405 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10407 gint grid_line_width;
10408 gtk_widget_style_get (GTK_WIDGET (tree_view),
10409 "grid-line-width", &grid_line_width,
10412 if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10413 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10416 PsppSheetViewColumn *first_column, *last_column;
10419 /* Find the last visible column. */
10420 last_column = NULL;
10421 for (list = g_list_last (tree_view->priv->columns);
10425 PsppSheetViewColumn *c = list->data;
10433 /* Find the first visible column. */
10434 first_column = NULL;
10435 for (list = g_list_first (tree_view->priv->columns);
10439 PsppSheetViewColumn *c = list->data;
10447 if (column == first_column)
10449 cell_area->width -= grid_line_width / 2;
10451 else if (column == last_column)
10453 cell_area->x += grid_line_width / 2;
10454 cell_area->width -= grid_line_width / 2;
10458 cell_area->x += grid_line_width / 2;
10459 cell_area->width -= grid_line_width;
10463 if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10464 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10466 cell_area->y += grid_line_width / 2;
10467 cell_area->height -= grid_line_width;
10471 if (column == NULL)
10474 cell_area->width = 0;
10479 * pspp_sheet_view_get_cell_area:
10480 * @tree_view: a #PsppSheetView
10481 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10482 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10483 * @rect: rectangle to fill with cell rect
10485 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10486 * row specified by @path and the column specified by @column. If @path is
10487 * %NULL, or points to a path not currently displayed, the @y and @height fields
10488 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10489 * fields will be filled with 0. The sum of all cell rects does not cover the
10490 * entire tree; there are extra pixels in between rows, for example. The
10491 * returned rectangle is equivalent to the @cell_area passed to
10492 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10496 pspp_sheet_view_get_cell_area (PsppSheetView *tree_view,
10498 PsppSheetViewColumn *column,
10499 GdkRectangle *rect)
10501 GdkRectangle background_area;
10503 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10504 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10505 g_return_if_fail (rect != NULL);
10506 g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10507 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10509 pspp_sheet_view_get_background_area (tree_view, path, column,
10511 pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10516 * pspp_sheet_view_get_background_area:
10517 * @tree_view: a #PsppSheetView
10518 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10519 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10520 * @rect: rectangle to fill with cell background rect
10522 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10523 * row specified by @path and the column specified by @column. If @path is
10524 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10525 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10526 * fields will be filled with 0. The returned rectangle is equivalent to the
10527 * @background_area passed to gtk_cell_renderer_render(). These background
10528 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10529 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10530 * itself, excluding surrounding borders.
10534 pspp_sheet_view_get_background_area (PsppSheetView *tree_view,
10536 PsppSheetViewColumn *column,
10537 GdkRectangle *rect)
10541 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10542 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10543 g_return_if_fail (rect != NULL);
10552 /* Get vertical coords */
10554 _pspp_sheet_view_find_node (tree_view, path, &node);
10558 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10560 rect->height = ROW_HEIGHT (tree_view);
10567 pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10568 rect->width = x2 - rect->x;
10573 * pspp_sheet_view_get_visible_rect:
10574 * @tree_view: a #PsppSheetView
10575 * @visible_rect: rectangle to fill
10577 * Fills @visible_rect with the currently-visible region of the
10578 * buffer, in tree coordinates. Convert to bin_window coordinates with
10579 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10580 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10581 * scrollable area of the tree.
10584 pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view,
10585 GdkRectangle *visible_rect)
10589 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10591 widget = GTK_WIDGET (tree_view);
10595 visible_rect->x = tree_view->priv->hadjustment->value;
10596 visible_rect->y = tree_view->priv->vadjustment->value;
10597 visible_rect->width = widget->allocation.width;
10598 visible_rect->height = widget->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10603 * pspp_sheet_view_widget_to_tree_coords:
10604 * @tree_view: a #PsppSheetView
10605 * @wx: X coordinate relative to bin_window
10606 * @wy: Y coordinate relative to bin_window
10607 * @tx: return location for tree X coordinate
10608 * @ty: return location for tree Y coordinate
10610 * Converts bin_window coordinates to coordinates for the
10611 * tree (the full scrollable area of the tree).
10613 * Deprecated: 2.12: Due to historial reasons the name of this function is
10614 * incorrect. For converting coordinates relative to the widget to
10615 * bin_window coordinates, please see
10616 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10620 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10626 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10629 *tx = wx + tree_view->priv->hadjustment->value;
10631 *ty = wy + tree_view->priv->dy;
10635 * pspp_sheet_view_tree_to_widget_coords:
10636 * @tree_view: a #PsppSheetView
10637 * @tx: tree X coordinate
10638 * @ty: tree Y coordinate
10639 * @wx: return location for X coordinate relative to bin_window
10640 * @wy: return location for Y coordinate relative to bin_window
10642 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10643 * to bin_window coordinates.
10645 * Deprecated: 2.12: Due to historial reasons the name of this function is
10646 * incorrect. For converting bin_window coordinates to coordinates relative
10647 * to bin_window, please see
10648 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10652 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10658 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10661 *wx = tx - tree_view->priv->hadjustment->value;
10663 *wy = ty - tree_view->priv->dy;
10668 * pspp_sheet_view_convert_widget_to_tree_coords:
10669 * @tree_view: a #PsppSheetView
10670 * @wx: X coordinate relative to the widget
10671 * @wy: Y coordinate relative to the widget
10672 * @tx: return location for tree X coordinate
10673 * @ty: return location for tree Y coordinate
10675 * Converts widget coordinates to coordinates for the
10676 * tree (the full scrollable area of the tree).
10681 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10689 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10691 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10694 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10700 * pspp_sheet_view_convert_tree_to_widget_coords:
10701 * @tree_view: a #PsppSheetView
10702 * @tx: X coordinate relative to the tree
10703 * @ty: Y coordinate relative to the tree
10704 * @wx: return location for widget X coordinate
10705 * @wy: return location for widget Y coordinate
10707 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10708 * to widget coordinates.
10713 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10721 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10723 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10726 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10732 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10733 * @tree_view: a #PsppSheetView
10734 * @wx: X coordinate relative to the widget
10735 * @wy: Y coordinate relative to the widget
10736 * @bx: return location for bin_window X coordinate
10737 * @by: return location for bin_window Y coordinate
10739 * Converts widget coordinates to coordinates for the bin_window
10740 * (see pspp_sheet_view_get_bin_window()).
10745 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10751 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10754 *bx = wx + tree_view->priv->hadjustment->value;
10756 *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10760 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10761 * @tree_view: a #PsppSheetView
10762 * @bx: bin_window X coordinate
10763 * @by: bin_window Y coordinate
10764 * @wx: return location for widget X coordinate
10765 * @wy: return location for widget Y coordinate
10767 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10768 * to widget relative coordinates.
10773 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10779 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10782 *wx = bx - tree_view->priv->hadjustment->value;
10784 *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10788 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10789 * @tree_view: a #PsppSheetView
10790 * @tx: tree X coordinate
10791 * @ty: tree Y coordinate
10792 * @bx: return location for X coordinate relative to bin_window
10793 * @by: return location for Y coordinate relative to bin_window
10795 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10796 * to bin_window coordinates.
10801 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10807 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10812 *by = ty - tree_view->priv->dy;
10816 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10817 * @tree_view: a #PsppSheetView
10818 * @bx: X coordinate relative to bin_window
10819 * @by: Y coordinate relative to bin_window
10820 * @tx: return location for tree X coordinate
10821 * @ty: return location for tree Y coordinate
10823 * Converts bin_window coordinates to coordinates for the
10824 * tree (the full scrollable area of the tree).
10829 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10835 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10840 *ty = by + tree_view->priv->dy;
10846 * pspp_sheet_view_get_visible_range:
10847 * @tree_view: A #PsppSheetView
10848 * @start_path: (allow-none): Return location for start of region, or %NULL.
10849 * @end_path: (allow-none): Return location for end of region, or %NULL.
10851 * Sets @start_path and @end_path to be the first and last visible path.
10852 * Note that there may be invisible paths in between.
10854 * The paths should be freed with gtk_tree_path_free() after use.
10856 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
10861 pspp_sheet_view_get_visible_range (PsppSheetView *tree_view,
10862 GtkTreePath **start_path,
10863 GtkTreePath **end_path)
10868 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10870 if (!tree_view->priv->row_count)
10877 pspp_sheet_view_find_offset (tree_view,
10878 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
10881 *start_path = _pspp_sheet_view_find_path (tree_view, node);
10890 if (tree_view->priv->height < tree_view->priv->vadjustment->page_size)
10891 y = tree_view->priv->height - 1;
10893 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, tree_view->priv->vadjustment->page_size) - 1;
10895 pspp_sheet_view_find_offset (tree_view, y, &node);
10897 *end_path = _pspp_sheet_view_find_path (tree_view, node);
10906 unset_reorderable (PsppSheetView *tree_view)
10908 if (tree_view->priv->reorderable)
10910 tree_view->priv->reorderable = FALSE;
10911 g_object_notify (G_OBJECT (tree_view), "reorderable");
10916 * pspp_sheet_view_enable_model_drag_source:
10917 * @tree_view: a #PsppSheetView
10918 * @start_button_mask: Mask of allowed buttons to start drag
10919 * @targets: the table of targets that the drag will support
10920 * @n_targets: the number of items in @targets
10921 * @actions: the bitmask of possible actions for a drag from this
10924 * Turns @tree_view into a drag source for automatic DND. Calling this
10925 * method sets #PsppSheetView:reorderable to %FALSE.
10928 pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view,
10929 GdkModifierType start_button_mask,
10930 const GtkTargetEntry *targets,
10932 GdkDragAction actions)
10934 TreeViewDragInfo *di;
10936 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10938 gtk_drag_source_set (GTK_WIDGET (tree_view),
10944 di = ensure_info (tree_view);
10946 di->start_button_mask = start_button_mask;
10947 di->source_actions = actions;
10948 di->source_set = TRUE;
10950 unset_reorderable (tree_view);
10954 * pspp_sheet_view_enable_model_drag_dest:
10955 * @tree_view: a #PsppSheetView
10956 * @targets: the table of targets that the drag will support
10957 * @n_targets: the number of items in @targets
10958 * @actions: the bitmask of possible actions for a drag from this
10961 * Turns @tree_view into a drop destination for automatic DND. Calling
10962 * this method sets #PsppSheetView:reorderable to %FALSE.
10965 pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view,
10966 const GtkTargetEntry *targets,
10968 GdkDragAction actions)
10970 TreeViewDragInfo *di;
10972 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10974 gtk_drag_dest_set (GTK_WIDGET (tree_view),
10980 di = ensure_info (tree_view);
10981 di->dest_set = TRUE;
10983 unset_reorderable (tree_view);
10987 * pspp_sheet_view_unset_rows_drag_source:
10988 * @tree_view: a #PsppSheetView
10990 * Undoes the effect of
10991 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
10992 * #PsppSheetView:reorderable to %FALSE.
10995 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
10997 TreeViewDragInfo *di;
10999 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11001 di = get_info (tree_view);
11005 if (di->source_set)
11007 gtk_drag_source_unset (GTK_WIDGET (tree_view));
11008 di->source_set = FALSE;
11011 if (!di->dest_set && !di->source_set)
11012 remove_info (tree_view);
11015 unset_reorderable (tree_view);
11019 * pspp_sheet_view_unset_rows_drag_dest:
11020 * @tree_view: a #PsppSheetView
11022 * Undoes the effect of
11023 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
11024 * #PsppSheetView:reorderable to %FALSE.
11027 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
11029 TreeViewDragInfo *di;
11031 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11033 di = get_info (tree_view);
11039 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
11040 di->dest_set = FALSE;
11043 if (!di->dest_set && !di->source_set)
11044 remove_info (tree_view);
11047 unset_reorderable (tree_view);
11051 * pspp_sheet_view_set_drag_dest_row:
11052 * @tree_view: a #PsppSheetView
11053 * @path: (allow-none): The path of the row to highlight, or %NULL.
11054 * @pos: Specifies whether to drop before, after or into the row
11056 * Sets the row that is highlighted for feedback.
11059 pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view,
11061 PsppSheetViewDropPosition pos)
11063 GtkTreePath *current_dest;
11065 /* Note; this function is exported to allow a custom DND
11066 * implementation, so it can't touch TreeViewDragInfo
11069 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11071 current_dest = NULL;
11073 if (tree_view->priv->drag_dest_row)
11075 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11076 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
11079 /* special case a drop on an empty model */
11080 tree_view->priv->empty_view_drop = 0;
11082 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
11083 && gtk_tree_path_get_depth (path) == 1
11084 && gtk_tree_path_get_indices (path)[0] == 0)
11088 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
11092 tree_view->priv->empty_view_drop = 1;
11095 tree_view->priv->drag_dest_pos = pos;
11099 tree_view->priv->drag_dest_row =
11100 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
11101 pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
11104 tree_view->priv->drag_dest_row = NULL;
11108 int node, new_node;
11110 _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11111 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11115 new_node = pspp_sheet_view_node_next (tree_view, node);
11117 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11119 new_node = pspp_sheet_view_node_prev (tree_view, node);
11121 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11123 gtk_tree_path_free (current_dest);
11128 * pspp_sheet_view_get_drag_dest_row:
11129 * @tree_view: a #PsppSheetView
11130 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11131 * @pos: (allow-none): Return location for the drop position, or %NULL
11133 * Gets information about the row that is highlighted for feedback.
11136 pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view,
11137 GtkTreePath **path,
11138 PsppSheetViewDropPosition *pos)
11140 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11144 if (tree_view->priv->drag_dest_row)
11145 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11148 if (tree_view->priv->empty_view_drop)
11149 *path = gtk_tree_path_new_from_indices (0, -1);
11156 *pos = tree_view->priv->drag_dest_pos;
11160 * pspp_sheet_view_get_dest_row_at_pos:
11161 * @tree_view: a #PsppSheetView
11162 * @drag_x: the position to determine the destination row for
11163 * @drag_y: the position to determine the destination row for
11164 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11165 * @pos: (allow-none): Return location for the drop position, or %NULL
11167 * Determines the destination row for a given position. @drag_x and
11168 * @drag_y are expected to be in widget coordinates. This function is only
11169 * meaningful if @tree_view is realized. Therefore this function will always
11170 * return %FALSE if @tree_view is not realized or does not have a model.
11172 * Return value: whether there is a row at the given position, %TRUE if this
11173 * is indeed the case.
11176 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view,
11179 GtkTreePath **path,
11180 PsppSheetViewDropPosition *pos)
11184 gdouble offset_into_row;
11187 PsppSheetViewColumn *column = NULL;
11188 GtkTreePath *tmp_path = NULL;
11190 /* Note; this function is exported to allow a custom DND
11191 * implementation, so it can't touch TreeViewDragInfo
11194 g_return_val_if_fail (tree_view != NULL, FALSE);
11195 g_return_val_if_fail (drag_x >= 0, FALSE);
11196 g_return_val_if_fail (drag_y >= 0, FALSE);
11201 if (tree_view->priv->bin_window == NULL)
11204 if (tree_view->priv->row_count == 0)
11207 /* If in the top third of a row, we drop before that row; if
11208 * in the bottom third, drop after that row; if in the middle,
11209 * and the row has children, drop into the row.
11211 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11214 if (!pspp_sheet_view_get_path_at_pos (tree_view,
11223 pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11226 offset_into_row = cell_y;
11231 gtk_tree_path_free (tmp_path);
11235 third = cell.height / 3.0;
11239 if (offset_into_row < third)
11241 *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11243 else if (offset_into_row < (cell.height / 2.0))
11245 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11247 else if (offset_into_row < third * 2.0)
11249 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11253 *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11262 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11264 * pspp_sheet_view_create_row_drag_icon:
11265 * @tree_view: a #PsppSheetView
11266 * @path: a #GtkTreePath in @tree_view
11268 * Creates a #GdkPixmap representation of the row at @path.
11269 * This image is used for a drag icon.
11271 * Return value: a newly-allocated pixmap of the drag icon.
11274 pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view,
11281 GdkRectangle background_area;
11282 GdkRectangle expose_area;
11284 /* start drawing inside the black outline */
11286 GdkDrawable *drawable;
11287 gint bin_window_width;
11290 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11291 g_return_val_if_fail (path != NULL, NULL);
11293 widget = GTK_WIDGET (tree_view);
11295 if (!gtk_widget_get_realized (widget))
11298 _pspp_sheet_view_find_node (tree_view,
11305 if (!gtk_tree_model_get_iter (tree_view->priv->model,
11312 background_area.y = y;
11313 background_area.height = ROW_HEIGHT (tree_view);
11315 gdk_drawable_get_size (tree_view->priv->bin_window,
11316 &bin_window_width, NULL);
11318 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11319 bin_window_width + 2,
11320 background_area.height + 2,
11325 expose_area.width = bin_window_width + 2;
11326 expose_area.height = background_area.height + 2;
11328 gdk_draw_rectangle (drawable,
11329 widget->style->base_gc [gtk_widget_get_state (widget)],
11332 bin_window_width + 2,
11333 background_area.height + 2);
11335 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11337 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11339 list = (rtl ? list->prev : list->next))
11341 PsppSheetViewColumn *column = list->data;
11342 GdkRectangle cell_area;
11343 gint vertical_separator;
11345 if (!column->visible)
11348 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11350 background_area.x = cell_offset;
11351 background_area.width = column->width;
11353 gtk_widget_style_get (widget,
11354 "vertical-separator", &vertical_separator,
11357 cell_area = background_area;
11359 cell_area.y += vertical_separator / 2;
11360 cell_area.height -= vertical_separator;
11362 if (pspp_sheet_view_column_cell_is_visible (column))
11363 _pspp_sheet_view_column_cell_render (column,
11369 cell_offset += column->width;
11372 gdk_draw_rectangle (drawable,
11373 widget->style->black_gc,
11376 bin_window_width + 1,
11377 background_area.height + 1);
11384 * pspp_sheet_view_set_destroy_count_func:
11385 * @tree_view: A #PsppSheetView
11386 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11387 * @data: (allow-none): User data to be passed to @func, or %NULL
11388 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11390 * This function should almost never be used. It is meant for private use by
11391 * ATK for determining the number of visible children that are removed when a row is deleted.
11394 pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view,
11395 PsppSheetDestroyCountFunc func,
11397 GDestroyNotify destroy)
11399 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11401 if (tree_view->priv->destroy_count_destroy)
11402 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11404 tree_view->priv->destroy_count_func = func;
11405 tree_view->priv->destroy_count_data = data;
11406 tree_view->priv->destroy_count_destroy = destroy;
11411 * Interactive search
11415 * pspp_sheet_view_set_enable_search:
11416 * @tree_view: A #PsppSheetView
11417 * @enable_search: %TRUE, if the user can search interactively
11419 * If @enable_search is set, then the user can type in text to search through
11420 * the tree interactively (this is sometimes called "typeahead find").
11422 * Note that even if this is %FALSE, the user can still initiate a search
11423 * using the "start-interactive-search" key binding.
11426 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11427 gboolean enable_search)
11429 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11431 enable_search = !!enable_search;
11433 if (tree_view->priv->enable_search != enable_search)
11435 tree_view->priv->enable_search = enable_search;
11436 g_object_notify (G_OBJECT (tree_view), "enable-search");
11441 * pspp_sheet_view_get_enable_search:
11442 * @tree_view: A #PsppSheetView
11444 * Returns whether or not the tree allows to start interactive searching
11445 * by typing in text.
11447 * Return value: whether or not to let the user search interactively
11450 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11452 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11454 return tree_view->priv->enable_search;
11459 * pspp_sheet_view_get_search_column:
11460 * @tree_view: A #PsppSheetView
11462 * Gets the column searched on by the interactive search code.
11464 * Return value: the column the interactive search code searches in.
11467 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11469 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11471 return (tree_view->priv->search_column);
11475 * pspp_sheet_view_set_search_column:
11476 * @tree_view: A #PsppSheetView
11477 * @column: the column of the model to search in, or -1 to disable searching
11479 * Sets @column as the column where the interactive search code should
11480 * search in for the current model.
11482 * If the search column is set, users can use the "start-interactive-search"
11483 * key binding to bring up search popup. The enable-search property controls
11484 * whether simply typing text will also start an interactive search.
11486 * Note that @column refers to a column of the current model. The search
11487 * column is reset to -1 when the model is changed.
11490 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11493 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11494 g_return_if_fail (column >= -1);
11496 if (tree_view->priv->search_column == column)
11499 tree_view->priv->search_column = column;
11500 g_object_notify (G_OBJECT (tree_view), "search-column");
11504 * pspp_sheet_view_get_search_equal_func:
11505 * @tree_view: A #PsppSheetView
11507 * Returns the compare function currently in use.
11509 * Return value: the currently used compare function for the search code.
11512 PsppSheetViewSearchEqualFunc
11513 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11515 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11517 return tree_view->priv->search_equal_func;
11521 * pspp_sheet_view_set_search_equal_func:
11522 * @tree_view: A #PsppSheetView
11523 * @search_equal_func: the compare function to use during the search
11524 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11525 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11527 * Sets the compare function for the interactive search capabilities; note
11528 * that somewhat like strcmp() returning 0 for equality
11529 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11532 pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view,
11533 PsppSheetViewSearchEqualFunc search_equal_func,
11534 gpointer search_user_data,
11535 GDestroyNotify search_destroy)
11537 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11538 g_return_if_fail (search_equal_func != NULL);
11540 if (tree_view->priv->search_destroy)
11541 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11543 tree_view->priv->search_equal_func = search_equal_func;
11544 tree_view->priv->search_user_data = search_user_data;
11545 tree_view->priv->search_destroy = search_destroy;
11546 if (tree_view->priv->search_equal_func == NULL)
11547 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11551 * pspp_sheet_view_get_search_entry:
11552 * @tree_view: A #PsppSheetView
11554 * Returns the #GtkEntry which is currently in use as interactive search
11555 * entry for @tree_view. In case the built-in entry is being used, %NULL
11556 * will be returned.
11558 * Return value: the entry currently in use as search entry.
11563 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11565 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11567 if (tree_view->priv->search_custom_entry_set)
11568 return GTK_ENTRY (tree_view->priv->search_entry);
11574 * pspp_sheet_view_set_search_entry:
11575 * @tree_view: A #PsppSheetView
11576 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11578 * Sets the entry which the interactive search code will use for this
11579 * @tree_view. This is useful when you want to provide a search entry
11580 * in our interface at all time at a fixed position. Passing %NULL for
11581 * @entry will make the interactive search code use the built-in popup
11587 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11590 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11591 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11593 if (tree_view->priv->search_custom_entry_set)
11595 if (tree_view->priv->search_entry_changed_id)
11597 g_signal_handler_disconnect (tree_view->priv->search_entry,
11598 tree_view->priv->search_entry_changed_id);
11599 tree_view->priv->search_entry_changed_id = 0;
11601 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11602 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11605 g_object_unref (tree_view->priv->search_entry);
11607 else if (tree_view->priv->search_window)
11609 gtk_widget_destroy (tree_view->priv->search_window);
11611 tree_view->priv->search_window = NULL;
11616 tree_view->priv->search_entry = g_object_ref (entry);
11617 tree_view->priv->search_custom_entry_set = TRUE;
11619 if (tree_view->priv->search_entry_changed_id == 0)
11621 tree_view->priv->search_entry_changed_id =
11622 g_signal_connect (tree_view->priv->search_entry, "changed",
11623 G_CALLBACK (pspp_sheet_view_search_init),
11627 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11628 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11631 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11635 tree_view->priv->search_entry = NULL;
11636 tree_view->priv->search_custom_entry_set = FALSE;
11641 * pspp_sheet_view_set_search_position_func:
11642 * @tree_view: A #PsppSheetView
11643 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11644 * to use the default search position function
11645 * @data: (allow-none): user data to pass to @func, or %NULL
11646 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11648 * Sets the function to use when positioning the search dialog.
11653 pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view,
11654 PsppSheetViewSearchPositionFunc func,
11655 gpointer user_data,
11656 GDestroyNotify destroy)
11658 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11660 if (tree_view->priv->search_position_destroy)
11661 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11663 tree_view->priv->search_position_func = func;
11664 tree_view->priv->search_position_user_data = user_data;
11665 tree_view->priv->search_position_destroy = destroy;
11666 if (tree_view->priv->search_position_func == NULL)
11667 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11671 * pspp_sheet_view_get_search_position_func:
11672 * @tree_view: A #PsppSheetView
11674 * Returns the positioning function currently in use.
11676 * Return value: the currently used function for positioning the search dialog.
11680 PsppSheetViewSearchPositionFunc
11681 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11683 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11685 return tree_view->priv->search_position_func;
11690 pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
11691 PsppSheetView *tree_view)
11693 if (tree_view->priv->disable_popdown)
11696 if (tree_view->priv->search_entry_changed_id)
11698 g_signal_handler_disconnect (tree_view->priv->search_entry,
11699 tree_view->priv->search_entry_changed_id);
11700 tree_view->priv->search_entry_changed_id = 0;
11702 if (tree_view->priv->typeselect_flush_timeout)
11704 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11705 tree_view->priv->typeselect_flush_timeout = 0;
11708 if (gtk_widget_get_visible (search_dialog))
11710 /* send focus-in event */
11711 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11712 gtk_widget_hide (search_dialog);
11713 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11714 send_focus_change (GTK_WIDGET (tree_view), TRUE);
11719 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11720 GtkWidget *search_dialog,
11721 gpointer user_data)
11724 gint tree_x, tree_y;
11725 gint tree_width, tree_height;
11726 GdkWindow *tree_window = GTK_WIDGET (tree_view)->window;
11727 GdkScreen *screen = gdk_drawable_get_screen (tree_window);
11728 GtkRequisition requisition;
11730 GdkRectangle monitor;
11732 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11733 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11735 gtk_widget_realize (search_dialog);
11737 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11738 gdk_drawable_get_size (tree_window,
11741 gtk_widget_size_request (search_dialog, &requisition);
11743 if (tree_x + tree_width > gdk_screen_get_width (screen))
11744 x = gdk_screen_get_width (screen) - requisition.width;
11745 else if (tree_x + tree_width - requisition.width < 0)
11748 x = tree_x + tree_width - requisition.width;
11750 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11751 y = gdk_screen_get_height (screen) - requisition.height;
11752 else if (tree_y + tree_height < 0) /* isn't really possible ... */
11755 y = tree_y + tree_height;
11757 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11761 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11765 PsppSheetView *tree_view = (PsppSheetView *)data;
11767 tree_view->priv->disable_popdown = 1;
11768 g_signal_connect (menu, "hide",
11769 G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11772 /* Because we're visible but offscreen, we just set a flag in the preedit
11776 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11777 PsppSheetView *tree_view)
11779 tree_view->priv->imcontext_changed = 1;
11780 if (tree_view->priv->typeselect_flush_timeout)
11782 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11783 tree_view->priv->typeselect_flush_timeout =
11784 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11785 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11792 pspp_sheet_view_search_activate (GtkEntry *entry,
11793 PsppSheetView *tree_view)
11798 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11801 /* If we have a row selected and it's the cursor row, we activate
11803 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11805 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11807 _pspp_sheet_view_find_node (tree_view, path, &node);
11809 if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11810 pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11812 gtk_tree_path_free (path);
11817 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11819 PsppSheetView *tree_view = (PsppSheetView *)data;
11821 tree_view->priv->disable_popdown = 0;
11827 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11830 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11834 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11835 GdkEventAny *event,
11836 PsppSheetView *tree_view)
11838 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11840 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11846 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11847 GdkEventButton *event,
11848 PsppSheetView *tree_view)
11850 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11852 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11854 if (event->window == tree_view->priv->bin_window)
11855 pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
11861 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
11862 GdkEventScroll *event,
11863 PsppSheetView *tree_view)
11865 gboolean retval = FALSE;
11867 if (event->direction == GDK_SCROLL_UP)
11869 pspp_sheet_view_search_move (widget, tree_view, TRUE);
11872 else if (event->direction == GDK_SCROLL_DOWN)
11874 pspp_sheet_view_search_move (widget, tree_view, FALSE);
11878 /* renew the flush timeout */
11879 if (retval && tree_view->priv->typeselect_flush_timeout
11880 && !tree_view->priv->search_custom_entry_set)
11882 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11883 tree_view->priv->typeselect_flush_timeout =
11884 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11885 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11893 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
11894 GdkEventKey *event,
11895 PsppSheetView *tree_view)
11897 gboolean retval = FALSE;
11899 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11900 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11902 /* close window and cancel the search */
11903 if (!tree_view->priv->search_custom_entry_set
11904 && (event->keyval == GDK_Escape ||
11905 event->keyval == GDK_Tab ||
11906 event->keyval == GDK_KP_Tab ||
11907 event->keyval == GDK_ISO_Left_Tab))
11909 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11913 /* select previous matching iter */
11914 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
11916 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11917 gtk_widget_error_bell (widget);
11922 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
11923 && (event->keyval == GDK_g || event->keyval == GDK_G))
11925 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11926 gtk_widget_error_bell (widget);
11931 /* select next matching iter */
11932 if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
11934 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11935 gtk_widget_error_bell (widget);
11940 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
11941 && (event->keyval == GDK_g || event->keyval == GDK_G))
11943 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11944 gtk_widget_error_bell (widget);
11949 /* renew the flush timeout */
11950 if (retval && tree_view->priv->typeselect_flush_timeout
11951 && !tree_view->priv->search_custom_entry_set)
11953 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11954 tree_view->priv->typeselect_flush_timeout =
11955 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11956 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11963 /* this function returns FALSE if there is a search string but
11964 * nothing was found, and TRUE otherwise.
11967 pspp_sheet_view_search_move (GtkWidget *window,
11968 PsppSheetView *tree_view,
11976 GtkTreeModel *model;
11977 PsppSheetSelection *selection;
11979 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
11981 g_return_val_if_fail (text != NULL, FALSE);
11983 len = strlen (text);
11985 if (up && tree_view->priv->selected_iter == 1)
11986 return strlen (text) < 1;
11988 len = strlen (text);
11993 model = pspp_sheet_view_get_model (tree_view);
11994 selection = pspp_sheet_view_get_selection (tree_view);
11997 pspp_sheet_selection_unselect_all (selection);
11998 if (!gtk_tree_model_get_iter_first (model, &iter))
12001 ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
12002 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
12007 tree_view->priv->selected_iter += up?(-1):(1);
12012 /* return to old iter */
12014 gtk_tree_model_get_iter_first (model, &iter);
12015 pspp_sheet_view_search_iter (model, selection,
12017 &count, tree_view->priv->selected_iter);
12023 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
12027 gpointer search_data)
12029 gboolean retval = TRUE;
12031 gchar *normalized_string;
12032 gchar *normalized_key;
12033 gchar *case_normalized_string = NULL;
12034 gchar *case_normalized_key = NULL;
12035 GValue value = {0,};
12036 GValue transformed = {0,};
12038 gtk_tree_model_get_value (model, iter, column, &value);
12040 g_value_init (&transformed, G_TYPE_STRING);
12042 if (!g_value_transform (&value, &transformed))
12044 g_value_unset (&value);
12048 g_value_unset (&value);
12050 str = g_value_get_string (&transformed);
12053 g_value_unset (&transformed);
12057 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
12058 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
12060 if (normalized_string && normalized_key)
12062 case_normalized_string = g_utf8_casefold (normalized_string, -1);
12063 case_normalized_key = g_utf8_casefold (normalized_key, -1);
12065 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
12069 g_value_unset (&transformed);
12070 g_free (normalized_key);
12071 g_free (normalized_string);
12072 g_free (case_normalized_key);
12073 g_free (case_normalized_string);
12079 pspp_sheet_view_search_iter (GtkTreeModel *model,
12080 PsppSheetSelection *selection,
12089 PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
12091 path = gtk_tree_model_get_path (model, iter);
12092 _pspp_sheet_view_find_node (tree_view, path, &node);
12096 gboolean done = FALSE;
12098 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
12103 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
12105 pspp_sheet_selection_select_iter (selection, iter);
12106 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12109 gtk_tree_path_free (path);
12118 node = pspp_sheet_view_node_next (tree_view, node);
12124 has_next = gtk_tree_model_iter_next (model, iter);
12127 gtk_tree_path_next (path);
12130 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12135 gtk_tree_path_free (path);
12137 /* we've run out of tree, done with this func */
12149 pspp_sheet_view_search_init (GtkWidget *entry,
12150 PsppSheetView *tree_view)
12156 GtkTreeModel *model;
12157 PsppSheetSelection *selection;
12159 g_return_if_fail (GTK_IS_ENTRY (entry));
12160 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12162 text = gtk_entry_get_text (GTK_ENTRY (entry));
12164 model = pspp_sheet_view_get_model (tree_view);
12165 selection = pspp_sheet_view_get_selection (tree_view);
12168 pspp_sheet_selection_unselect_all (selection);
12169 if (tree_view->priv->typeselect_flush_timeout
12170 && !tree_view->priv->search_custom_entry_set)
12172 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12173 tree_view->priv->typeselect_flush_timeout =
12174 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12175 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12182 if (!gtk_tree_model_get_iter_first (model, &iter))
12185 ret = pspp_sheet_view_search_iter (model, selection,
12190 tree_view->priv->selected_iter = 1;
12194 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12195 PsppSheetView *tree_view)
12197 if (tree_view->priv->edited_column == NULL)
12200 _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12201 tree_view->priv->edited_column = NULL;
12203 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12204 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12206 g_signal_handlers_disconnect_by_func (cell_editable,
12207 pspp_sheet_view_remove_widget,
12209 g_signal_handlers_disconnect_by_func (cell_editable,
12210 pspp_sheet_view_editable_button_press_event,
12212 g_signal_handlers_disconnect_by_func (cell_editable,
12213 pspp_sheet_view_editable_clicked,
12216 gtk_container_remove (GTK_CONTAINER (tree_view),
12217 GTK_WIDGET (cell_editable));
12219 /* FIXME should only redraw a single node */
12220 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12224 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12225 GtkTreePath *cursor_path)
12228 GdkRectangle background_area;
12229 GdkRectangle cell_area;
12230 GtkCellEditable *editable_widget = NULL;
12231 gchar *path_string;
12232 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12233 gint retval = FALSE;
12236 g_assert (tree_view->priv->focus_column);
12238 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12241 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12242 if (cursor_node < 0)
12245 path_string = gtk_tree_path_to_string (cursor_path);
12246 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12248 pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12249 tree_view->priv->model,
12251 pspp_sheet_view_get_background_area (tree_view,
12253 tree_view->priv->focus_column,
12255 pspp_sheet_view_get_cell_area (tree_view,
12257 tree_view->priv->focus_column,
12260 if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12269 if (editable_widget != NULL)
12273 GtkCellRenderer *cell;
12276 cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12278 _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12281 area.width -= right + left;
12283 pspp_sheet_view_real_start_editing (tree_view,
12284 tree_view->priv->focus_column,
12293 g_free (path_string);
12298 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12299 GdkEventButton *event,
12300 PsppSheetView *sheet_view)
12304 node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12305 "pspp-sheet-view-node"));
12306 return pspp_sheet_view_row_head_clicked (sheet_view,
12308 sheet_view->priv->edited_column,
12313 pspp_sheet_view_editable_clicked (GtkButton *button,
12314 PsppSheetView *sheet_view)
12316 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12321 is_all_selected (GtkWidget *widget)
12323 GtkEntryBuffer *buffer;
12324 gint start_pos, end_pos;
12326 if (!GTK_IS_ENTRY (widget))
12329 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12330 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12331 &start_pos, &end_pos)
12333 && end_pos == gtk_entry_buffer_get_length (buffer));
12337 is_at_left (GtkWidget *widget)
12339 return (GTK_IS_ENTRY (widget)
12340 && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12344 is_at_right (GtkWidget *widget)
12346 GtkEntryBuffer *buffer;
12349 if (!GTK_IS_ENTRY (widget))
12352 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12353 length = gtk_entry_buffer_get_length (buffer);
12354 return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12358 pspp_sheet_view_event (GtkWidget *widget,
12359 GdkEventKey *event,
12360 PsppSheetView *tree_view)
12362 PsppSheetViewColumn *column;
12369 /* Intercept only key press events.
12370 It would make sense to use "key-press-event" instead of "event", but
12371 GtkEntry attaches its own signal handler to "key-press-event" that runs
12372 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12374 if (event->type != GDK_KEY_PRESS)
12377 keyval = event->keyval;
12379 switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12382 switch (event->keyval)
12384 case GDK_Left: case GDK_KP_Left:
12385 case GDK_Home: case GDK_KP_Home:
12386 if (!is_all_selected (widget) && !is_at_left (widget))
12390 case GDK_Right: case GDK_KP_Right:
12391 case GDK_End: case GDK_KP_End:
12392 if (!is_all_selected (widget) && !is_at_right (widget))
12396 case GDK_Up: case GDK_KP_Up:
12397 case GDK_Down: case GDK_KP_Down:
12400 case GDK_Page_Up: case GDK_KP_Page_Up:
12401 case GDK_Page_Down: case GDK_KP_Page_Down:
12412 case GDK_Tab: case GDK_KP_Tab:
12413 case GDK_ISO_Left_Tab:
12422 case GDK_SHIFT_MASK:
12423 switch (event->keyval)
12426 case GDK_ISO_Left_Tab:
12435 case GDK_CONTROL_MASK:
12436 switch (event->keyval)
12438 case GDK_Left: case GDK_KP_Left:
12439 if (!is_all_selected (widget) && !is_at_left (widget))
12443 case GDK_Right: case GDK_KP_Right:
12444 if (!is_all_selected (widget) && !is_at_right (widget))
12448 case GDK_Up: case GDK_KP_Up:
12449 case GDK_Down: case GDK_KP_Down:
12461 row = tree_view->priv->edited_row;
12462 column = tree_view->priv->edited_column;
12463 path = gtk_tree_path_new_from_indices (row, -1);
12465 pspp_sheet_view_stop_editing (tree_view, cancel);
12466 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12468 pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12469 gtk_tree_path_free (path);
12471 handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12472 GTK_OBJECT (tree_view));
12474 g_signal_stop_emission_by_name (widget, "event");
12476 pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12477 pspp_sheet_view_start_editing (tree_view, path);
12478 gtk_tree_path_free (path);
12484 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12487 PsppSheetView *sheet_view = data;
12489 g_signal_connect (widget, "event",
12490 G_CALLBACK (pspp_sheet_view_event),
12493 if (GTK_IS_CONTAINER (widget))
12494 gtk_container_foreach (GTK_CONTAINER (widget),
12495 pspp_sheet_view_override_cell_keypresses,
12500 pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
12501 PsppSheetViewColumn *column,
12503 GtkCellEditable *cell_editable,
12504 GdkRectangle *cell_area,
12508 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12509 gint pre_val = tree_view->priv->vadjustment->value;
12510 GtkRequisition requisition;
12513 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12515 tree_view->priv->edited_column = column;
12516 _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12518 row = gtk_tree_path_get_indices (path)[0];
12519 tree_view->priv->edited_row = row;
12520 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12521 cell_area->y += pre_val - (int)tree_view->priv->vadjustment->value;
12523 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12524 pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12525 tree_view->priv->anchor_column = column;
12527 gtk_widget_size_request (GTK_WIDGET (cell_editable), &requisition);
12529 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12531 if (requisition.height < cell_area->height)
12533 gint diff = cell_area->height - requisition.height;
12534 pspp_sheet_view_put (tree_view,
12535 GTK_WIDGET (cell_editable),
12536 cell_area->x, cell_area->y + diff/2,
12537 cell_area->width, requisition.height);
12541 pspp_sheet_view_put (tree_view,
12542 GTK_WIDGET (cell_editable),
12543 cell_area->x, cell_area->y,
12544 cell_area->width, cell_area->height);
12547 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12548 (GdkEvent *)event);
12550 gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12551 g_signal_connect (cell_editable, "remove-widget",
12552 G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12553 if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12554 GTK_IS_BUTTON (cell_editable))
12556 g_signal_connect (cell_editable, "button-press-event",
12557 G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12559 g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12560 GINT_TO_POINTER (row));
12561 g_signal_connect (cell_editable, "clicked",
12562 G_CALLBACK (pspp_sheet_view_editable_clicked),
12566 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12571 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12572 gboolean cancel_editing)
12574 PsppSheetViewColumn *column;
12575 GtkCellRenderer *cell;
12577 if (tree_view->priv->edited_column == NULL)
12581 * This is very evil. We need to do this, because
12582 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12583 * later on. If pspp_sheet_view_row_changed notices
12584 * tree_view->priv->edited_column != NULL, it'll call
12585 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12587 * Please read that again if you intend to modify anything here.
12590 column = tree_view->priv->edited_column;
12591 tree_view->priv->edited_column = NULL;
12593 cell = _pspp_sheet_view_column_get_edited_cell (column);
12594 gtk_cell_renderer_stop_editing (cell, cancel_editing);
12596 if (!cancel_editing)
12597 gtk_cell_editable_editing_done (column->editable_widget);
12599 tree_view->priv->edited_column = column;
12601 gtk_cell_editable_remove_widget (column->editable_widget);
12606 * pspp_sheet_view_set_hover_selection:
12607 * @tree_view: a #PsppSheetView
12608 * @hover: %TRUE to enable hover selection mode
12610 * Enables of disables the hover selection mode of @tree_view.
12611 * Hover selection makes the selected row follow the pointer.
12612 * Currently, this works only for the selection modes
12613 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12618 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12621 hover = hover != FALSE;
12623 if (hover != tree_view->priv->hover_selection)
12625 tree_view->priv->hover_selection = hover;
12627 g_object_notify (G_OBJECT (tree_view), "hover-selection");
12632 * pspp_sheet_view_get_hover_selection:
12633 * @tree_view: a #PsppSheetView
12635 * Returns whether hover selection mode is turned on for @tree_view.
12637 * Return value: %TRUE if @tree_view is in hover selection mode
12642 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12644 return tree_view->priv->hover_selection;
12648 * pspp_sheet_view_set_rubber_banding:
12649 * @tree_view: a #PsppSheetView
12650 * @enable: %TRUE to enable rubber banding
12652 * Enables or disables rubber banding in @tree_view. If the selection mode is
12653 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12654 * banding will allow the user to select multiple rows by dragging the mouse.
12659 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12662 enable = enable != FALSE;
12664 if (enable != tree_view->priv->rubber_banding_enable)
12666 tree_view->priv->rubber_banding_enable = enable;
12668 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12673 * pspp_sheet_view_get_rubber_banding:
12674 * @tree_view: a #PsppSheetView
12676 * Returns whether rubber banding is turned on for @tree_view. If the
12677 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12678 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12679 * select multiple rows by dragging the mouse.
12681 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12686 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12688 return tree_view->priv->rubber_banding_enable;
12692 * pspp_sheet_view_is_rubber_banding_active:
12693 * @tree_view: a #PsppSheetView
12695 * Returns whether a rubber banding operation is currently being done
12698 * Return value: %TRUE if a rubber banding operation is currently being
12699 * done in @tree_view.
12704 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12706 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12708 if (tree_view->priv->rubber_banding_enable
12709 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12716 pspp_sheet_view_grab_notify (GtkWidget *widget,
12717 gboolean was_grabbed)
12719 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12721 tree_view->priv->in_grab = !was_grabbed;
12725 tree_view->priv->pressed_button = -1;
12727 if (tree_view->priv->rubber_band_status)
12728 pspp_sheet_view_stop_rubber_band (tree_view);
12733 pspp_sheet_view_state_changed (GtkWidget *widget,
12734 GtkStateType previous_state)
12736 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12738 if (gtk_widget_get_realized (widget))
12740 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
12741 gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
12744 gtk_widget_queue_draw (widget);
12748 * pspp_sheet_view_get_grid_lines:
12749 * @tree_view: a #PsppSheetView
12751 * Returns which grid lines are enabled in @tree_view.
12753 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12758 PsppSheetViewGridLines
12759 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12761 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12763 return tree_view->priv->grid_lines;
12767 * pspp_sheet_view_set_grid_lines:
12768 * @tree_view: a #PsppSheetView
12769 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12772 * Sets which grid lines to draw in @tree_view.
12777 pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view,
12778 PsppSheetViewGridLines grid_lines)
12780 PsppSheetViewPrivate *priv;
12781 PsppSheetViewGridLines old_grid_lines;
12783 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12785 priv = tree_view->priv;
12787 old_grid_lines = priv->grid_lines;
12788 priv->grid_lines = grid_lines;
12790 if (old_grid_lines != grid_lines)
12792 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12794 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12799 * pspp_sheet_view_get_special_cells:
12800 * @tree_view: a #PsppSheetView
12802 * Returns which grid lines are enabled in @tree_view.
12804 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12805 * the sheet view contain special cells.
12807 PsppSheetViewSpecialCells
12808 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12810 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12812 return tree_view->priv->special_cells;
12816 * pspp_sheet_view_set_special_cells:
12817 * @tree_view: a #PsppSheetView
12818 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12819 * the sheet view contain special cells.
12821 * Sets whether rows in the sheet view contain special cells, controlling the
12822 * rendering of row selections.
12825 pspp_sheet_view_set_special_cells (PsppSheetView *tree_view,
12826 PsppSheetViewSpecialCells special_cells)
12828 PsppSheetViewPrivate *priv;
12830 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12832 priv = tree_view->priv;
12834 if (priv->special_cells != special_cells)
12836 priv->special_cells = special_cells;
12837 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12838 g_object_notify (G_OBJECT (tree_view), "special-cells");
12843 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12845 /* XXX (re)calculate fixed_height if necessary */
12846 return tree_view->priv->fixed_height;
12850 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
12853 g_return_if_fail (fixed_height > 0);
12855 if (tree_view->priv->fixed_height != fixed_height)
12857 tree_view->priv->fixed_height = fixed_height;
12858 g_object_notify (G_OBJECT (tree_view), "fixed-height");
12860 if (!tree_view->priv->fixed_height_set)
12862 tree_view->priv->fixed_height_set = TRUE;
12863 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
12868 * pspp_sheet_view_set_tooltip_row:
12869 * @tree_view: a #PsppSheetView
12870 * @tooltip: a #GtkTooltip
12871 * @path: a #GtkTreePath
12873 * Sets the tip area of @tooltip to be the area covered by the row at @path.
12874 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12875 * See also gtk_tooltip_set_tip_area().
12880 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
12881 GtkTooltip *tooltip,
12884 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12885 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12887 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
12891 * pspp_sheet_view_set_tooltip_cell:
12892 * @tree_view: a #PsppSheetView
12893 * @tooltip: a #GtkTooltip
12894 * @path: (allow-none): a #GtkTreePath or %NULL
12895 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
12896 * @cell: (allow-none): a #GtkCellRenderer or %NULL
12898 * Sets the tip area of @tooltip to the area @path, @column and @cell have
12899 * in common. For example if @path is %NULL and @column is set, the tip
12900 * area will be set to the full area covered by @column. See also
12901 * gtk_tooltip_set_tip_area().
12903 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12908 pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view,
12909 GtkTooltip *tooltip,
12911 PsppSheetViewColumn *column,
12912 GtkCellRenderer *cell)
12916 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12917 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12918 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
12919 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
12921 /* Determine x values. */
12922 if (column && cell)
12927 pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
12928 pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
12930 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12933 rect.width = width;
12939 pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
12940 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12943 rect.width = tmp.width;
12948 rect.width = GTK_WIDGET (tree_view)->allocation.width;
12951 /* Determine y values. */
12956 pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
12957 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12960 rect.height = tmp.height;
12965 rect.height = tree_view->priv->vadjustment->page_size;
12968 gtk_tooltip_set_tip_area (tooltip, &rect);
12972 * pspp_sheet_view_get_tooltip_context:
12973 * @tree_view: a #PsppSheetView
12974 * @x: the x coordinate (relative to widget coordinates)
12975 * @y: the y coordinate (relative to widget coordinates)
12976 * @keyboard_tip: whether this is a keyboard tooltip or not
12977 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
12978 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
12979 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
12981 * This function is supposed to be used in a #GtkWidget::query-tooltip
12982 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
12983 * which are received in the signal handler, should be passed to this
12984 * function without modification.
12986 * The return value indicates whether there is a tree view row at the given
12987 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
12988 * tooltips the row returned will be the cursor row. When %TRUE, then any of
12989 * @model, @path and @iter which have been provided will be set to point to
12990 * that row and the corresponding model. @x and @y will always be converted
12991 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
12993 * Return value: whether or not the given tooltip context points to a row.
12998 pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view,
13001 gboolean keyboard_tip,
13002 GtkTreeModel **model,
13003 GtkTreePath **path,
13006 GtkTreePath *tmppath = NULL;
13008 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
13009 g_return_val_if_fail (x != NULL, FALSE);
13010 g_return_val_if_fail (y != NULL, FALSE);
13014 pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
13021 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
13024 if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
13025 &tmppath, NULL, NULL, NULL))
13030 *model = pspp_sheet_view_get_model (tree_view);
13033 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
13039 gtk_tree_path_free (tmppath);
13045 pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget,
13048 gboolean keyboard_tip,
13049 GtkTooltip *tooltip,
13052 GValue value = { 0, };
13053 GValue transformed = { 0, };
13056 GtkTreeModel *model;
13057 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
13059 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
13062 &model, &path, &iter))
13065 gtk_tree_model_get_value (model, &iter,
13066 tree_view->priv->tooltip_column, &value);
13068 g_value_init (&transformed, G_TYPE_STRING);
13070 if (!g_value_transform (&value, &transformed))
13072 g_value_unset (&value);
13073 gtk_tree_path_free (path);
13078 g_value_unset (&value);
13080 if (!g_value_get_string (&transformed))
13082 g_value_unset (&transformed);
13083 gtk_tree_path_free (path);
13088 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
13089 pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
13091 gtk_tree_path_free (path);
13092 g_value_unset (&transformed);
13098 * pspp_sheet_view_set_tooltip_column:
13099 * @tree_view: a #PsppSheetView
13100 * @column: an integer, which is a valid column number for @tree_view's model
13102 * If you only plan to have simple (text-only) tooltips on full rows, you
13103 * can use this function to have #PsppSheetView handle these automatically
13104 * for you. @column should be set to the column in @tree_view's model
13105 * containing the tooltip texts, or -1 to disable this feature.
13107 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
13108 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
13110 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
13111 * so &, <, etc have to be escaped in the text.
13116 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
13119 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13121 if (column == tree_view->priv->tooltip_column)
13126 g_signal_handlers_disconnect_by_func (tree_view,
13127 pspp_sheet_view_set_tooltip_query_cb,
13129 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13133 if (tree_view->priv->tooltip_column == -1)
13135 g_signal_connect (tree_view, "query-tooltip",
13136 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13137 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13141 tree_view->priv->tooltip_column = column;
13142 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13146 * pspp_sheet_view_get_tooltip_column:
13147 * @tree_view: a #PsppSheetView
13149 * Returns the column of @tree_view's model which is being used for
13150 * displaying tooltips on @tree_view's rows.
13152 * Return value: the index of the tooltip column that is currently being
13153 * used, or -1 if this is disabled.
13158 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13160 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13162 return tree_view->priv->tooltip_column;
13166 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13167 GValue *return_accu,
13168 const GValue *handler_return,
13171 gboolean continue_emission;
13172 gboolean signal_handled;
13174 signal_handled = g_value_get_boolean (handler_return);
13175 g_value_set_boolean (return_accu, signal_handled);
13176 continue_emission = !signal_handled;
13178 return continue_emission;
13182 pspp_sheet_view_grid_lines_get_type (void)
13184 static GType etype = 0;
13185 if (G_UNLIKELY(etype == 0)) {
13186 static const GEnumValue values[] = {
13187 { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13188 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13189 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13190 { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13193 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13199 pspp_sheet_view_special_cells_get_type (void)
13201 static GType etype = 0;
13202 if (G_UNLIKELY(etype == 0)) {
13203 static const GEnumValue values[] = {
13204 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13205 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13206 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13209 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);