1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012, 2013 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Library General Public
22 * License as published by the Free Software Foundation; either
23 * version 2 of the License, or (at your option) any later version.
25 * This library is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Library General Public License for more details.
30 * You should have received a copy of the GNU Library General Public
31 * License along with this library; if not, write to the
32 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
33 * Boston, MA 02111-1307, USA.
38 #include "ui/gui/pspp-sheet-private.h"
42 #include <gdk/gdkkeysyms.h>
43 #include <gdk/gdkkeysyms-compat.h>
46 #include "ui/gui/psppire-marshal.h"
47 #include "ui/gui/pspp-sheet-selection.h"
49 #define P_(STRING) STRING
50 #define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
51 #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
53 /* Many keyboard shortcuts for Mac are the same as for X
54 * except they use Command key instead of Control (e.g. Cut,
55 * Copy, Paste). This symbol is for those simple cases. */
56 #ifndef GDK_WINDOWING_QUARTZ
57 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_CONTROL_MASK
59 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_META_MASK
62 #define PSPP_SHEET_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
63 #define PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC (PSPP_SHEET_VIEW_PRIORITY_VALIDATE + 2)
64 #define PSPP_SHEET_VIEW_TIME_MS_PER_IDLE 30
65 #define SCROLL_EDGE_SIZE 15
66 #define EXPANDER_EXTRA_PADDING 4
67 #define PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT 5000
69 /* The "background" areas of all rows/cells add up to cover the entire tree.
70 * The background includes all inter-row and inter-cell spacing.
71 * The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
72 * i.e. just the cells, no spacing.
75 #define BACKGROUND_HEIGHT(tree_view) (tree_view->priv->fixed_height)
76 #define CELL_HEIGHT(tree_view, separator) ((BACKGROUND_HEIGHT (tree_view)) - (separator))
78 /* Translate from bin_window coordinates to rbtree (tree coordinates) and
81 #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->priv->dy)
82 #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->priv->dy)
84 /* This is in bin_window coordinates */
85 #define BACKGROUND_FIRST_PIXEL(tree_view,node) (RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, pspp_sheet_view_node_find_offset (tree_view, (node))))
86 #define CELL_FIRST_PIXEL(tree_view,node,separator) (BACKGROUND_FIRST_PIXEL (tree_view,node) + separator/2)
88 #define ROW_HEIGHT(tree_view) \
89 ((tree_view->priv->fixed_height > 0) ? (tree_view->priv->fixed_height) : (tree_view)->priv->expander_size)
92 typedef struct _PsppSheetViewChild PsppSheetViewChild;
93 struct _PsppSheetViewChild
96 PsppSheetViewColumn *column;
101 typedef struct _TreeViewDragInfo TreeViewDragInfo;
102 struct _TreeViewDragInfo
104 GdkModifierType start_button_mask;
105 GtkTargetList *_unused_source_target_list;
106 GdkDragAction source_actions;
108 GtkTargetList *_unused_dest_target_list;
110 guint source_set : 1;
126 START_INTERACTIVE_SEARCH,
138 PROP_HEADERS_VISIBLE,
139 PROP_HEADERS_CLICKABLE,
144 PROP_HOVER_SELECTION,
146 PROP_ENABLE_GRID_LINES,
150 PROP_FIXED_HEIGHT_SET
154 static void pspp_sheet_view_finalize (GObject *object);
155 static void pspp_sheet_view_set_property (GObject *object,
159 static void pspp_sheet_view_get_property (GObject *object,
164 static void pspp_sheet_view_dispose (GObject *object);
166 /* gtkwidget signals */
167 static void pspp_sheet_view_realize (GtkWidget *widget);
168 static void pspp_sheet_view_unrealize (GtkWidget *widget);
169 static void pspp_sheet_view_map (GtkWidget *widget);
170 static void pspp_sheet_view_size_request (GtkWidget *widget,
171 GtkRequisition *requisition);
172 static void pspp_sheet_view_size_allocate (GtkWidget *widget,
173 GtkAllocation *allocation);
174 static gboolean pspp_sheet_view_draw (GtkWidget *widget,
176 static gboolean pspp_sheet_view_key_press (GtkWidget *widget,
178 static gboolean pspp_sheet_view_key_release (GtkWidget *widget,
180 static gboolean pspp_sheet_view_motion (GtkWidget *widget,
181 GdkEventMotion *event);
182 static gboolean pspp_sheet_view_enter_notify (GtkWidget *widget,
183 GdkEventCrossing *event);
184 static gboolean pspp_sheet_view_leave_notify (GtkWidget *widget,
185 GdkEventCrossing *event);
186 static gboolean pspp_sheet_view_button_press (GtkWidget *widget,
187 GdkEventButton *event);
188 static gboolean pspp_sheet_view_button_release (GtkWidget *widget,
189 GdkEventButton *event);
190 static gboolean pspp_sheet_view_grab_broken (GtkWidget *widget,
191 GdkEventGrabBroken *event);
193 static void pspp_sheet_view_set_focus_child (GtkContainer *container,
195 static gint pspp_sheet_view_focus_out (GtkWidget *widget,
196 GdkEventFocus *event);
197 static gint pspp_sheet_view_focus (GtkWidget *widget,
198 GtkDirectionType direction);
199 static void pspp_sheet_view_grab_focus (GtkWidget *widget);
200 static void pspp_sheet_view_style_updated (GtkWidget *widget);
201 static void pspp_sheet_view_grab_notify (GtkWidget *widget,
202 gboolean was_grabbed);
203 static void pspp_sheet_view_state_changed (GtkWidget *widget,
204 GtkStateType previous_state);
206 /* container signals */
207 static void pspp_sheet_view_remove (GtkContainer *container,
209 static void pspp_sheet_view_forall (GtkContainer *container,
210 gboolean include_internals,
211 GtkCallback callback,
212 gpointer callback_data);
214 /* Source side drag signals */
215 static void pspp_sheet_view_drag_begin (GtkWidget *widget,
216 GdkDragContext *context);
217 static void pspp_sheet_view_drag_end (GtkWidget *widget,
218 GdkDragContext *context);
219 static void pspp_sheet_view_drag_data_get (GtkWidget *widget,
220 GdkDragContext *context,
221 GtkSelectionData *selection_data,
224 static void pspp_sheet_view_drag_data_delete (GtkWidget *widget,
225 GdkDragContext *context);
227 /* Target side drag signals */
228 static void pspp_sheet_view_drag_leave (GtkWidget *widget,
229 GdkDragContext *context,
231 static gboolean pspp_sheet_view_drag_motion (GtkWidget *widget,
232 GdkDragContext *context,
236 static gboolean pspp_sheet_view_drag_drop (GtkWidget *widget,
237 GdkDragContext *context,
241 static void pspp_sheet_view_drag_data_received (GtkWidget *widget,
242 GdkDragContext *context,
245 GtkSelectionData *selection_data,
249 /* tree_model signals */
250 static void pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
252 GtkAdjustment *vadj);
253 static gboolean pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
254 GtkMovementStep step,
256 static gboolean pspp_sheet_view_real_select_all (PsppSheetView *tree_view);
257 static gboolean pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view);
258 static gboolean pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
259 gboolean start_editing,
260 PsppSheetSelectMode mode);
261 static gboolean pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view);
262 static void pspp_sheet_view_row_changed (GtkTreeModel *model,
266 static void pspp_sheet_view_row_inserted (GtkTreeModel *model,
270 static void pspp_sheet_view_row_deleted (GtkTreeModel *model,
273 static void pspp_sheet_view_rows_reordered (GtkTreeModel *model,
279 /* Incremental reflow */
280 static gint validate_row (PsppSheetView *tree_view,
284 static void validate_visible_area (PsppSheetView *tree_view);
285 static gboolean validate_rows_handler (PsppSheetView *tree_view);
286 static gboolean presize_handler_callback (gpointer data);
287 static void install_presize_handler (PsppSheetView *tree_view);
288 static void install_scroll_sync_handler (PsppSheetView *tree_view);
289 static void pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
292 static void pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view);
293 static void pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view);
294 static void invalidate_empty_focus (PsppSheetView *tree_view);
296 /* Internal functions */
297 static GtkAdjustment *pspp_sheet_view_do_get_hadjustment (PsppSheetView *);
298 static GtkAdjustment *pspp_sheet_view_do_get_vadjustment (PsppSheetView *);
299 static void pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
300 GtkAdjustment *adjustment);
301 static void pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
302 GtkAdjustment *adjustment);
303 static void pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
306 gboolean add_shifted_binding,
307 GtkMovementStep step,
309 static void pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
311 const GdkRectangle *clip_rect);
312 static gint pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
315 static void pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
316 PsppSheetView *tree_view);
317 static void pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
319 static void pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
320 PsppSheetViewColumn *column,
321 gboolean focus_to_cell);
322 static gboolean pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
323 GdkEventMotion *event);
324 static void pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view);
325 static gboolean pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
327 PsppSheetSelectMode mode);
328 static void pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
330 PsppSheetSelectMode mode);
331 static void pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
333 PsppSheetSelectMode mode);
334 static void pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
336 PsppSheetSelectMode mode);
337 static void pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
339 static void pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
341 PsppSheetSelectMode mode);
342 static void pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
344 gboolean clear_and_select,
346 PsppSheetSelectMode mode);
347 static gboolean pspp_sheet_view_has_special_cell (PsppSheetView *tree_view);
348 static void pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view);
349 static void update_prelight (PsppSheetView *tree_view,
352 static void initialize_fixed_height_mode (PsppSheetView *tree_view);
354 /* interactive search */
355 static void pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view);
356 static void pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
357 PsppSheetView *tree_view);
358 static void pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
359 GtkWidget *search_dialog,
361 static void pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
365 static void pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
366 PsppSheetView *tree_view);
368 static void pspp_sheet_view_search_activate (GtkEntry *entry,
369 PsppSheetView *tree_view);
370 static gboolean pspp_sheet_view_real_search_enable_popdown(gpointer data);
371 static void pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
373 static gboolean pspp_sheet_view_search_delete_event (GtkWidget *widget,
375 PsppSheetView *tree_view);
376 static gboolean pspp_sheet_view_search_button_press_event (GtkWidget *widget,
377 GdkEventButton *event,
378 PsppSheetView *tree_view);
379 static gboolean pspp_sheet_view_search_scroll_event (GtkWidget *entry,
380 GdkEventScroll *event,
381 PsppSheetView *tree_view);
382 static gboolean pspp_sheet_view_search_key_press_event (GtkWidget *entry,
384 PsppSheetView *tree_view);
385 static gboolean pspp_sheet_view_search_move (GtkWidget *window,
386 PsppSheetView *tree_view,
388 static gboolean pspp_sheet_view_search_equal_func (GtkTreeModel *model,
392 gpointer search_data);
393 static gboolean pspp_sheet_view_search_iter (GtkTreeModel *model,
394 PsppSheetSelection *selection,
399 static void pspp_sheet_view_search_init (GtkWidget *entry,
400 PsppSheetView *tree_view);
401 static void pspp_sheet_view_put (PsppSheetView *tree_view,
402 GtkWidget *child_widget,
404 PsppSheetViewColumn *column);
405 static gboolean pspp_sheet_view_start_editing (PsppSheetView *tree_view,
406 GtkTreePath *cursor_path);
407 static gboolean pspp_sheet_view_editable_button_press_event (GtkWidget *,
410 static void pspp_sheet_view_editable_clicked (GtkButton *, PsppSheetView *);
411 static void pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
412 PsppSheetViewColumn *column,
414 GtkCellEditable *cell_editable,
415 GdkRectangle *cell_area,
418 static gboolean pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
419 gboolean keybinding);
420 static gboolean pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view);
421 static PsppSheetViewColumn *pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
422 PsppSheetViewColumn *column,
425 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
426 PsppSheetViewColumn *column,
427 const GdkRectangle *background_area,
428 gboolean subtract_focus_rect,
429 GdkRectangle *cell_area);
430 static gint pspp_sheet_view_find_offset (PsppSheetView *tree_view,
435 static void pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
439 static void pspp_sheet_view_buildable_init (GtkBuildableIface *iface);
442 static gboolean scroll_row_timeout (gpointer data);
443 static void add_scroll_timeout (PsppSheetView *tree_view);
444 static void remove_scroll_timeout (PsppSheetView *tree_view);
446 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
448 static GtkBindingSet *edit_bindings;
455 G_DEFINE_TYPE_WITH_CODE (PsppSheetView, pspp_sheet_view, GTK_TYPE_CONTAINER,
456 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
457 pspp_sheet_view_buildable_init)
458 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
461 pspp_sheet_view_get_preferred_width (GtkWidget *widget,
465 GtkRequisition requisition;
467 pspp_sheet_view_size_request (widget, &requisition);
469 *minimal_width = *natural_width = requisition.width;
473 pspp_sheet_view_get_preferred_height (GtkWidget *widget,
474 gint *minimal_height,
475 gint *natural_height)
477 GtkRequisition requisition;
479 pspp_sheet_view_size_request (widget, &requisition);
481 *minimal_height = *natural_height = requisition.height;
485 pspp_sheet_view_class_init (PsppSheetViewClass *class)
487 GObjectClass *o_class;
488 GtkWidgetClass *widget_class;
489 GtkContainerClass *container_class;
490 GtkBindingSet *binding_set[2];
493 binding_set[0] = gtk_binding_set_by_class (class);
495 binding_set[1] = gtk_binding_set_new ("PsppSheetViewEditing");
496 edit_bindings = binding_set[1];
498 o_class = (GObjectClass *) class;
499 widget_class = (GtkWidgetClass *) class;
500 container_class = (GtkContainerClass *) class;
502 /* GObject signals */
503 o_class->set_property = pspp_sheet_view_set_property;
504 o_class->get_property = pspp_sheet_view_get_property;
505 o_class->finalize = pspp_sheet_view_finalize;
506 o_class->dispose = pspp_sheet_view_dispose;
508 /* GtkWidget signals */
509 widget_class->map = pspp_sheet_view_map;
510 widget_class->realize = pspp_sheet_view_realize;
511 widget_class->unrealize = pspp_sheet_view_unrealize;
512 widget_class->get_preferred_width = pspp_sheet_view_get_preferred_width;
513 widget_class->get_preferred_height = pspp_sheet_view_get_preferred_height;
514 widget_class->size_allocate = pspp_sheet_view_size_allocate;
515 widget_class->button_press_event = pspp_sheet_view_button_press;
516 widget_class->button_release_event = pspp_sheet_view_button_release;
517 widget_class->grab_broken_event = pspp_sheet_view_grab_broken;
518 /*widget_class->configure_event = pspp_sheet_view_configure;*/
519 widget_class->motion_notify_event = pspp_sheet_view_motion;
520 widget_class->draw = pspp_sheet_view_draw;
521 widget_class->key_press_event = pspp_sheet_view_key_press;
522 widget_class->key_release_event = pspp_sheet_view_key_release;
523 widget_class->enter_notify_event = pspp_sheet_view_enter_notify;
524 widget_class->leave_notify_event = pspp_sheet_view_leave_notify;
525 widget_class->focus_out_event = pspp_sheet_view_focus_out;
526 widget_class->drag_begin = pspp_sheet_view_drag_begin;
527 widget_class->drag_end = pspp_sheet_view_drag_end;
528 widget_class->drag_data_get = pspp_sheet_view_drag_data_get;
529 widget_class->drag_data_delete = pspp_sheet_view_drag_data_delete;
530 widget_class->drag_leave = pspp_sheet_view_drag_leave;
531 widget_class->drag_motion = pspp_sheet_view_drag_motion;
532 widget_class->drag_drop = pspp_sheet_view_drag_drop;
533 widget_class->drag_data_received = pspp_sheet_view_drag_data_received;
534 widget_class->focus = pspp_sheet_view_focus;
535 widget_class->grab_focus = pspp_sheet_view_grab_focus;
536 widget_class->style_updated = pspp_sheet_view_style_updated;
537 widget_class->grab_notify = pspp_sheet_view_grab_notify;
538 widget_class->state_changed = pspp_sheet_view_state_changed;
540 /* GtkContainer signals */
541 container_class->remove = pspp_sheet_view_remove;
542 container_class->forall = pspp_sheet_view_forall;
543 container_class->set_focus_child = pspp_sheet_view_set_focus_child;
545 class->set_scroll_adjustments = pspp_sheet_view_set_adjustments;
546 class->move_cursor = pspp_sheet_view_real_move_cursor;
547 class->select_all = pspp_sheet_view_real_select_all;
548 class->unselect_all = pspp_sheet_view_real_unselect_all;
549 class->select_cursor_row = pspp_sheet_view_real_select_cursor_row;
550 class->toggle_cursor_row = pspp_sheet_view_real_toggle_cursor_row;
551 class->start_interactive_search = pspp_sheet_view_start_interactive_search;
555 g_object_class_install_property (o_class,
557 g_param_spec_object ("model",
558 P_("TreeView Model"),
559 P_("The model for the tree view"),
561 GTK_PARAM_READWRITE));
563 g_object_class_override_property (o_class, PROP_HADJUSTMENT, "hadjustment");
564 g_object_class_override_property (o_class, PROP_VADJUSTMENT, "vadjustment");
565 g_object_class_override_property (o_class, PROP_HSCROLL_POLICY, "hscroll-policy");
566 g_object_class_override_property (o_class, PROP_VSCROLL_POLICY, "vscroll-policy");
568 g_object_class_install_property (o_class,
569 PROP_HEADERS_VISIBLE,
570 g_param_spec_boolean ("headers-visible",
571 P_("Headers Visible"),
572 P_("Show the column header buttons"),
574 GTK_PARAM_READWRITE));
576 g_object_class_install_property (o_class,
577 PROP_HEADERS_CLICKABLE,
578 g_param_spec_boolean ("headers-clickable",
579 P_("Headers Clickable"),
580 P_("Column headers respond to click events"),
582 GTK_PARAM_READWRITE));
584 g_object_class_install_property (o_class,
586 g_param_spec_boolean ("reorderable",
588 P_("View is reorderable"),
590 GTK_PARAM_READWRITE));
592 g_object_class_install_property (o_class,
594 g_param_spec_boolean ("rules-hint",
596 P_("Set a hint to the theme engine to draw rows in alternating colors"),
598 GTK_PARAM_READWRITE));
600 g_object_class_install_property (o_class,
602 g_param_spec_boolean ("enable-search",
604 P_("View allows user to search through columns interactively"),
606 GTK_PARAM_READWRITE));
608 g_object_class_install_property (o_class,
610 g_param_spec_int ("search-column",
612 P_("Model column to search through during interactive search"),
616 GTK_PARAM_READWRITE));
619 * PsppSheetView:hover-selection:
621 * Enables of disables the hover selection mode of @tree_view.
622 * Hover selection makes the selected row follow the pointer.
623 * Currently, this works only for the selection modes
624 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
626 * This mode is primarily intended for treeviews in popups, e.g.
627 * in #GtkComboBox or #GtkEntryCompletion.
631 g_object_class_install_property (o_class,
632 PROP_HOVER_SELECTION,
633 g_param_spec_boolean ("hover-selection",
634 P_("Hover Selection"),
635 P_("Whether the selection should follow the pointer"),
637 GTK_PARAM_READWRITE));
639 g_object_class_install_property (o_class,
641 g_param_spec_boolean ("rubber-banding",
642 P_("Rubber Banding"),
643 P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
645 GTK_PARAM_READWRITE));
647 g_object_class_install_property (o_class,
648 PROP_ENABLE_GRID_LINES,
649 g_param_spec_enum ("enable-grid-lines",
650 P_("Enable Grid Lines"),
651 P_("Whether grid lines should be drawn in the tree view"),
652 PSPP_TYPE_SHEET_VIEW_GRID_LINES,
653 PSPP_SHEET_VIEW_GRID_LINES_NONE,
654 GTK_PARAM_READWRITE));
656 g_object_class_install_property (o_class,
658 g_param_spec_int ("tooltip-column",
659 P_("Tooltip Column"),
660 P_("The column in the model containing the tooltip texts for the rows"),
664 GTK_PARAM_READWRITE));
666 g_object_class_install_property (o_class,
668 g_param_spec_enum ("special-cells",
670 P_("Whether rows have special cells."),
671 PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS,
672 PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT,
673 GTK_PARAM_READWRITE));
675 g_object_class_install_property (o_class,
677 g_param_spec_int ("fixed-height",
679 P_("Height of a single row. Normally the height of a row is determined automatically. Writing this property sets fixed-height-set to true, preventing this property's value from changing."),
683 GTK_PARAM_READWRITE));
685 g_object_class_install_property (o_class,
686 PROP_FIXED_HEIGHT_SET,
687 g_param_spec_boolean ("fixed-height-set",
688 P_("Fixed Height Set"),
689 P_("Whether fixed-height was set externally."),
691 GTK_PARAM_READWRITE));
693 /* Style properties */
694 #define _TREE_VIEW_EXPANDER_SIZE 12
695 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
696 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
698 gtk_widget_class_install_style_property (widget_class,
699 g_param_spec_int ("expander-size",
701 P_("Size of the expander arrow"),
704 _TREE_VIEW_EXPANDER_SIZE,
705 GTK_PARAM_READABLE));
707 gtk_widget_class_install_style_property (widget_class,
708 g_param_spec_int ("vertical-separator",
709 P_("Vertical Separator Width"),
710 P_("Vertical space between cells. Must be an even number"),
713 _TREE_VIEW_VERTICAL_SEPARATOR,
714 GTK_PARAM_READABLE));
716 gtk_widget_class_install_style_property (widget_class,
717 g_param_spec_int ("horizontal-separator",
718 P_("Horizontal Separator Width"),
719 P_("Horizontal space between cells. Must be an even number"),
722 _TREE_VIEW_HORIZONTAL_SEPARATOR,
723 GTK_PARAM_READABLE));
725 gtk_widget_class_install_style_property (widget_class,
726 g_param_spec_boolean ("allow-rules",
728 P_("Allow drawing of alternating color rows"),
730 GTK_PARAM_READABLE));
732 gtk_widget_class_install_style_property (widget_class,
733 g_param_spec_boxed ("even-row-color",
734 P_("Even Row Color"),
735 P_("Color to use for even rows"),
737 GTK_PARAM_READABLE));
739 gtk_widget_class_install_style_property (widget_class,
740 g_param_spec_boxed ("odd-row-color",
742 P_("Color to use for odd rows"),
744 GTK_PARAM_READABLE));
746 gtk_widget_class_install_style_property (widget_class,
747 g_param_spec_boolean ("row-ending-details",
748 P_("Row Ending details"),
749 P_("Enable extended row background theming"),
751 GTK_PARAM_READABLE));
753 gtk_widget_class_install_style_property (widget_class,
754 g_param_spec_int ("grid-line-width",
755 P_("Grid line width"),
756 P_("Width, in pixels, of the tree view grid lines"),
758 GTK_PARAM_READABLE));
760 gtk_widget_class_install_style_property (widget_class,
761 g_param_spec_int ("tree-line-width",
762 P_("Tree line width"),
763 P_("Width, in pixels, of the tree view lines"),
765 GTK_PARAM_READABLE));
767 gtk_widget_class_install_style_property (widget_class,
768 g_param_spec_string ("tree-line-pattern",
769 P_("Tree line pattern"),
770 P_("Dash pattern used to draw the tree view lines"),
772 GTK_PARAM_READABLE));
777 * PsppSheetView::set-scroll-adjustments
778 * @horizontal: the horizontal #GtkAdjustment
779 * @vertical: the vertical #GtkAdjustment
781 * Set the scroll adjustments for the tree view. Usually scrolled containers
782 * like #GtkScrolledWindow will emit this signal to connect two instances
783 * of #GtkScrollbar to the scroll directions of the #PsppSheetView.
785 widget_class->set_scroll_adjustments_signal =
786 g_signal_new ("set-scroll-adjustments",
787 G_TYPE_FROM_CLASS (o_class),
788 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
789 G_STRUCT_OFFSET (PsppSheetViewClass, set_scroll_adjustments),
791 psppire_marshal_VOID__OBJECT_OBJECT,
794 GTK_TYPE_ADJUSTMENT);
798 * PsppSheetView::row-activated:
799 * @tree_view: the object on which the signal is emitted
800 * @path: the #GtkTreePath for the activated row
801 * @column: the #PsppSheetViewColumn in which the activation occurred
803 * The "row-activated" signal is emitted when the method
804 * pspp_sheet_view_row_activated() is called or the user double clicks
805 * a treeview row. It is also emitted when a non-editable row is
806 * selected and one of the keys: Space, Shift+Space, Return or
809 * For selection handling refer to the <link linkend="TreeWidget">tree
810 * widget conceptual overview</link> as well as #PsppSheetSelection.
812 tree_view_signals[ROW_ACTIVATED] =
813 g_signal_new ("row-activated",
814 G_TYPE_FROM_CLASS (o_class),
815 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
816 G_STRUCT_OFFSET (PsppSheetViewClass, row_activated),
818 psppire_marshal_VOID__BOXED_OBJECT,
821 PSPP_TYPE_SHEET_VIEW_COLUMN);
824 * PsppSheetView::columns-changed:
825 * @tree_view: the object on which the signal is emitted
827 * The number of columns of the treeview has changed.
829 tree_view_signals[COLUMNS_CHANGED] =
830 g_signal_new ("columns-changed",
831 G_TYPE_FROM_CLASS (o_class),
833 G_STRUCT_OFFSET (PsppSheetViewClass, columns_changed),
835 g_cclosure_marshal_VOID__VOID,
839 * PsppSheetView::cursor-changed:
840 * @tree_view: the object on which the signal is emitted
842 * The position of the cursor (focused cell) has changed.
844 tree_view_signals[CURSOR_CHANGED] =
845 g_signal_new ("cursor-changed",
846 G_TYPE_FROM_CLASS (o_class),
848 G_STRUCT_OFFSET (PsppSheetViewClass, cursor_changed),
850 g_cclosure_marshal_VOID__VOID,
853 tree_view_signals[MOVE_CURSOR] =
854 g_signal_new ("move-cursor",
855 G_TYPE_FROM_CLASS (o_class),
856 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
857 G_STRUCT_OFFSET (PsppSheetViewClass, move_cursor),
859 psppire_marshal_BOOLEAN__ENUM_INT,
861 GTK_TYPE_MOVEMENT_STEP,
864 tree_view_signals[SELECT_ALL] =
865 g_signal_new ("select-all",
866 G_TYPE_FROM_CLASS (o_class),
867 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
868 G_STRUCT_OFFSET (PsppSheetViewClass, select_all),
870 psppire_marshal_BOOLEAN__VOID,
873 tree_view_signals[UNSELECT_ALL] =
874 g_signal_new ("unselect-all",
875 G_TYPE_FROM_CLASS (o_class),
876 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
877 G_STRUCT_OFFSET (PsppSheetViewClass, unselect_all),
879 psppire_marshal_BOOLEAN__VOID,
882 tree_view_signals[SELECT_CURSOR_ROW] =
883 g_signal_new ("select-cursor-row",
884 G_TYPE_FROM_CLASS (o_class),
885 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
886 G_STRUCT_OFFSET (PsppSheetViewClass, select_cursor_row),
888 psppire_marshal_BOOLEAN__BOOLEAN,
890 G_TYPE_BOOLEAN, G_TYPE_INT);
892 tree_view_signals[TOGGLE_CURSOR_ROW] =
893 g_signal_new ("toggle-cursor-row",
894 G_TYPE_FROM_CLASS (o_class),
895 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
896 G_STRUCT_OFFSET (PsppSheetViewClass, toggle_cursor_row),
898 psppire_marshal_BOOLEAN__VOID,
901 tree_view_signals[START_INTERACTIVE_SEARCH] =
902 g_signal_new ("start-interactive-search",
903 G_TYPE_FROM_CLASS (o_class),
904 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
905 G_STRUCT_OFFSET (PsppSheetViewClass, start_interactive_search),
907 psppire_marshal_BOOLEAN__VOID,
911 for (i = 0; i < 2; i++)
913 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Up, 0, TRUE,
914 GTK_MOVEMENT_DISPLAY_LINES, -1);
915 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Up, 0, TRUE,
916 GTK_MOVEMENT_DISPLAY_LINES, -1);
918 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Down, 0, TRUE,
919 GTK_MOVEMENT_DISPLAY_LINES, 1);
920 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Down, 0, TRUE,
921 GTK_MOVEMENT_DISPLAY_LINES, 1);
923 pspp_sheet_view_add_move_binding (binding_set[i], GDK_p, GDK_CONTROL_MASK, FALSE,
924 GTK_MOVEMENT_DISPLAY_LINES, -1);
926 pspp_sheet_view_add_move_binding (binding_set[i], GDK_n, GDK_CONTROL_MASK, FALSE,
927 GTK_MOVEMENT_DISPLAY_LINES, 1);
929 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Home, 0, TRUE,
930 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
931 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Home, 0, TRUE,
932 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
934 pspp_sheet_view_add_move_binding (binding_set[i], GDK_End, 0, TRUE,
935 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
936 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_End, 0, TRUE,
937 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
939 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Up, 0, TRUE,
940 GTK_MOVEMENT_PAGES, -1);
941 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Up, 0, TRUE,
942 GTK_MOVEMENT_PAGES, -1);
944 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Down, 0, TRUE,
945 GTK_MOVEMENT_PAGES, 1);
946 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Down, 0, TRUE,
947 GTK_MOVEMENT_PAGES, 1);
950 gtk_binding_entry_add_signal (binding_set[i], GDK_Up, GDK_CONTROL_MASK, "move-cursor", 2,
951 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
954 gtk_binding_entry_add_signal (binding_set[i], GDK_Down, GDK_CONTROL_MASK, "move-cursor", 2,
955 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
958 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, 0, "move-cursor", 2,
959 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
962 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, 0, "move-cursor", 2,
963 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
966 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, 0, "move-cursor", 2,
967 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
970 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, GDK_SHIFT_MASK, "move-cursor", 2,
971 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
974 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, 0, "move-cursor", 2,
975 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
978 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, 0, "move-cursor", 2,
979 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
982 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, GDK_CONTROL_MASK,
984 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
987 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, GDK_CONTROL_MASK,
989 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
992 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, GDK_CONTROL_MASK,
994 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
997 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, GDK_CONTROL_MASK,
999 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1002 gtk_binding_entry_add_signal (binding_set[i], GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
1004 gtk_binding_entry_add_signal (binding_set[i], GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
1007 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1008 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1010 gtk_binding_entry_add_signal (binding_set[0], GDK_a, GDK_CONTROL_MASK, "select-all", 0);
1011 gtk_binding_entry_add_signal (binding_set[0], GDK_slash, GDK_CONTROL_MASK, "select-all", 0);
1013 gtk_binding_entry_add_signal (binding_set[0], GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
1014 gtk_binding_entry_add_signal (binding_set[0], GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0);
1016 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1017 G_TYPE_BOOLEAN, TRUE,
1018 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1019 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1020 G_TYPE_BOOLEAN, TRUE,
1021 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1023 gtk_binding_entry_add_signal (binding_set[0], GDK_space, 0, "select-cursor-row", 1,
1024 G_TYPE_BOOLEAN, TRUE,
1026 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, 0, "select-cursor-row", 1,
1027 G_TYPE_BOOLEAN, TRUE,
1029 gtk_binding_entry_add_signal (binding_set[0], GDK_Return, 0, "select-cursor-row", 1,
1030 G_TYPE_BOOLEAN, TRUE,
1032 gtk_binding_entry_add_signal (binding_set[0], GDK_ISO_Enter, 0, "select-cursor-row", 1,
1033 G_TYPE_BOOLEAN, TRUE,
1035 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Enter, 0, "select-cursor-row", 1,
1036 G_TYPE_BOOLEAN, TRUE,
1039 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, 0, "select-cursor-parent", 0);
1040 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0);
1042 g_type_class_add_private (o_class, sizeof (PsppSheetViewPrivate));
1046 pspp_sheet_view_buildable_init (GtkBuildableIface *iface)
1048 iface->add_child = pspp_sheet_view_buildable_add_child;
1052 pspp_sheet_view_init (PsppSheetView *tree_view)
1054 tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, PSPP_TYPE_SHEET_VIEW, PsppSheetViewPrivate);
1056 gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
1057 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE);
1059 tree_view->priv->flags = PSPP_SHEET_VIEW_DRAW_KEYFOCUS
1060 | PSPP_SHEET_VIEW_HEADERS_VISIBLE;
1062 /* We need some padding */
1063 tree_view->priv->selected = range_tower_create ();
1064 tree_view->priv->dy = 0;
1065 tree_view->priv->cursor_offset = 0;
1066 tree_view->priv->n_columns = 0;
1067 tree_view->priv->header_height = 1;
1068 tree_view->priv->x_drag = 0;
1069 tree_view->priv->drag_pos = -1;
1070 tree_view->priv->header_has_focus = FALSE;
1071 tree_view->priv->pressed_button = -1;
1072 tree_view->priv->press_start_x = -1;
1073 tree_view->priv->press_start_y = -1;
1074 tree_view->priv->reorderable = FALSE;
1075 tree_view->priv->presize_handler_timer = 0;
1076 tree_view->priv->scroll_sync_timer = 0;
1077 tree_view->priv->fixed_height = -1;
1078 tree_view->priv->fixed_height_set = FALSE;
1079 pspp_sheet_view_set_adjustments (tree_view, NULL, NULL);
1080 tree_view->priv->selection = _pspp_sheet_selection_new_with_tree_view (tree_view);
1081 tree_view->priv->enable_search = TRUE;
1082 tree_view->priv->search_column = -1;
1083 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
1084 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
1085 tree_view->priv->search_custom_entry_set = FALSE;
1086 tree_view->priv->typeselect_flush_timeout = 0;
1087 tree_view->priv->init_hadjust_value = TRUE;
1088 tree_view->priv->width = 0;
1090 tree_view->priv->hover_selection = FALSE;
1092 tree_view->priv->rubber_banding_enable = FALSE;
1094 tree_view->priv->grid_lines = PSPP_SHEET_VIEW_GRID_LINES_NONE;
1096 tree_view->priv->tooltip_column = -1;
1098 tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT;
1100 tree_view->priv->post_validation_flag = FALSE;
1102 tree_view->priv->last_button_x = -1;
1103 tree_view->priv->last_button_y = -1;
1105 tree_view->priv->event_last_x = -10000;
1106 tree_view->priv->event_last_y = -10000;
1108 tree_view->priv->prelight_node = -1;
1109 tree_view->priv->rubber_band_start_node = -1;
1110 tree_view->priv->rubber_band_end_node = -1;
1112 tree_view->priv->anchor_column = NULL;
1114 tree_view->priv->button_style = NULL;
1116 tree_view->dispose_has_run = FALSE;
1118 pspp_sheet_view_do_set_vadjustment (tree_view, NULL);
1119 pspp_sheet_view_do_set_hadjustment (tree_view, NULL);
1120 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (tree_view)),
1121 GTK_STYLE_CLASS_VIEW);
1130 pspp_sheet_view_set_property (GObject *object,
1132 const GValue *value,
1135 PsppSheetView *tree_view;
1137 tree_view = PSPP_SHEET_VIEW (object);
1142 pspp_sheet_view_set_model (tree_view, g_value_get_object (value));
1144 case PROP_HADJUSTMENT:
1145 pspp_sheet_view_do_set_hadjustment (tree_view, g_value_get_object (value));
1147 case PROP_VADJUSTMENT:
1148 pspp_sheet_view_do_set_vadjustment (tree_view, g_value_get_object (value));
1150 case PROP_HSCROLL_POLICY:
1151 tree_view->priv->hscroll_policy = g_value_get_enum (value);
1152 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1154 case PROP_VSCROLL_POLICY:
1155 tree_view->priv->vscroll_policy = g_value_get_enum (value);
1156 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1158 case PROP_HEADERS_VISIBLE:
1159 pspp_sheet_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1161 case PROP_HEADERS_CLICKABLE:
1162 pspp_sheet_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1164 case PROP_REORDERABLE:
1165 pspp_sheet_view_set_reorderable (tree_view, g_value_get_boolean (value));
1167 case PROP_RULES_HINT:
1168 pspp_sheet_view_set_rules_hint (tree_view, g_value_get_boolean (value));
1170 case PROP_ENABLE_SEARCH:
1171 pspp_sheet_view_set_enable_search (tree_view, g_value_get_boolean (value));
1173 case PROP_SEARCH_COLUMN:
1174 pspp_sheet_view_set_search_column (tree_view, g_value_get_int (value));
1176 case PROP_HOVER_SELECTION:
1177 tree_view->priv->hover_selection = g_value_get_boolean (value);
1179 case PROP_RUBBER_BANDING:
1180 tree_view->priv->rubber_banding_enable = g_value_get_boolean (value);
1182 case PROP_ENABLE_GRID_LINES:
1183 pspp_sheet_view_set_grid_lines (tree_view, g_value_get_enum (value));
1185 case PROP_TOOLTIP_COLUMN:
1186 pspp_sheet_view_set_tooltip_column (tree_view, g_value_get_int (value));
1188 case PROP_SPECIAL_CELLS:
1189 pspp_sheet_view_set_special_cells (tree_view, g_value_get_enum (value));
1191 case PROP_FIXED_HEIGHT:
1192 pspp_sheet_view_set_fixed_height (tree_view, g_value_get_int (value));
1194 case PROP_FIXED_HEIGHT_SET:
1195 if (g_value_get_boolean (value))
1197 if (!tree_view->priv->fixed_height_set
1198 && tree_view->priv->fixed_height >= 0)
1200 tree_view->priv->fixed_height_set = true;
1201 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1206 if (tree_view->priv->fixed_height_set)
1208 tree_view->priv->fixed_height_set = false;
1209 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1210 install_presize_handler (tree_view);
1215 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1221 pspp_sheet_view_get_property (GObject *object,
1226 PsppSheetView *tree_view;
1228 tree_view = PSPP_SHEET_VIEW (object);
1233 g_value_set_object (value, tree_view->priv->model);
1235 case PROP_HADJUSTMENT:
1236 g_value_set_object (value, tree_view->priv->hadjustment);
1238 case PROP_VADJUSTMENT:
1239 g_value_set_object (value, tree_view->priv->vadjustment);
1241 case PROP_HSCROLL_POLICY:
1242 g_value_set_enum (value, tree_view->priv->hscroll_policy);
1244 case PROP_VSCROLL_POLICY:
1245 g_value_set_enum (value, tree_view->priv->vscroll_policy);
1247 case PROP_HEADERS_VISIBLE:
1248 g_value_set_boolean (value, pspp_sheet_view_get_headers_visible (tree_view));
1250 case PROP_HEADERS_CLICKABLE:
1251 g_value_set_boolean (value, pspp_sheet_view_get_headers_clickable (tree_view));
1253 case PROP_REORDERABLE:
1254 g_value_set_boolean (value, tree_view->priv->reorderable);
1256 case PROP_RULES_HINT:
1257 g_value_set_boolean (value, tree_view->priv->has_rules);
1259 case PROP_ENABLE_SEARCH:
1260 g_value_set_boolean (value, tree_view->priv->enable_search);
1262 case PROP_SEARCH_COLUMN:
1263 g_value_set_int (value, tree_view->priv->search_column);
1265 case PROP_HOVER_SELECTION:
1266 g_value_set_boolean (value, tree_view->priv->hover_selection);
1268 case PROP_RUBBER_BANDING:
1269 g_value_set_boolean (value, tree_view->priv->rubber_banding_enable);
1271 case PROP_ENABLE_GRID_LINES:
1272 g_value_set_enum (value, tree_view->priv->grid_lines);
1274 case PROP_TOOLTIP_COLUMN:
1275 g_value_set_int (value, tree_view->priv->tooltip_column);
1277 case PROP_SPECIAL_CELLS:
1278 g_value_set_enum (value, tree_view->priv->special_cells);
1280 case PROP_FIXED_HEIGHT:
1281 g_value_set_int (value, pspp_sheet_view_get_fixed_height (tree_view));
1283 case PROP_FIXED_HEIGHT_SET:
1284 g_value_set_boolean (value, tree_view->priv->fixed_height_set);
1287 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1293 pspp_sheet_view_dispose (GObject *object)
1295 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1297 if (tree_view->dispose_has_run)
1300 tree_view->dispose_has_run = TRUE;
1302 if (tree_view->priv->selection != NULL)
1304 _pspp_sheet_selection_set_tree_view (tree_view->priv->selection, NULL);
1305 g_object_unref (tree_view->priv->selection);
1306 tree_view->priv->selection = NULL;
1309 if (tree_view->priv->hadjustment)
1311 g_object_unref (tree_view->priv->hadjustment);
1312 tree_view->priv->hadjustment = NULL;
1314 if (tree_view->priv->vadjustment)
1316 g_object_unref (tree_view->priv->vadjustment);
1317 tree_view->priv->vadjustment = NULL;
1320 if (tree_view->priv->button_style)
1322 g_object_unref (tree_view->priv->button_style);
1323 tree_view->priv->button_style = NULL;
1327 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->dispose (object);
1333 pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
1334 GtkBuilder *builder,
1338 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), PSPP_SHEET_VIEW_COLUMN (child));
1342 pspp_sheet_view_finalize (GObject *object)
1344 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1346 pspp_sheet_view_stop_editing (tree_view, TRUE);
1348 if (tree_view->priv->selected != NULL)
1350 range_tower_destroy (tree_view->priv->selected);
1351 tree_view->priv->selected = NULL;
1355 tree_view->priv->prelight_node = -1;
1358 if (tree_view->priv->scroll_to_path != NULL)
1360 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
1361 tree_view->priv->scroll_to_path = NULL;
1364 if (tree_view->priv->drag_dest_row != NULL)
1366 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
1367 tree_view->priv->drag_dest_row = NULL;
1370 if (tree_view->priv->top_row != NULL)
1372 gtk_tree_row_reference_free (tree_view->priv->top_row);
1373 tree_view->priv->top_row = NULL;
1376 if (tree_view->priv->column_drop_func_data &&
1377 tree_view->priv->column_drop_func_data_destroy)
1379 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
1380 tree_view->priv->column_drop_func_data = NULL;
1383 if (tree_view->priv->destroy_count_destroy &&
1384 tree_view->priv->destroy_count_data)
1386 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
1387 tree_view->priv->destroy_count_data = NULL;
1390 gtk_tree_row_reference_free (tree_view->priv->cursor);
1391 tree_view->priv->cursor = NULL;
1393 gtk_tree_row_reference_free (tree_view->priv->anchor);
1394 tree_view->priv->anchor = NULL;
1396 /* destroy interactive search dialog */
1397 if (tree_view->priv->search_window)
1399 gtk_widget_destroy (tree_view->priv->search_window);
1400 tree_view->priv->search_window = NULL;
1401 tree_view->priv->search_entry = NULL;
1402 if (tree_view->priv->typeselect_flush_timeout)
1404 g_source_remove (tree_view->priv->typeselect_flush_timeout);
1405 tree_view->priv->typeselect_flush_timeout = 0;
1409 if (tree_view->priv->search_destroy && tree_view->priv->search_user_data)
1411 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
1412 tree_view->priv->search_user_data = NULL;
1415 if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data)
1417 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
1418 tree_view->priv->search_position_user_data = NULL;
1421 pspp_sheet_view_set_model (tree_view, NULL);
1424 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->finalize (object);
1429 /* GtkWidget Methods
1432 /* GtkWidget::map helper */
1434 pspp_sheet_view_map_buttons (PsppSheetView *tree_view)
1438 g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
1440 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
1442 PsppSheetViewColumn *column;
1444 for (list = tree_view->priv->columns; list; list = list->next)
1446 column = list->data;
1447 if (column->button != NULL &&
1448 gtk_widget_get_visible (column->button) &&
1449 !gtk_widget_get_mapped (column->button))
1450 gtk_widget_map (column->button);
1452 for (list = tree_view->priv->columns; list; list = list->next)
1454 column = list->data;
1455 if (column->visible == FALSE || column->window == NULL)
1457 if (column->resizable)
1459 gdk_window_raise (column->window);
1460 gdk_window_show (column->window);
1463 gdk_window_hide (column->window);
1465 gdk_window_show (tree_view->priv->header_window);
1470 pspp_sheet_view_map (GtkWidget *widget)
1472 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1475 gtk_widget_set_mapped (widget, TRUE);
1477 tmp_list = tree_view->priv->children;
1480 PsppSheetViewChild *child = tmp_list->data;
1481 tmp_list = tmp_list->next;
1483 if (gtk_widget_get_visible (child->widget))
1485 if (!gtk_widget_get_mapped (child->widget))
1486 gtk_widget_map (child->widget);
1489 gdk_window_show (tree_view->priv->bin_window);
1491 pspp_sheet_view_map_buttons (tree_view);
1493 gdk_window_show (gtk_widget_get_window (widget));
1497 pspp_sheet_view_realize (GtkWidget *widget)
1499 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1502 GdkWindowAttr attributes;
1503 gint attributes_mask;
1504 GtkAllocation allocation;
1506 gtk_widget_set_realized (widget, TRUE);
1508 gtk_widget_get_allocation (widget, &allocation);
1510 /* Make the main, clipping window */
1511 attributes.window_type = GDK_WINDOW_CHILD;
1512 attributes.x = allocation.x;
1513 attributes.y = allocation.y;
1514 attributes.width = allocation.width;
1515 attributes.height = allocation.height;
1516 attributes.wclass = GDK_INPUT_OUTPUT;
1517 attributes.visual = gtk_widget_get_visual (widget);
1518 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1520 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
1522 window = gdk_window_new (gtk_widget_get_parent_window (widget),
1523 &attributes, attributes_mask);
1524 gtk_widget_set_window (widget, window);
1526 gtk_widget_register_window (widget, window);
1527 gtk_widget_get_allocation (widget, &allocation);
1529 /* Make the window for the tree */
1531 attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
1532 attributes.width = MAX (tree_view->priv->width, allocation.width);
1533 attributes.height = allocation.height;
1534 attributes.event_mask = (GDK_EXPOSURE_MASK |
1536 GDK_POINTER_MOTION_MASK |
1537 GDK_ENTER_NOTIFY_MASK |
1538 GDK_LEAVE_NOTIFY_MASK |
1539 GDK_BUTTON_PRESS_MASK |
1540 GDK_BUTTON_RELEASE_MASK |
1541 gtk_widget_get_events (widget));
1543 tree_view->priv->bin_window = gdk_window_new (window,
1544 &attributes, attributes_mask);
1545 gtk_widget_register_window (widget, tree_view->priv->bin_window);
1546 gtk_widget_get_allocation (widget, &allocation);
1548 /* Make the column header window */
1551 attributes.width = MAX (tree_view->priv->width, allocation.width);
1552 attributes.height = tree_view->priv->header_height;
1553 attributes.event_mask = (GDK_EXPOSURE_MASK |
1555 GDK_BUTTON_PRESS_MASK |
1556 GDK_BUTTON_RELEASE_MASK |
1557 GDK_KEY_PRESS_MASK |
1558 GDK_KEY_RELEASE_MASK |
1559 gtk_widget_get_events (widget));
1561 tree_view->priv->header_window = gdk_window_new (window,
1562 &attributes, attributes_mask);
1563 gtk_widget_register_window (widget, tree_view->priv->header_window);
1565 { /* Ensure Background */
1566 GtkStyleContext *context;
1568 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
1570 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
1571 gtk_style_context_set_background (context, tree_view->priv->header_window);
1574 tmp_list = tree_view->priv->children;
1577 PsppSheetViewChild *child = tmp_list->data;
1578 tmp_list = tmp_list->next;
1580 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
1583 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
1584 _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list->data));
1586 /* Need to call those here, since they create GCs */
1587 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
1589 install_presize_handler (tree_view);
1593 pspp_sheet_view_unrealize (GtkWidget *widget)
1595 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1596 PsppSheetViewPrivate *priv = tree_view->priv;
1599 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget);
1601 if (priv->scroll_timeout != 0)
1603 g_source_remove (priv->scroll_timeout);
1604 priv->scroll_timeout = 0;
1607 if (priv->open_dest_timeout != 0)
1609 g_source_remove (priv->open_dest_timeout);
1610 priv->open_dest_timeout = 0;
1613 if (priv->presize_handler_timer != 0)
1615 g_source_remove (priv->presize_handler_timer);
1616 priv->presize_handler_timer = 0;
1619 if (priv->validate_rows_timer != 0)
1621 g_source_remove (priv->validate_rows_timer);
1622 priv->validate_rows_timer = 0;
1625 if (priv->scroll_sync_timer != 0)
1627 g_source_remove (priv->scroll_sync_timer);
1628 priv->scroll_sync_timer = 0;
1631 if (priv->typeselect_flush_timeout)
1633 g_source_remove (priv->typeselect_flush_timeout);
1634 priv->typeselect_flush_timeout = 0;
1637 for (list = priv->columns; list; list = list->next)
1638 _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list->data));
1640 gdk_window_set_user_data (priv->bin_window, NULL);
1641 gdk_window_destroy (priv->bin_window);
1642 priv->bin_window = NULL;
1644 gdk_window_set_user_data (priv->header_window, NULL);
1645 gdk_window_destroy (priv->header_window);
1646 priv->header_window = NULL;
1648 if (priv->drag_window)
1650 gdk_window_set_user_data (priv->drag_window, NULL);
1651 gdk_window_destroy (priv->drag_window);
1652 priv->drag_window = NULL;
1655 if (priv->drag_highlight_window)
1657 gdk_window_set_user_data (priv->drag_highlight_window, NULL);
1658 gdk_window_destroy (priv->drag_highlight_window);
1659 priv->drag_highlight_window = NULL;
1662 if (tree_view->priv->columns != NULL)
1664 list = tree_view->priv->columns;
1667 PsppSheetViewColumn *column;
1668 column = PSPP_SHEET_VIEW_COLUMN (list->data);
1670 pspp_sheet_view_remove_column (tree_view, column);
1672 tree_view->priv->columns = NULL;
1676 /* GtkWidget::size_request helper */
1678 pspp_sheet_view_size_request_columns (PsppSheetView *tree_view)
1682 tree_view->priv->header_height = 0;
1684 if (tree_view->priv->model)
1686 for (list = tree_view->priv->columns; list; list = list->next)
1688 GtkRequisition requisition;
1689 PsppSheetViewColumn *column = list->data;
1691 pspp_sheet_view_column_size_request (column, &requisition);
1692 column->button_request = requisition.width;
1693 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
1699 /* Called only by ::size_request */
1701 pspp_sheet_view_update_size (PsppSheetView *tree_view)
1704 PsppSheetViewColumn *column;
1707 if (tree_view->priv->model == NULL)
1709 tree_view->priv->width = 0;
1710 tree_view->priv->prev_width = 0;
1711 tree_view->priv->height = 0;
1715 tree_view->priv->prev_width = tree_view->priv->width;
1716 tree_view->priv->width = 0;
1718 /* keep this in sync with size_allocate below */
1719 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
1721 gint real_requested_width = 0;
1722 column = list->data;
1723 if (!column->visible)
1726 if (column->use_resized_width)
1728 real_requested_width = column->resized_width;
1732 real_requested_width = column->fixed_width;
1735 if (column->min_width != -1)
1736 real_requested_width = MAX (real_requested_width, column->min_width);
1737 if (column->max_width != -1)
1738 real_requested_width = MIN (real_requested_width, column->max_width);
1740 tree_view->priv->width += real_requested_width;
1743 tree_view->priv->height = tree_view->priv->fixed_height * tree_view->priv->row_count;
1747 pspp_sheet_view_size_request (GtkWidget *widget,
1748 GtkRequisition *requisition)
1750 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1753 /* we validate some rows initially just to make sure we have some size.
1754 * In practice, with a lot of static lists, this should get a good width.
1756 initialize_fixed_height_mode (tree_view);
1757 pspp_sheet_view_size_request_columns (tree_view);
1758 pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget));
1760 requisition->width = tree_view->priv->width;
1761 requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
1763 tmp_list = tree_view->priv->children;
1767 PsppSheetViewChild *child = tmp_list->data;
1768 GtkRequisition child_requisition;
1770 tmp_list = tmp_list->next;
1772 if (gtk_widget_get_visible (child->widget))
1773 gtk_widget_size_request (child->widget, &child_requisition);
1778 invalidate_column (PsppSheetView *tree_view,
1779 PsppSheetViewColumn *column)
1781 gint column_offset = 0;
1783 GtkWidget *widget = GTK_WIDGET (tree_view);
1786 if (!gtk_widget_get_realized (widget))
1789 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1790 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
1792 list = (rtl ? list->prev : list->next))
1794 PsppSheetViewColumn *tmpcolumn = list->data;
1795 if (tmpcolumn == column)
1797 GdkRectangle invalid_rect;
1798 GtkAllocation allocation;
1800 gtk_widget_get_allocation (widget, &allocation);
1801 invalid_rect.x = column_offset;
1803 invalid_rect.width = column->width;
1804 invalid_rect.height = allocation.height;
1806 gdk_window_invalidate_rect (gtk_widget_get_window (widget), &invalid_rect, TRUE);
1810 column_offset += tmpcolumn->width;
1815 invalidate_last_column (PsppSheetView *tree_view)
1820 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1822 for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
1824 last_column = (rtl ? last_column->next : last_column->prev))
1826 if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible)
1828 invalidate_column (tree_view, last_column->data);
1835 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_view,
1836 PsppSheetViewColumn *column)
1838 gint real_requested_width;
1840 if (column->use_resized_width)
1842 real_requested_width = column->resized_width;
1846 real_requested_width = column->fixed_width;
1849 if (column->min_width != -1)
1850 real_requested_width = MAX (real_requested_width, column->min_width);
1851 if (column->max_width != -1)
1852 real_requested_width = MIN (real_requested_width, column->max_width);
1854 return real_requested_width;
1858 span_intersects (int a0, int a_width,
1859 int b0, int b_width)
1861 int a1 = a0 + a_width;
1862 int b1 = b0 + b_width;
1863 return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1);
1866 /* GtkWidget::size_allocate helper */
1868 pspp_sheet_view_size_allocate_columns (GtkWidget *widget,
1869 gboolean *width_changed)
1871 PsppSheetView *tree_view;
1872 GList *list, *first_column, *last_column;
1873 PsppSheetViewColumn *column;
1874 GtkAllocation col_allocation;
1875 GtkAllocation allocation;
1877 gint extra, extra_per_column;
1878 gint full_requested_width = 0;
1879 gint number_of_expand_columns = 0;
1880 gboolean column_changed = FALSE;
1883 tree_view = PSPP_SHEET_VIEW (widget);
1885 for (last_column = g_list_last (tree_view->priv->columns);
1886 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
1887 last_column = last_column->prev)
1890 if (last_column == NULL)
1893 for (first_column = g_list_first (tree_view->priv->columns);
1894 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
1895 first_column = first_column->next)
1898 col_allocation.y = 0;
1899 col_allocation.height = tree_view->priv->header_height;
1901 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1903 /* find out how many extra space and expandable columns we have */
1904 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
1906 column = (PsppSheetViewColumn *)list->data;
1908 if (!column->visible)
1911 full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1914 number_of_expand_columns++;
1917 gtk_widget_get_allocation (widget, &allocation);
1918 extra = MAX (allocation.width - full_requested_width, 0);
1919 if (number_of_expand_columns > 0)
1920 extra_per_column = extra/number_of_expand_columns;
1922 extra_per_column = 0;
1924 for (list = (rtl ? last_column : first_column);
1925 list != (rtl ? first_column->prev : last_column->next);
1926 list = (rtl ? list->prev : list->next))
1928 gint real_requested_width = 0;
1931 column = list->data;
1932 old_width = column->width;
1934 if (!column->visible)
1937 /* We need to handle the dragged button specially.
1939 if (column == tree_view->priv->drag_column)
1941 GtkAllocation drag_allocation;
1942 drag_allocation.width = gdk_window_get_width (tree_view->priv->drag_window);
1943 drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window);
1944 drag_allocation.x = 0;
1945 drag_allocation.y = 0;
1946 pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column,
1948 width += drag_allocation.width;
1952 real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1954 col_allocation.x = width;
1955 column->width = real_requested_width;
1959 if (number_of_expand_columns == 1)
1961 /* We add the remander to the last column as
1963 column->width += extra;
1967 column->width += extra_per_column;
1968 extra -= extra_per_column;
1969 number_of_expand_columns --;
1973 if (column->width != old_width)
1974 g_object_notify (G_OBJECT (column), "width");
1976 col_allocation.width = column->width;
1977 width += column->width;
1979 if (column->width > old_width)
1980 column_changed = TRUE;
1982 pspp_sheet_view_column_size_allocate (column, &col_allocation);
1985 gdk_window_move_resize (column->window,
1986 col_allocation.x + (rtl ? 0 : col_allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
1988 TREE_VIEW_DRAG_WIDTH, col_allocation.height);
1991 /* We change the width here. The user might have been resizing columns,
1992 * so the total width of the tree view changes.
1994 tree_view->priv->width = width;
1996 *width_changed = TRUE;
1999 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2003 update_childrens_allocation (PsppSheetView *tree_view)
2006 for (tmp_list = tree_view->priv->children; tmp_list; tmp_list = tmp_list->next)
2008 PsppSheetViewChild *child = tmp_list->data;
2009 GtkAllocation allocation;
2012 /* totally ignore our child's requisition */
2013 path = _pspp_sheet_view_find_path (tree_view, child->node);
2014 pspp_sheet_view_get_cell_area (tree_view, path, child->column, &allocation);
2015 gtk_tree_path_free (path);
2016 gtk_widget_size_allocate (child->widget, &allocation);
2021 pspp_sheet_view_size_allocate (GtkWidget *widget,
2022 GtkAllocation *allocation)
2024 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2026 gboolean width_changed = FALSE;
2027 GtkAllocation old_allocation;
2028 gtk_widget_get_allocation (widget, &old_allocation);
2030 if (allocation->width != old_allocation.width)
2031 width_changed = TRUE;
2033 gtk_widget_set_allocation (widget, allocation);
2035 /* We size-allocate the columns first because the width of the
2036 * tree view (used in updating the adjustments below) might change.
2038 pspp_sheet_view_size_allocate_columns (widget, &width_changed);
2040 gtk_adjustment_set_page_size (tree_view->priv->hadjustment, allocation->width);
2041 gtk_adjustment_set_page_increment (tree_view->priv->hadjustment, allocation->width * 0.9);
2042 gtk_adjustment_set_step_increment (tree_view->priv->hadjustment, allocation->width * 0.1);
2043 gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0);
2044 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->hadjustment), tree_view->priv->width));
2046 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2048 if (allocation->width < tree_view->priv->width)
2050 if (tree_view->priv->init_hadjust_value)
2052 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2053 tree_view->priv->init_hadjust_value = FALSE;
2055 else if (allocation->width != old_allocation.width)
2057 gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) - allocation->width + old_allocation.width, 0, tree_view->priv->width - allocation->width));
2060 gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (tree_view->priv->width - (tree_view->priv->prev_width - gtk_adjustment_get_value (tree_view->priv->hadjustment)), 0, tree_view->priv->width - allocation->width));
2064 gtk_adjustment_set_value (tree_view->priv->hadjustment, 0);
2065 tree_view->priv->init_hadjust_value = TRUE;
2069 if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + allocation->width > tree_view->priv->width)
2070 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2072 gtk_adjustment_changed (tree_view->priv->hadjustment);
2074 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2075 gtk_adjustment_set_step_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.1);
2076 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.9);
2077 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
2078 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->vadjustment), tree_view->priv->height));
2080 gtk_adjustment_changed (tree_view->priv->vadjustment);
2082 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2083 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
2084 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2085 else if (gtk_adjustment_get_value (tree_view->priv->vadjustment) + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
2086 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2087 tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
2088 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2089 pspp_sheet_view_top_row_to_dy (tree_view);
2091 pspp_sheet_view_dy_to_top_row (tree_view);
2093 if (gtk_widget_get_realized (widget))
2095 gdk_window_move_resize (gtk_widget_get_window (widget),
2096 allocation->x, allocation->y,
2097 allocation->width, allocation->height);
2098 gdk_window_move_resize (tree_view->priv->header_window,
2099 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2101 MAX (tree_view->priv->width, allocation->width),
2102 tree_view->priv->header_height);
2103 gdk_window_move_resize (tree_view->priv->bin_window,
2104 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2105 TREE_VIEW_HEADER_HEIGHT (tree_view),
2106 MAX (tree_view->priv->width, allocation->width),
2107 allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2110 if (tree_view->priv->row_count == 0)
2111 invalidate_empty_focus (tree_view);
2113 if (gtk_widget_get_realized (widget))
2115 gboolean has_expand_column = FALSE;
2116 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2118 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2120 has_expand_column = TRUE;
2125 /* This little hack only works if we have an LTR locale, and no column has the */
2128 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2129 ! has_expand_column)
2130 invalidate_last_column (tree_view);
2132 gtk_widget_queue_draw (widget);
2134 update_childrens_allocation(tree_view);
2138 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2140 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2142 GtkWidget *widget = GTK_WIDGET (tree_view);
2144 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2145 gtk_widget_grab_focus (widget);
2146 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2150 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2153 return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2157 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2160 range_tower_set1 (tree_view->priv->selected, node, 1);
2164 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2167 range_tower_set0 (tree_view->priv->selected, node, 1);
2171 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2174 return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2178 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2181 return node > 0 ? node - 1 : -1;
2185 all_columns_selected (PsppSheetView *tree_view)
2189 for (list = tree_view->priv->columns; list; list = list->next)
2191 PsppSheetViewColumn *column = list->data;
2192 if (column->selectable && !column->selected)
2200 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2202 PsppSheetViewColumn *column,
2203 GdkEventButton *event)
2205 PsppSheetSelection *selection;
2206 PsppSheetSelectionMode mode;
2208 gboolean update_anchor;
2212 g_return_val_if_fail (tree_view != NULL, FALSE);
2213 g_return_val_if_fail (column != NULL, FALSE);
2215 selection = tree_view->priv->selection;
2216 mode = pspp_sheet_selection_get_mode (selection);
2217 if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2220 if (!column->row_head)
2225 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2226 if (event->type != GDK_BUTTON_PRESS
2227 || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2233 path = gtk_tree_path_new_from_indices (node, -1);
2236 pspp_sheet_selection_unselect_all (selection);
2237 pspp_sheet_selection_select_path (selection, path);
2238 pspp_sheet_selection_select_all_columns (selection);
2239 update_anchor = TRUE;
2242 else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2244 if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2245 || !all_columns_selected (tree_view))
2247 pspp_sheet_selection_unselect_all (selection);
2248 pspp_sheet_selection_select_path (selection, path);
2249 pspp_sheet_selection_select_all_columns (selection);
2250 update_anchor = TRUE;
2254 update_anchor = handled = FALSE;
2256 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2257 && modifiers == GDK_CONTROL_MASK)
2259 if (!all_columns_selected (tree_view))
2261 pspp_sheet_selection_unselect_all (selection);
2262 pspp_sheet_selection_select_all_columns (selection);
2265 if (pspp_sheet_selection_path_is_selected (selection, path))
2266 pspp_sheet_selection_unselect_path (selection, path);
2268 pspp_sheet_selection_select_path (selection, path);
2269 update_anchor = TRUE;
2272 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2273 && modifiers == GDK_SHIFT_MASK)
2275 GtkTreeRowReference *anchor = tree_view->priv->anchor;
2276 GtkTreePath *anchor_path;
2278 if (all_columns_selected (tree_view)
2279 && gtk_tree_row_reference_valid (anchor))
2281 update_anchor = FALSE;
2282 anchor_path = gtk_tree_row_reference_get_path (anchor);
2286 update_anchor = TRUE;
2287 anchor_path = gtk_tree_path_copy (path);
2290 pspp_sheet_selection_unselect_all (selection);
2291 pspp_sheet_selection_select_range (selection, anchor_path, path);
2292 pspp_sheet_selection_select_all_columns (selection);
2294 gtk_tree_path_free (anchor_path);
2299 update_anchor = handled = FALSE;
2303 if (tree_view->priv->anchor)
2304 gtk_tree_row_reference_free (tree_view->priv->anchor);
2305 tree_view->priv->anchor =
2306 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2307 tree_view->priv->model,
2311 gtk_tree_path_free (path);
2316 find_click (PsppSheetView *tree_view,
2319 PsppSheetViewColumn **column,
2320 GdkRectangle *background_area,
2321 GdkRectangle *cell_area)
2328 /* find the node that was clicked */
2329 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2332 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2337 background_area->y = y_offset + y;
2338 background_area->height = ROW_HEIGHT (tree_view);
2339 background_area->x = 0;
2341 /* Let the column have a chance at selecting it. */
2342 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2343 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2344 list; list = (rtl ? list->prev : list->next))
2346 PsppSheetViewColumn *candidate = list->data;
2348 if (!candidate->visible)
2351 background_area->width = candidate->width;
2352 if ((background_area->x > x) ||
2353 (background_area->x + background_area->width <= x))
2355 background_area->x += background_area->width;
2359 /* we found the focus column */
2361 pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2363 *column = candidate;
2371 pspp_sheet_view_button_press (GtkWidget *widget,
2372 GdkEventButton *event)
2374 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2376 PsppSheetViewColumn *column = NULL;
2378 GdkRectangle background_area;
2379 GdkRectangle cell_area;
2382 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2383 pspp_sheet_view_stop_editing (tree_view, FALSE);
2386 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2387 * we're done handling the button press.
2390 if (event->window == tree_view->priv->bin_window)
2395 gint pre_val, aft_val;
2396 PsppSheetViewColumn *column = NULL;
2397 GtkCellRenderer *focus_cell = NULL;
2398 gboolean row_double_click = FALSE;
2401 if (tree_view->priv->row_count == 0)
2403 grab_focus_and_unset_draw_keyfocus (tree_view);
2407 if (!find_click (tree_view, event->x, event->y, &node, &column,
2408 &background_area, &cell_area))
2410 grab_focus_and_unset_draw_keyfocus (tree_view);
2414 tree_view->priv->focus_column = column;
2416 if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2420 pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2422 path = _pspp_sheet_view_find_path (tree_view, node);
2424 /* we only handle selection modifications on the first button press
2426 if (event->type == GDK_BUTTON_PRESS)
2428 PsppSheetSelectionMode mode = 0;
2430 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2431 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
2432 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2433 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
2435 focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2437 pspp_sheet_view_column_focus_cell (column, focus_cell);
2439 if (event->state & GDK_CONTROL_MASK)
2441 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, mode);
2442 pspp_sheet_view_real_toggle_cursor_row (tree_view);
2444 else if (event->state & GDK_SHIFT_MASK)
2446 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
2447 pspp_sheet_view_real_select_cursor_row (tree_view, FALSE, mode);
2451 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
2454 if (tree_view->priv->anchor_column == NULL ||
2455 !(event->state & GDK_SHIFT_MASK))
2456 tree_view->priv->anchor_column = column;
2457 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2458 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2459 tree_view->priv->anchor_column,
2463 /* the treeview may have been scrolled because of _set_cursor,
2467 aft_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2468 dval = pre_val - aft_val;
2470 cell_area.y += dval;
2471 background_area.y += dval;
2473 /* Save press to possibly begin a drag
2475 if (!tree_view->priv->in_grab &&
2476 tree_view->priv->pressed_button < 0)
2478 tree_view->priv->pressed_button = event->button;
2479 tree_view->priv->press_start_x = event->x;
2480 tree_view->priv->press_start_y = event->y;
2481 tree_view->priv->press_start_node = node;
2483 if (tree_view->priv->rubber_banding_enable
2484 && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2485 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2487 tree_view->priv->press_start_y += tree_view->priv->dy;
2488 tree_view->priv->rubber_band_x = event->x;
2489 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2490 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2492 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2493 tree_view->priv->rubber_band_ctrl = TRUE;
2494 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2495 tree_view->priv->rubber_band_shift = TRUE;
2500 /* Test if a double click happened on the same row. */
2501 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2503 int double_click_time, double_click_distance;
2505 g_object_get (gtk_settings_get_for_screen (
2506 gtk_widget_get_screen (widget)),
2507 "gtk-double-click-time", &double_click_time,
2508 "gtk-double-click-distance", &double_click_distance,
2511 /* Same conditions as _gdk_event_button_generate */
2512 if (tree_view->priv->last_button_x != -1 &&
2513 (event->time < tree_view->priv->last_button_time + double_click_time) &&
2514 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2515 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2517 /* We do no longer compare paths of this row and the
2518 * row clicked previously. We use the double click
2519 * distance to decide whether this is a valid click,
2520 * allowing the mouse to slightly move over another row.
2522 row_double_click = TRUE;
2524 tree_view->priv->last_button_time = 0;
2525 tree_view->priv->last_button_x = -1;
2526 tree_view->priv->last_button_y = -1;
2530 tree_view->priv->last_button_time = event->time;
2531 tree_view->priv->last_button_x = event->x;
2532 tree_view->priv->last_button_y = event->y;
2536 if (row_double_click)
2538 gtk_grab_remove (widget);
2539 pspp_sheet_view_row_activated (tree_view, path, column);
2541 if (tree_view->priv->pressed_button == event->button)
2542 tree_view->priv->pressed_button = -1;
2545 gtk_tree_path_free (path);
2547 /* If we activated the row through a double click we don't want to grab
2548 * focus back, as moving focus to another widget is pretty common.
2550 if (!row_double_click)
2551 grab_focus_and_unset_draw_keyfocus (tree_view);
2556 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2558 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2560 column = list->data;
2561 if (event->window == column->window &&
2562 column->resizable &&
2567 if (gdk_pointer_grab (column->window, FALSE,
2568 GDK_POINTER_MOTION_HINT_MASK |
2569 GDK_BUTTON1_MOTION_MASK |
2570 GDK_BUTTON_RELEASE_MASK,
2571 NULL, NULL, event->time))
2574 gtk_grab_add (widget);
2575 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2576 column->resized_width = column->width;
2578 /* block attached dnd signal handler */
2579 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2581 g_signal_handlers_block_matched (widget,
2582 G_SIGNAL_MATCH_DATA,
2586 tree_view->priv->drag_pos = i;
2587 tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2589 if (!gtk_widget_has_focus (widget))
2590 gtk_widget_grab_focus (widget);
2598 /* GtkWidget::button_release_event helper */
2600 pspp_sheet_view_button_release_drag_column (GtkWidget *widget,
2601 GdkEventButton *event)
2603 PsppSheetView *tree_view;
2607 tree_view = PSPP_SHEET_VIEW (widget);
2609 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2610 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2611 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2613 /* Move the button back */
2614 g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2616 g_object_ref (tree_view->priv->drag_column->button);
2617 gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2618 gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2619 gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2620 g_object_unref (tree_view->priv->drag_column->button);
2621 gtk_widget_queue_resize (widget);
2622 if (tree_view->priv->drag_column->resizable)
2624 gdk_window_raise (tree_view->priv->drag_column->window);
2625 gdk_window_show (tree_view->priv->drag_column->window);
2628 gdk_window_hide (tree_view->priv->drag_column->window);
2630 gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2634 if (tree_view->priv->cur_reorder &&
2635 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2636 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2637 tree_view->priv->cur_reorder->right_column);
2641 if (tree_view->priv->cur_reorder &&
2642 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2643 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2644 tree_view->priv->cur_reorder->left_column);
2646 tree_view->priv->drag_column = NULL;
2647 gdk_window_hide (tree_view->priv->drag_window);
2649 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2650 g_slice_free (PsppSheetViewColumnReorder, l->data);
2651 g_list_free (tree_view->priv->column_drag_info);
2652 tree_view->priv->column_drag_info = NULL;
2653 tree_view->priv->cur_reorder = NULL;
2655 if (tree_view->priv->drag_highlight_window)
2656 gdk_window_hide (tree_view->priv->drag_highlight_window);
2658 /* Reset our flags */
2659 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2660 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2665 /* GtkWidget::button_release_event helper */
2667 pspp_sheet_view_button_release_column_resize (GtkWidget *widget,
2668 GdkEventButton *event)
2670 PsppSheetView *tree_view;
2673 tree_view = PSPP_SHEET_VIEW (widget);
2675 tree_view->priv->drag_pos = -1;
2677 /* unblock attached dnd signal handler */
2678 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2680 g_signal_handlers_unblock_matched (widget,
2681 G_SIGNAL_MATCH_DATA,
2685 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2686 gtk_grab_remove (widget);
2687 gdk_display_pointer_ungrab (gdk_window_get_display (event->window),
2693 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2694 GdkEventButton *event)
2696 GtkCellEditable *cell_editable;
2701 PsppSheetViewColumn *column;
2702 GdkRectangle background_area;
2703 GdkRectangle cell_area;
2709 if (event->window != tree_view->priv->bin_window)
2712 /* Ignore a released button, if that button wasn't depressed */
2713 if (tree_view->priv->pressed_button != event->button)
2716 if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2720 /* decide if we edit */
2721 path = _pspp_sheet_view_find_path (tree_view, node);
2722 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2723 if (event->button != 1 || modifiers)
2726 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2727 pspp_sheet_view_column_cell_set_cell_data (column,
2728 tree_view->priv->model,
2731 if (!pspp_sheet_view_column_get_quick_edit (column)
2732 && _pspp_sheet_view_column_has_editable_cell (column))
2735 flags = 0; /* FIXME: get the right flags */
2736 path_string = gtk_tree_path_to_string (path);
2738 if (!_pspp_sheet_view_column_cell_event (column,
2746 if (cell_editable == NULL)
2749 pspp_sheet_view_real_set_cursor (tree_view, path,
2750 TRUE, TRUE, 0); /* XXX mode? */
2751 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2754 _pspp_sheet_view_column_get_neighbor_sizes (
2755 column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2758 area.width -= right + left;
2760 pspp_sheet_view_real_start_editing (tree_view,
2767 g_free (path_string);
2768 gtk_tree_path_free (path);
2773 pspp_sheet_view_button_release (GtkWidget *widget,
2774 GdkEventButton *event)
2776 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2778 pspp_sheet_view_stop_editing (tree_view, FALSE);
2779 if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2780 && pspp_sheet_view_button_release_edit (tree_view, event))
2782 if (tree_view->priv->pressed_button == event->button)
2783 tree_view->priv->pressed_button = -1;
2785 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2789 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2790 return pspp_sheet_view_button_release_drag_column (widget, event);
2792 if (tree_view->priv->rubber_band_status)
2793 pspp_sheet_view_stop_rubber_band (tree_view);
2795 if (tree_view->priv->pressed_button == event->button)
2796 tree_view->priv->pressed_button = -1;
2798 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2799 return pspp_sheet_view_button_release_column_resize (widget, event);
2805 pspp_sheet_view_grab_broken (GtkWidget *widget,
2806 GdkEventGrabBroken *event)
2808 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2810 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2811 pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2813 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2814 pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2819 /* GtkWidget::motion_event function set.
2823 do_prelight (PsppSheetView *tree_view,
2825 /* these are in bin_window coords */
2829 int prev_node = tree_view->priv->prelight_node;
2831 if (prev_node != node)
2833 tree_view->priv->prelight_node = node;
2836 _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2839 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2845 prelight_or_select (PsppSheetView *tree_view,
2847 /* these are in bin_window coords */
2851 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2853 if (tree_view->priv->hover_selection &&
2854 (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2855 !(tree_view->priv->edited_column &&
2856 tree_view->priv->edited_column->editable_widget))
2860 if (!pspp_sheet_view_node_is_selected (tree_view, node))
2864 path = _pspp_sheet_view_find_path (tree_view, node);
2865 pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2866 if (pspp_sheet_view_node_is_selected (tree_view, node))
2868 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2869 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE, 0); /* XXX mode? */
2871 gtk_tree_path_free (path);
2875 else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2876 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2879 do_prelight (tree_view, node, x, y);
2883 ensure_unprelighted (PsppSheetView *tree_view)
2885 do_prelight (tree_view,
2887 -1000, -1000); /* coords not possibly over an arrow */
2889 g_assert (tree_view->priv->prelight_node < 0);
2893 update_prelight (PsppSheetView *tree_view,
2900 if (tree_view->priv->row_count == 0)
2905 ensure_unprelighted (tree_view);
2909 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2913 pspp_sheet_view_find_offset (tree_view, new_y, &node);
2916 prelight_or_select (tree_view, node, x, y);
2922 /* Our motion arrow is either a box (in the case of the original spot)
2923 * or an arrow. It is expander_size wide.
2946 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2949 PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2950 GtkWidget *widget = GTK_WIDGET (tree_view);
2951 GdkBitmap *mask = NULL;
2956 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2957 GdkWindowAttr attributes;
2958 guint attributes_mask;
2961 reorder->left_column == tree_view->priv->drag_column ||
2962 reorder->right_column == tree_view->priv->drag_column)
2963 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2964 else if (reorder->left_column || reorder->right_column)
2966 GdkRectangle visible_rect;
2967 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2968 if (reorder->left_column)
2969 x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2971 x = reorder->right_column->allocation.x;
2973 if (x < visible_rect.x)
2974 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2975 else if (x > visible_rect.x + visible_rect.width)
2976 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2978 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2981 /* We want to draw the rectangle over the initial location. */
2982 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2987 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2989 if (tree_view->priv->drag_highlight_window)
2991 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2993 gdk_window_destroy (tree_view->priv->drag_highlight_window);
2996 attributes.window_type = GDK_WINDOW_CHILD;
2997 attributes.wclass = GDK_INPUT_OUTPUT;
2998 attributes.x = tree_view->priv->drag_column_x;
3000 width = attributes.width = tree_view->priv->drag_column->allocation.width;
3001 height = attributes.height = tree_view->priv->drag_column->allocation.height;
3002 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3003 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3004 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3005 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3006 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
3007 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3009 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3010 gc = gdk_gc_new (mask);
3012 gdk_gc_set_foreground (gc, &col);
3013 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3015 gdk_gc_set_foreground(gc, &col);
3016 gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
3017 g_object_unref (gc);
3019 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3021 if (mask) g_object_unref (mask);
3022 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3025 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
3031 width = tree_view->priv->expander_size;
3033 /* Get x, y, width, height of arrow */
3034 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
3035 if (reorder->left_column)
3037 x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
3038 height = reorder->left_column->allocation.height;
3042 x += reorder->right_column->allocation.x - width/2;
3043 height = reorder->right_column->allocation.height;
3045 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3046 height += tree_view->priv->expander_size;
3048 /* Create the new window */
3049 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3051 if (tree_view->priv->drag_highlight_window)
3053 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3055 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3058 attributes.window_type = GDK_WINDOW_TEMP;
3059 attributes.wclass = GDK_INPUT_OUTPUT;
3060 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3061 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3062 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3063 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3066 attributes.width = width;
3067 attributes.height = height;
3068 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3069 &attributes, attributes_mask);
3070 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3072 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3073 gc = gdk_gc_new (mask);
3075 gdk_gc_set_foreground (gc, &col);
3076 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3078 /* Draw the 2 arrows as per above */
3080 gdk_gc_set_foreground (gc, &col);
3081 for (i = 0; i < width; i ++)
3083 if (i == (width/2 - 1))
3085 gdk_draw_line (mask, gc, i, j, i, height - j);
3086 if (i < (width/2 - 1))
3091 g_object_unref (gc);
3092 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3094 if (mask) g_object_unref (mask);
3097 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3098 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3100 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3101 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3107 width = tree_view->priv->expander_size;
3109 /* Get x, y, width, height of arrow */
3110 width = width/2; /* remember, the arrow only takes half the available width */
3111 gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
3112 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3113 x += widget->allocation.width - width;
3115 if (reorder->left_column)
3116 height = reorder->left_column->allocation.height;
3118 height = reorder->right_column->allocation.height;
3120 y -= tree_view->priv->expander_size;
3121 height += 2*tree_view->priv->expander_size;
3123 /* Create the new window */
3124 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3125 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3127 if (tree_view->priv->drag_highlight_window)
3129 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3131 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3134 attributes.window_type = GDK_WINDOW_TEMP;
3135 attributes.wclass = GDK_INPUT_OUTPUT;
3136 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3137 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3138 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3139 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3142 attributes.width = width;
3143 attributes.height = height;
3144 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3145 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3147 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3148 gc = gdk_gc_new (mask);
3150 gdk_gc_set_foreground (gc, &col);
3151 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3153 /* Draw the 2 arrows as per above */
3155 gdk_gc_set_foreground (gc, &col);
3156 j = tree_view->priv->expander_size;
3157 for (i = 0; i < width; i ++)
3160 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3164 gdk_draw_line (mask, gc, k, j, k, height - j);
3165 gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3166 gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3169 g_object_unref (gc);
3170 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3172 if (mask) g_object_unref (mask);
3175 tree_view->priv->drag_column_window_state = arrow_type;
3176 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3180 g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3181 gdk_window_hide (tree_view->priv->drag_highlight_window);
3185 gdk_window_show (tree_view->priv->drag_highlight_window);
3186 gdk_window_raise (tree_view->priv->drag_highlight_window);
3191 pspp_sheet_view_motion_resize_column (GtkWidget *widget,
3192 GdkEventMotion *event)
3196 PsppSheetViewColumn *column;
3197 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3199 column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3201 if (event->is_hint || event->window != gtk_widget_get_window (widget))
3202 gtk_widget_get_pointer (widget, &x, NULL);
3206 if (tree_view->priv->hadjustment)
3207 x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
3209 new_width = pspp_sheet_view_new_column_width (tree_view,
3210 tree_view->priv->drag_pos, &x);
3211 if (x != tree_view->priv->x_drag &&
3212 (new_width != column->fixed_width))
3214 column->use_resized_width = TRUE;
3215 column->resized_width = new_width;
3218 column->resized_width -= tree_view->priv->last_extra_space_per_column;
3220 gtk_widget_queue_resize (widget);
3228 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3230 PsppSheetViewColumnReorder *reorder = NULL;
3234 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3235 for (list = tree_view->priv->column_drag_info; list; list = list->next)
3237 reorder = (PsppSheetViewColumnReorder *) list->data;
3238 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3243 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3246 tree_view->priv->cur_reorder = reorder;
3247 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3251 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3253 GdkRectangle visible_rect;
3258 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3259 y += tree_view->priv->dy;
3261 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3263 /* see if we are near the edge. */
3264 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3267 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3272 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->vadjustment) + offset, 0.0,
3273 gtk_adjustment_get_upper (tree_view->priv->vadjustment) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
3274 gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3278 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3280 GdkRectangle visible_rect;
3285 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3287 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3289 /* See if we are near the edge. */
3290 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3293 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3299 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) + offset,
3300 0.0, gtk_adjustment_get_upper (tree_view->priv->hadjustment) - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
3301 gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3308 pspp_sheet_view_motion_drag_column (GtkWidget *widget,
3309 GdkEventMotion *event)
3311 PsppSheetView *tree_view = (PsppSheetView *) widget;
3312 PsppSheetViewColumn *column = tree_view->priv->drag_column;
3314 GtkAllocation allocation;
3317 if ((column == NULL) ||
3318 (event->window != tree_view->priv->drag_window))
3321 /* Handle moving the header */
3322 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3323 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
3324 x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3325 MAX (tree_view->priv->width, allocation.width) - column->allocation.width);
3326 gdk_window_move (tree_view->priv->drag_window, x, y);
3328 /* autoscroll, if needed */
3329 pspp_sheet_view_horizontal_autoscroll (tree_view);
3330 /* Update the current reorder position and arrow; */
3331 pspp_sheet_view_update_current_reorder (tree_view);
3337 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3339 remove_scroll_timeout (tree_view);
3340 gtk_grab_remove (GTK_WIDGET (tree_view));
3342 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3344 GtkTreePath *tmp_path;
3346 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3348 /* The anchor path should be set to the start path */
3349 tmp_path = _pspp_sheet_view_find_path (tree_view,
3350 tree_view->priv->rubber_band_start_node);
3352 if (tree_view->priv->anchor)
3353 gtk_tree_row_reference_free (tree_view->priv->anchor);
3355 tree_view->priv->anchor =
3356 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3357 tree_view->priv->model,
3360 gtk_tree_path_free (tmp_path);
3362 /* ... and the cursor to the end path */
3363 tmp_path = _pspp_sheet_view_find_path (tree_view,
3364 tree_view->priv->rubber_band_end_node);
3365 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE, 0); /* XXX mode? */
3366 gtk_tree_path_free (tmp_path);
3368 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3371 /* Clear status variables */
3372 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3373 tree_view->priv->rubber_band_shift = 0;
3374 tree_view->priv->rubber_band_ctrl = 0;
3376 tree_view->priv->rubber_band_start_node = -1;
3377 tree_view->priv->rubber_band_end_node = -1;
3381 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3385 gboolean skip_start,
3388 if (start_node == end_node)
3391 /* We skip the first node and jump inside the loop */
3397 /* Small optimization by assuming insensitive nodes are never
3402 if (tree_view->priv->rubber_band_shift)
3403 pspp_sheet_view_node_select (tree_view, start_node);
3404 else if (tree_view->priv->rubber_band_ctrl)
3406 /* Toggle the selection state */
3407 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3408 pspp_sheet_view_node_unselect (tree_view, start_node);
3410 pspp_sheet_view_node_select (tree_view, start_node);
3413 pspp_sheet_view_node_select (tree_view, start_node);
3417 /* Mirror the above */
3418 if (tree_view->priv->rubber_band_shift)
3419 pspp_sheet_view_node_unselect (tree_view, start_node);
3420 else if (tree_view->priv->rubber_band_ctrl)
3422 /* Toggle the selection state */
3423 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3424 pspp_sheet_view_node_unselect (tree_view, start_node);
3426 pspp_sheet_view_node_select (tree_view, start_node);
3429 pspp_sheet_view_node_unselect (tree_view, start_node);
3432 _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3434 if (start_node == end_node)
3439 start_node = pspp_sheet_view_node_next (tree_view, start_node);
3442 /* Ran out of tree */
3445 if (skip_end && start_node == end_node)
3452 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3455 return node * tree_view->priv->fixed_height;
3459 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3463 int fixed_height = tree_view->priv->fixed_height;
3464 if (fixed_height <= 0
3466 || height >= tree_view->priv->row_count * fixed_height)
3473 *new_node = height / fixed_height;
3474 return height % fixed_height;
3479 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3484 pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3485 pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3487 /* Handle the start area first */
3488 if (tree_view->priv->rubber_band_start_node < 0)
3490 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3497 else if (start_node < tree_view->priv->rubber_band_start_node)
3499 /* New node is above the old one; selection became bigger */
3500 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3502 tree_view->priv->rubber_band_start_node,
3507 else if (start_node > tree_view->priv->rubber_band_start_node)
3509 /* New node is below the old one; selection became smaller */
3510 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3511 tree_view->priv->rubber_band_start_node,
3518 tree_view->priv->rubber_band_start_node = start_node;
3520 /* Next, handle the end area */
3521 if (tree_view->priv->rubber_band_end_node < 0)
3523 /* In the event this happens, start_node was also -1; this case is
3527 else if (end_node < 0)
3529 /* Find the last node in the tree */
3530 pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3533 /* Selection reached end of the tree */
3534 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3535 tree_view->priv->rubber_band_end_node,
3541 else if (end_node > tree_view->priv->rubber_band_end_node)
3543 /* New node is below the old one; selection became bigger */
3544 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3545 tree_view->priv->rubber_band_end_node,
3551 else if (end_node < tree_view->priv->rubber_band_end_node)
3553 /* New node is above the old one; selection became smaller */
3554 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3556 tree_view->priv->rubber_band_end_node,
3562 tree_view->priv->rubber_band_end_node = end_node;
3565 #define GDK_RECTANGLE_PTR(X) ((GdkRectangle *)(X))
3568 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3571 cairo_rectangle_int_t old_area;
3572 cairo_rectangle_int_t new_area;
3573 cairo_rectangle_int_t common;
3574 cairo_region_t *invalid_region;
3575 PsppSheetViewColumn *column;
3577 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3578 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3579 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3580 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3582 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3585 y = MAX (y, 0) + tree_view->priv->dy;
3587 new_area.x = MIN (tree_view->priv->press_start_x, x);
3588 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3589 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3590 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3592 invalid_region = cairo_region_create_rectangle (&old_area);
3593 cairo_region_union_rectangle (invalid_region, &new_area);
3595 gdk_rectangle_intersect (GDK_RECTANGLE_PTR (&old_area),
3596 GDK_RECTANGLE_PTR (&new_area), GDK_RECTANGLE_PTR (&common));
3597 if (common.width > 2 && common.height > 2)
3599 cairo_region_t *common_region;
3601 /* make sure the border is invalidated */
3607 common_region = cairo_region_create_rectangle (&common);
3609 cairo_region_subtract (invalid_region, common_region);
3610 cairo_region_destroy (common_region);
3613 #if GTK_MAJOR_VERSION == 3
3614 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
3617 cairo_rectangle_int_t extents;
3619 cairo_region_get_extents (invalid_region, &extents);
3620 ereg = gdk_region_rectangle (GDK_RECTANGLE_PTR (&extents));
3621 gdk_window_invalidate_region (tree_view->priv->bin_window, ereg, TRUE);
3622 gdk_region_destroy (ereg);
3626 cairo_region_destroy (invalid_region);
3628 tree_view->priv->rubber_band_x = x;
3629 tree_view->priv->rubber_band_y = y;
3630 pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3632 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3633 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3634 tree_view->priv->anchor_column,
3637 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3639 pspp_sheet_view_update_rubber_band_selection (tree_view);
3644 pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view,
3649 GdkRectangle rubber_rect;
3653 rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3654 rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3655 rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3656 rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3658 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3661 cr = gdk_cairo_create (tree_view->priv->bin_window);
3662 cairo_set_line_width (cr, 1.0);
3664 style = gtk_widget_get_style (GTK_WIDGET (tree_view));
3665 cairo_set_source_rgba (cr,
3666 style->fg[GTK_STATE_NORMAL].red / 65535.,
3667 style->fg[GTK_STATE_NORMAL].green / 65535.,
3668 style->fg[GTK_STATE_NORMAL].blue / 65535.,
3671 gdk_cairo_rectangle (cr, &rect);
3675 cairo_set_source_rgb (cr,
3676 style->fg[GTK_STATE_NORMAL].red / 65535.,
3677 style->fg[GTK_STATE_NORMAL].green / 65535.,
3678 style->fg[GTK_STATE_NORMAL].blue / 65535.);
3680 cairo_rectangle (cr,
3681 rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3682 rubber_rect.width - 1, rubber_rect.height - 1);
3691 pspp_sheet_view_motion_bin_window (GtkWidget *widget,
3692 GdkEventMotion *event)
3694 PsppSheetView *tree_view;
3698 tree_view = (PsppSheetView *) widget;
3700 if (tree_view->priv->row_count == 0)
3703 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3705 GdkRectangle background_area, cell_area;
3706 PsppSheetViewColumn *column;
3708 if (find_click (tree_view, event->x, event->y, &node, &column,
3709 &background_area, &cell_area)
3710 && tree_view->priv->focus_column == column
3711 && tree_view->priv->press_start_node == node)
3714 gtk_grab_add (GTK_WIDGET (tree_view));
3715 pspp_sheet_view_update_rubber_band (tree_view);
3717 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3719 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3721 pspp_sheet_view_update_rubber_band (tree_view);
3723 add_scroll_timeout (tree_view);
3726 /* only check for an initiated drag when a button is pressed */
3727 if (tree_view->priv->pressed_button >= 0
3728 && !tree_view->priv->rubber_band_status)
3729 pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3731 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3735 pspp_sheet_view_find_offset (tree_view, new_y, &node);
3737 tree_view->priv->event_last_x = event->x;
3738 tree_view->priv->event_last_y = event->y;
3740 prelight_or_select (tree_view, node, event->x, event->y);
3746 pspp_sheet_view_motion (GtkWidget *widget,
3747 GdkEventMotion *event)
3749 PsppSheetView *tree_view;
3751 tree_view = (PsppSheetView *) widget;
3753 /* Resizing a column */
3754 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3755 return pspp_sheet_view_motion_resize_column (widget, event);
3758 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3759 return pspp_sheet_view_motion_drag_column (widget, event);
3761 /* Sanity check it */
3762 if (event->window == tree_view->priv->bin_window)
3763 return pspp_sheet_view_motion_bin_window (widget, event);
3768 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3769 * the tree is empty.
3772 invalidate_empty_focus (PsppSheetView *tree_view)
3776 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3781 area.width = gdk_window_get_width (tree_view->priv->bin_window);
3782 area.height = gdk_window_get_height (tree_view->priv->bin_window);
3783 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3786 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3790 draw_empty_focus (PsppSheetView *tree_view)
3792 GtkWidget *widget = GTK_WIDGET (tree_view);
3794 cairo_t *cr = gdk_cairo_create (tree_view->priv->bin_window);
3796 if (!gtk_widget_has_focus (widget))
3799 w = gdk_window_get_width (tree_view->priv->bin_window);
3800 h = gdk_window_get_height (tree_view->priv->bin_window);
3806 gtk_paint_focus (gtk_widget_get_style (widget),
3808 gtk_widget_get_state (widget),
3816 pspp_sheet_view_draw_vertical_grid_lines (PsppSheetView *tree_view,
3818 gint n_visible_columns,
3822 GList *list = tree_view->priv->columns;
3826 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3827 && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3830 /* Only draw the lines for visible rows and columns */
3831 for (list = tree_view->priv->columns; list; list = list->next, i++)
3833 PsppSheetViewColumn *column = list->data;
3836 if (! column->visible)
3839 current_x += column->width;
3841 /* Generally the grid lines should fit within the column, but for the
3842 last visible column we put it just past the end of the column.
3843 (Otherwise horizontal grid lines sometimes stick out by one pixel.) */
3845 if (i != n_visible_columns - 1)
3848 cairo_set_line_width (cr, 1.0);
3849 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
3850 cairo_move_to (cr, x + 0.5, min_y);
3851 cairo_line_to (cr, x + 0.5, max_y - min_y);
3856 /* Warning: Very scary function.
3857 * Modify at your own risk
3859 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3860 * FIXME: It's not...
3863 pspp_sheet_view_draw_bin (GtkWidget *widget,
3866 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3871 int drag_highlight = -1;
3874 gint y_offset, cell_offset;
3876 GdkRectangle background_area;
3877 GdkRectangle cell_area;
3879 gint bin_window_width;
3880 gint bin_window_height;
3881 GtkTreePath *cursor_path;
3882 GtkTreePath *drag_dest_path;
3883 GList *first_column, *last_column;
3884 gint vertical_separator;
3885 gint horizontal_separator;
3886 gint focus_line_width;
3887 gboolean allow_rules;
3888 gboolean has_special_cell;
3890 gint n_visible_columns;
3891 gint grid_line_width;
3892 gboolean row_ending_details;
3893 gboolean draw_vgrid_lines, draw_hgrid_lines;
3895 GtkStyleContext *context;
3896 context = gtk_widget_get_style_context (widget);
3899 GtkAllocation allocation;
3900 gtk_widget_get_allocation (widget, &allocation);
3902 GdkRectangle exposed_rect;
3903 gdk_cairo_get_clip_rectangle (cr, &exposed_rect);
3907 Zarea.height = allocation.height;
3909 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3911 gtk_widget_style_get (widget,
3912 "horizontal-separator", &horizontal_separator,
3913 "vertical-separator", &vertical_separator,
3914 "allow-rules", &allow_rules,
3915 "focus-line-width", &focus_line_width,
3916 "row-ending-details", &row_ending_details,
3919 if (tree_view->priv->row_count == 0)
3921 draw_empty_focus (tree_view);
3926 /* clip event->area to the visible area */
3927 if (Zarea.height < 0.5)
3931 validate_visible_area (tree_view);
3933 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, Zarea.y);
3937 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3939 gdk_window_get_width (tree_view->priv->bin_window);
3942 gdk_window_get_height (tree_view->priv->bin_window);
3945 if (tree_view->priv->height < bin_window_height)
3947 gtk_paint_flat_box (gtk_widget_get_style (widget),
3949 gtk_widget_get_state (widget),
3953 0, tree_view->priv->height,
3955 bin_window_height - tree_view->priv->height);
3961 /* find the path for the node */
3962 path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3963 gtk_tree_model_get_iter (tree_view->priv->model,
3966 gtk_tree_path_free (path);
3969 drag_dest_path = NULL;
3971 if (tree_view->priv->cursor)
3972 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3975 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3977 if (tree_view->priv->drag_dest_row)
3978 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3981 _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3985 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3986 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3988 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
3989 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3991 if (draw_vgrid_lines || draw_hgrid_lines)
3992 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
3994 n_visible_columns = 0;
3995 for (list = tree_view->priv->columns; list; list = list->next)
3997 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
3999 n_visible_columns ++;
4002 /* Find the last column */
4003 for (last_column = g_list_last (tree_view->priv->columns);
4004 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
4005 last_column = last_column->prev)
4009 for (first_column = g_list_first (tree_view->priv->columns);
4010 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
4011 first_column = first_column->next)
4014 /* Actually process the expose event. To do this, we want to
4015 * start at the first node of the event, and walk the tree in
4016 * order, drawing each successive node.
4023 gboolean is_first = FALSE;
4024 gboolean is_last = FALSE;
4025 gboolean done = FALSE;
4028 max_height = ROW_HEIGHT (tree_view);
4032 background_area.y = y_offset + Zarea.y;
4033 background_area.height = max_height;
4034 max_y = background_area.y + max_height;
4038 if (node == tree_view->priv->prelight_node)
4039 flags |= GTK_CELL_RENDERER_PRELIT;
4041 selected = pspp_sheet_view_node_is_selected (tree_view, node);
4045 if (tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
4047 /* we *need* to set cell data on all cells before the call
4048 * to _has_special_cell, else _has_special_cell() does not
4049 * return a correct value.
4051 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4053 list = (rtl ? list->prev : list->next))
4055 PsppSheetViewColumn *column = list->data;
4056 pspp_sheet_view_column_cell_set_cell_data (column,
4057 tree_view->priv->model,
4061 has_special_cell = pspp_sheet_view_has_special_cell (tree_view);
4064 has_special_cell = tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
4066 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4068 list = (rtl ? list->prev : list->next))
4070 PsppSheetViewColumn *column = list->data;
4071 const gchar *detail = NULL;
4072 gboolean selected_column;
4075 if (!column->visible)
4078 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
4079 selected_column = column->selected && column->selectable;
4081 selected_column = TRUE;
4084 if (cell_offset > Zarea.x + Zarea.width ||
4085 cell_offset + column->width < Zarea.x)
4087 cell_offset += column->width;
4092 if (selected && selected_column)
4093 flags |= GTK_CELL_RENDERER_SELECTED;
4095 flags &= ~GTK_CELL_RENDERER_SELECTED;
4097 if (column->show_sort_indicator)
4098 flags |= GTK_CELL_RENDERER_SORTED;
4100 flags &= ~GTK_CELL_RENDERER_SORTED;
4103 flags |= GTK_CELL_RENDERER_FOCUSED;
4105 flags &= ~GTK_CELL_RENDERER_FOCUSED;
4107 background_area.x = cell_offset;
4108 background_area.width = column->width;
4110 cell_area = background_area;
4111 cell_area.y += vertical_separator / 2;
4112 cell_area.x += horizontal_separator / 2;
4113 cell_area.height -= vertical_separator;
4114 cell_area.width -= horizontal_separator;
4116 if (draw_vgrid_lines)
4118 if (list == first_column)
4120 cell_area.width -= grid_line_width / 2;
4122 else if (list == last_column)
4124 cell_area.x += grid_line_width / 2;
4125 cell_area.width -= grid_line_width / 2;
4129 cell_area.x += grid_line_width / 2;
4130 cell_area.width -= grid_line_width;
4134 if (draw_hgrid_lines)
4136 cell_area.y += grid_line_width / 2;
4137 cell_area.height -= grid_line_width;
4141 if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4143 if (!gdk_rectangle_intersect (&background_area, &exposed_rect, NULL))
4146 cell_offset += column->width;
4151 pspp_sheet_view_column_cell_set_cell_data (column,
4152 tree_view->priv->model,
4155 /* Select the detail for drawing the cell. relevant
4156 * factors are parity, sortedness, and whether to
4159 if (allow_rules && tree_view->priv->has_rules)
4161 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4162 n_visible_columns >= 3)
4165 detail = "cell_odd_ruled_sorted";
4167 detail = "cell_even_ruled_sorted";
4172 detail = "cell_odd_ruled";
4174 detail = "cell_even_ruled";
4179 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4180 n_visible_columns >= 3)
4183 detail = "cell_odd_sorted";
4185 detail = "cell_even_sorted";
4190 detail = "cell_odd";
4192 detail = "cell_even";
4198 gtk_style_context_save (context);
4199 state = gtk_cell_renderer_get_state (NULL, widget, flags);
4200 gtk_style_context_set_state (context, state);
4201 gtk_style_context_add_class (context, GTK_STYLE_CLASS_CELL);
4203 /* Draw background */
4204 gtk_render_background (context, cr,
4207 background_area.width,
4208 background_area.height);
4211 gtk_render_frame (context, cr,
4214 background_area.width,
4215 background_area.height);
4217 if (draw_hgrid_lines)
4219 cairo_set_line_width (cr, 1.0);
4220 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
4222 if (background_area.y >= 0)
4225 gdk_draw_line (event->window,
4226 tree_view->priv->grid_line_gc[widget->state],
4227 background_area.x, background_area.y,
4228 background_area.x + background_area.width,
4231 cairo_move_to (cr, background_area.x, background_area.y - 0.5);
4232 cairo_line_to (cr, background_area.x + background_area.width,
4233 background_area.y - 0.5);
4237 if (y_offset + max_height <= Zarea.height - 0.5)
4240 gdk_draw_line (event->window,
4241 tree_view->priv->grid_line_gc[widget->state],
4242 background_area.x, background_area.y + max_height,
4243 background_area.x + background_area.width,
4244 background_area.y + max_height);
4247 cairo_move_to (cr, background_area.x, background_area.y + max_height - 0.5);
4248 cairo_line_to (cr, background_area.x + background_area.width,
4249 background_area.y + max_height - 0.5);
4255 _pspp_sheet_view_column_cell_render (column,
4262 cell_offset += column->width;
4263 gtk_style_context_restore (context);
4266 if (node == drag_highlight)
4268 /* Draw indicator for the drop
4270 gint highlight_y = -1;
4274 switch (tree_view->priv->drag_dest_pos)
4276 case PSPP_SHEET_VIEW_DROP_BEFORE:
4277 highlight_y = background_area.y - 1;
4278 if (highlight_y < 0)
4282 case PSPP_SHEET_VIEW_DROP_AFTER:
4283 highlight_y = background_area.y + background_area.height - 1;
4286 case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE:
4287 case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER:
4288 _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node);
4292 width = gdk_window_get_width (tree_view->priv->bin_window);
4294 if (row_ending_details)
4295 gtk_paint_focus (gtk_widget_get_style (widget),
4297 gtk_widget_get_state (widget),
4300 ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4301 : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4302 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4303 - focus_line_width / 2,
4304 width, ROW_HEIGHT (tree_view)
4305 - focus_line_width + 1);
4307 gtk_paint_focus (gtk_widget_get_style (widget),
4309 gtk_widget_get_state (widget),
4311 "treeview-drop-indicator",
4312 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4313 - focus_line_width / 2,
4314 width, ROW_HEIGHT (tree_view)
4315 - focus_line_width + 1);
4320 if (highlight_y >= 0)
4322 gdk_draw_line (event->window,
4323 widget->style->fg_gc[gtk_widget_get_state (widget)],
4326 rtl ? 0 : bin_window_width,
4332 y_offset += max_height;
4336 node = pspp_sheet_view_node_next (tree_view, node);
4339 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
4343 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
4350 while (y_offset < Zarea.height);
4353 pspp_sheet_view_draw_vertical_grid_lines (tree_view, cr, n_visible_columns,
4357 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4359 GdkRectangle *rectangles;
4362 gdk_region_get_rectangles (event->region,
4366 while (n_rectangles--)
4367 pspp_sheet_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
4369 g_free (rectangles);
4374 gtk_tree_path_free (cursor_path);
4377 gtk_tree_path_free (drag_dest_path);
4384 pspp_sheet_view_draw (GtkWidget *widget,
4387 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4388 GtkStyleContext *context;
4390 context = gtk_widget_get_style_context (widget);
4392 if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window))
4397 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->bin_window);
4398 pspp_sheet_view_draw_bin (widget, cr);
4401 /* We can't just chain up to Container::expose as it will try to send the
4402 * event to the headers, so we handle propagating it to our children
4403 * (eg. widgets being edited) ourselves.
4405 tmp_list = tree_view->priv->children;
4408 PsppSheetViewChild *child = tmp_list->data;
4409 tmp_list = tmp_list->next;
4411 gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
4416 gtk_render_background (context, cr,
4418 gtk_widget_get_allocated_width (widget),
4419 gtk_widget_get_allocated_height (widget));
4422 gtk_style_context_save (context);
4423 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_VIEW);
4425 if (gtk_cairo_should_draw_window (cr, tree_view->priv->header_window))
4427 gint n_visible_columns;
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 (int) gtk_adjustment_get_value (tree_view->priv->hadjustment),
4439 (int) gtk_widget_get_allocated_width (widget))
4440 && column->button != NULL)
4441 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4442 column->button, cr);
4445 n_visible_columns = 0;
4446 for (list = tree_view->priv->columns; list; list = list->next)
4448 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4450 n_visible_columns ++;
4453 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->header_window);
4454 pspp_sheet_view_draw_vertical_grid_lines (tree_view,
4458 TREE_VIEW_HEADER_HEIGHT (tree_view));
4461 if (tree_view->priv->drag_window &&
4462 gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window))
4464 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4465 tree_view->priv->drag_column->button,
4469 gtk_style_context_restore (context);
4481 /* returns 0x1 when no column has been found -- yes it's hackish */
4482 static PsppSheetViewColumn *
4483 pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
4484 PsppSheetViewColumn *column,
4487 PsppSheetViewColumn *left_column = NULL;
4488 PsppSheetViewColumn *cur_column = NULL;
4491 if (!column->reorderable)
4492 return (PsppSheetViewColumn *)0x1;
4494 switch (drop_position)
4497 /* find first column where we can drop */
4498 tmp_list = tree_view->priv->columns;
4499 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4500 return (PsppSheetViewColumn *)0x1;
4504 g_assert (tmp_list);
4506 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4507 tmp_list = tmp_list->next;
4509 if (left_column && left_column->visible == FALSE)
4512 if (!tree_view->priv->column_drop_func)
4515 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4517 left_column = cur_column;
4524 if (!tree_view->priv->column_drop_func)
4527 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4530 return (PsppSheetViewColumn *)0x1;
4534 /* find first column after column where we can drop */
4535 tmp_list = tree_view->priv->columns;
4537 for (; tmp_list; tmp_list = tmp_list->next)
4538 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4541 if (!tmp_list || !tmp_list->next)
4542 return (PsppSheetViewColumn *)0x1;
4544 tmp_list = tmp_list->next;
4545 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4546 tmp_list = tmp_list->next;
4550 g_assert (tmp_list);
4552 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4553 tmp_list = tmp_list->next;
4555 if (left_column && left_column->visible == FALSE)
4557 left_column = cur_column;
4559 tmp_list = tmp_list->next;
4563 if (!tree_view->priv->column_drop_func)
4566 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4568 left_column = cur_column;
4575 if (!tree_view->priv->column_drop_func)
4578 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4581 return (PsppSheetViewColumn *)0x1;
4585 /* find first column before column where we can drop */
4586 tmp_list = tree_view->priv->columns;
4588 for (; tmp_list; tmp_list = tmp_list->next)
4589 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4592 if (!tmp_list || !tmp_list->prev)
4593 return (PsppSheetViewColumn *)0x1;
4595 tmp_list = tmp_list->prev;
4596 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4597 tmp_list = tmp_list->prev;
4601 g_assert (tmp_list);
4603 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4605 if (left_column && !left_column->visible)
4607 /*if (!tmp_list->prev)
4608 return (PsppSheetViewColumn *)0x1;
4611 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4612 tmp_list = tmp_list->prev->prev;
4615 cur_column = left_column;
4617 tmp_list = tmp_list->prev;
4621 if (!tree_view->priv->column_drop_func)
4624 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4627 cur_column = left_column;
4628 tmp_list = tmp_list->prev;
4631 if (!tree_view->priv->column_drop_func)
4634 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4637 return (PsppSheetViewColumn *)0x1;
4641 /* same as DROP_HOME case, but doing it backwards */
4642 tmp_list = g_list_last (tree_view->priv->columns);
4645 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4646 return (PsppSheetViewColumn *)0x1;
4650 g_assert (tmp_list);
4652 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4654 if (left_column && !left_column->visible)
4656 cur_column = left_column;
4657 tmp_list = tmp_list->prev;
4660 if (!tree_view->priv->column_drop_func)
4663 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4666 cur_column = left_column;
4667 tmp_list = tmp_list->prev;
4670 if (!tree_view->priv->column_drop_func)
4673 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4676 return (PsppSheetViewColumn *)0x1;
4680 return (PsppSheetViewColumn *)0x1;
4684 pspp_sheet_view_key_press (GtkWidget *widget,
4687 PsppSheetView *tree_view = (PsppSheetView *) widget;
4689 if (tree_view->priv->rubber_band_status)
4691 if (event->keyval == GDK_Escape)
4692 pspp_sheet_view_stop_rubber_band (tree_view);
4697 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4699 if (event->keyval == GDK_Escape)
4701 tree_view->priv->cur_reorder = NULL;
4702 pspp_sheet_view_button_release_drag_column (widget, NULL);
4707 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4709 GList *focus_column;
4712 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4714 for (focus_column = tree_view->priv->columns;
4716 focus_column = focus_column->next)
4718 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4720 if (column->button && gtk_widget_has_focus (column->button))
4725 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4726 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4727 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4729 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4731 if (!column->resizable)
4733 gtk_widget_error_bell (widget);
4737 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4738 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4740 gint old_width = column->resized_width;
4742 column->resized_width = MAX (column->resized_width,
4744 column->resized_width -= 2;
4745 if (column->resized_width < 0)
4746 column->resized_width = 0;
4748 if (column->min_width == -1)
4749 column->resized_width = MAX (column->button_request,
4750 column->resized_width);
4752 column->resized_width = MAX (column->min_width,
4753 column->resized_width);
4755 if (column->max_width != -1)
4756 column->resized_width = MIN (column->resized_width,
4759 column->use_resized_width = TRUE;
4761 if (column->resized_width != old_width)
4762 gtk_widget_queue_resize (widget);
4764 gtk_widget_error_bell (widget);
4766 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4767 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4769 gint old_width = column->resized_width;
4771 column->resized_width = MAX (column->resized_width,
4773 column->resized_width += 2;
4775 if (column->max_width != -1)
4776 column->resized_width = MIN (column->resized_width,
4779 column->use_resized_width = TRUE;
4781 if (column->resized_width != old_width)
4782 gtk_widget_queue_resize (widget);
4784 gtk_widget_error_bell (widget);
4791 (event->state & GDK_MOD1_MASK) &&
4792 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4793 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4794 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4795 || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4797 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4799 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4800 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4802 PsppSheetViewColumn *col;
4803 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
4804 if (col != (PsppSheetViewColumn *)0x1)
4805 pspp_sheet_view_move_column_after (tree_view, column, col);
4807 gtk_widget_error_bell (widget);
4809 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4810 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4812 PsppSheetViewColumn *col;
4813 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4814 if (col != (PsppSheetViewColumn *)0x1)
4815 pspp_sheet_view_move_column_after (tree_view, column, col);
4817 gtk_widget_error_bell (widget);
4819 else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4821 PsppSheetViewColumn *col;
4822 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4823 if (col != (PsppSheetViewColumn *)0x1)
4824 pspp_sheet_view_move_column_after (tree_view, column, col);
4826 gtk_widget_error_bell (widget);
4828 else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4830 PsppSheetViewColumn *col;
4831 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4832 if (col != (PsppSheetViewColumn *)0x1)
4833 pspp_sheet_view_move_column_after (tree_view, column, col);
4835 gtk_widget_error_bell (widget);
4842 /* Chain up to the parent class. It handles the keybindings. */
4843 if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4846 if (tree_view->priv->search_entry_avoid_unhandled_binding)
4848 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4852 /* We pass the event to the search_entry. If its text changes, then we start
4853 * the typeahead find capabilities. */
4854 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4855 && tree_view->priv->enable_search
4856 && !tree_view->priv->search_custom_entry_set)
4858 GdkEvent *new_event;
4860 const char *new_text;
4863 gboolean text_modified;
4864 gulong popup_menu_id;
4866 pspp_sheet_view_ensure_interactive_directory (tree_view);
4868 /* Make a copy of the current text */
4869 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4870 new_event = gdk_event_copy ((GdkEvent *) event);
4871 g_object_unref (((GdkEventKey *) new_event)->window);
4872 ((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window));
4873 gtk_widget_realize (tree_view->priv->search_window);
4875 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
4876 "popup-menu", G_CALLBACK (gtk_true),
4879 /* Move the entry off screen */
4880 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4881 gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4882 gdk_screen_get_width (screen) + 1,
4883 gdk_screen_get_height (screen) + 1);
4884 gtk_widget_show (tree_view->priv->search_window);
4886 /* Send the event to the window. If the preedit_changed signal is emitted
4887 * during this event, we will set priv->imcontext_changed */
4888 tree_view->priv->imcontext_changed = FALSE;
4889 retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4890 gdk_event_free (new_event);
4891 gtk_widget_hide (tree_view->priv->search_window);
4893 g_signal_handler_disconnect (tree_view->priv->search_entry,
4896 /* We check to make sure that the entry tried to handle the text, and that
4897 * the text has changed.
4899 new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
4900 text_modified = strcmp (old_text, new_text) != 0;
4902 if (tree_view->priv->imcontext_changed || /* we're in a preedit */
4903 (retval && text_modified)) /* ...or the text was modified */
4905 if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
4907 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
4912 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
4922 pspp_sheet_view_key_release (GtkWidget *widget,
4925 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4927 if (tree_view->priv->rubber_band_status)
4930 return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
4933 /* FIXME Is this function necessary? Can I get an enter_notify event
4934 * w/o either an expose event or a mouse motion event?
4937 pspp_sheet_view_enter_notify (GtkWidget *widget,
4938 GdkEventCrossing *event)
4940 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4944 /* Sanity check it */
4945 if (event->window != tree_view->priv->bin_window)
4948 if (tree_view->priv->row_count == 0)
4951 if (event->mode == GDK_CROSSING_GRAB ||
4952 event->mode == GDK_CROSSING_GTK_GRAB ||
4953 event->mode == GDK_CROSSING_GTK_UNGRAB ||
4954 event->mode == GDK_CROSSING_STATE_CHANGED)
4957 /* find the node internally */
4958 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
4961 pspp_sheet_view_find_offset (tree_view, new_y, &node);
4963 tree_view->priv->event_last_x = event->x;
4964 tree_view->priv->event_last_y = event->y;
4966 prelight_or_select (tree_view, node, event->x, event->y);
4972 pspp_sheet_view_leave_notify (GtkWidget *widget,
4973 GdkEventCrossing *event)
4975 PsppSheetView *tree_view;
4977 if (event->mode == GDK_CROSSING_GRAB)
4980 tree_view = PSPP_SHEET_VIEW (widget);
4982 if (tree_view->priv->prelight_node >= 0)
4983 _pspp_sheet_view_queue_draw_node (tree_view,
4984 tree_view->priv->prelight_node,
4987 tree_view->priv->event_last_x = -10000;
4988 tree_view->priv->event_last_y = -10000;
4990 prelight_or_select (tree_view,
4992 -1000, -1000); /* coords not possibly over an arrow */
4999 pspp_sheet_view_focus_out (GtkWidget *widget,
5000 GdkEventFocus *event)
5002 PsppSheetView *tree_view;
5004 tree_view = PSPP_SHEET_VIEW (widget);
5006 gtk_widget_queue_draw (widget);
5008 /* destroy interactive search dialog */
5009 if (tree_view->priv->search_window)
5010 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
5016 /* Incremental Reflow
5020 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
5023 GtkAllocation allocation;
5024 gint y = pspp_sheet_view_node_find_offset (tree_view, node)
5025 - gtk_adjustment_get_value (tree_view->priv->vadjustment)
5026 + TREE_VIEW_HEADER_HEIGHT (tree_view);
5028 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5030 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
5033 tree_view->priv->fixed_height);
5037 node_is_visible (PsppSheetView *tree_view,
5043 y = pspp_sheet_view_node_find_offset (tree_view, node);
5044 height = ROW_HEIGHT (tree_view);
5046 if (y >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5047 y + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5048 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5054 /* Returns the row height. */
5056 validate_row (PsppSheetView *tree_view,
5061 PsppSheetViewColumn *column;
5062 GList *list, *first_column, *last_column;
5064 gint horizontal_separator;
5065 gint vertical_separator;
5066 gint focus_line_width;
5067 gboolean draw_vgrid_lines, draw_hgrid_lines;
5069 gint grid_line_width;
5070 gboolean wide_separators;
5071 gint separator_height;
5073 gtk_widget_style_get (GTK_WIDGET (tree_view),
5074 "focus-padding", &focus_pad,
5075 "focus-line-width", &focus_line_width,
5076 "horizontal-separator", &horizontal_separator,
5077 "vertical-separator", &vertical_separator,
5078 "grid-line-width", &grid_line_width,
5079 "wide-separators", &wide_separators,
5080 "separator-height", &separator_height,
5084 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5085 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5087 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5088 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5090 for (last_column = g_list_last (tree_view->priv->columns);
5091 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5092 last_column = last_column->prev)
5095 for (first_column = g_list_first (tree_view->priv->columns);
5096 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5097 first_column = first_column->next)
5100 for (list = tree_view->priv->columns; list; list = list->next)
5105 column = list->data;
5107 if (! column->visible)
5110 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5111 pspp_sheet_view_column_cell_get_size (column,
5113 &tmp_width, &tmp_height);
5115 tmp_height += vertical_separator;
5116 height = MAX (height, tmp_height);
5118 tmp_width = tmp_width + horizontal_separator;
5120 if (draw_vgrid_lines)
5122 if (list->data == first_column || list->data == last_column)
5123 tmp_width += grid_line_width / 2.0;
5125 tmp_width += grid_line_width;
5128 if (tmp_width > column->requested_width)
5129 column->requested_width = tmp_width;
5132 if (draw_hgrid_lines)
5133 height += grid_line_width;
5135 tree_view->priv->post_validation_flag = TRUE;
5141 validate_visible_area (PsppSheetView *tree_view)
5143 GtkTreePath *path = NULL;
5144 GtkTreePath *above_path = NULL;
5147 gboolean size_changed = FALSE;
5149 gint area_above = 0;
5150 gint area_below = 0;
5151 GtkAllocation allocation;
5153 if (tree_view->priv->row_count == 0)
5156 if (tree_view->priv->scroll_to_path == NULL)
5159 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5161 total_height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5163 if (total_height == 0)
5166 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5169 /* we are going to scroll, and will update dy */
5170 _pspp_sheet_view_find_node (tree_view, path, &node);
5171 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5173 if (tree_view->priv->scroll_to_use_align)
5175 gint height = ROW_HEIGHT (tree_view);
5176 area_above = (total_height - height) *
5177 tree_view->priv->scroll_to_row_align;
5178 area_below = total_height - area_above - height;
5179 area_above = MAX (area_above, 0);
5180 area_below = MAX (area_below, 0);
5185 * 1) row not visible
5189 gint height = ROW_HEIGHT (tree_view);
5191 dy = pspp_sheet_view_node_find_offset (tree_view, node);
5193 if (dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5194 dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5195 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5197 /* row visible: keep the row at the same position */
5198 area_above = dy - gtk_adjustment_get_value (tree_view->priv->vadjustment);
5199 area_below = (gtk_adjustment_get_value (tree_view->priv->vadjustment) +
5200 gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5205 /* row not visible */
5207 && dy + height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5209 /* row at the beginning -- fixed */
5211 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment)
5212 - area_above - height;
5214 else if (dy >= (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5215 gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5217 /* row at the end -- fixed */
5218 area_above = dy - (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5219 gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
5220 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) -
5221 area_above - height;
5225 area_above = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - height;
5231 /* row somewhere in the middle, bring it to the top
5235 area_below = total_height - height;
5241 /* the scroll to isn't valid; ignore it.
5244 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5245 tree_view->priv->scroll_to_path = NULL;
5249 above_path = gtk_tree_path_copy (path);
5251 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5252 * backwards is much slower then forward, as there is no iter_prev function.
5253 * We go forwards first in case we run out of tree. Then we go backwards to
5256 while (node >= 0 && area_below > 0)
5258 gboolean done = FALSE;
5261 node = pspp_sheet_view_node_next (tree_view, node);
5264 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5266 gtk_tree_path_next (path);
5269 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5279 area_below -= ROW_HEIGHT (tree_view);
5281 gtk_tree_path_free (path);
5283 /* If we ran out of tree, and have extra area_below left, we need to add it
5286 area_above += area_below;
5288 _pspp_sheet_view_find_node (tree_view, above_path, &node);
5290 /* We walk backwards */
5291 while (area_above > 0)
5293 node = pspp_sheet_view_node_prev (tree_view, node);
5295 /* Always find the new path in the tree. We cannot just assume
5296 * a gtk_tree_path_prev() is enough here, as there might be children
5297 * in between this node and the previous sibling node. If this
5298 * appears to be a performance hotspot in profiles, we can look into
5299 * intrigate logic for keeping path, node and iter in sync like
5300 * we do for forward walks. (Which will be hard because of the lacking
5307 gtk_tree_path_free (above_path);
5308 above_path = _pspp_sheet_view_find_path (tree_view, node);
5310 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5312 area_above -= ROW_HEIGHT (tree_view);
5315 /* set the dy here to scroll to the path,
5316 * and sync the top row accordingly
5318 pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5319 pspp_sheet_view_top_row_to_dy (tree_view);
5321 /* update width/height and queue a resize */
5324 GtkRequisition requisition;
5326 /* We temporarily guess a size, under the assumption that it will be the
5327 * same when we get our next size_allocate. If we don't do this, we'll be
5328 * in an inconsistent state if we call top_row_to_dy. */
5330 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5331 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5332 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5333 gtk_adjustment_changed (tree_view->priv->hadjustment);
5334 gtk_adjustment_changed (tree_view->priv->vadjustment);
5335 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5338 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5339 tree_view->priv->scroll_to_path = NULL;
5342 gtk_tree_path_free (above_path);
5344 if (tree_view->priv->scroll_to_column)
5346 tree_view->priv->scroll_to_column = NULL;
5348 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5352 initialize_fixed_height_mode (PsppSheetView *tree_view)
5354 if (!tree_view->priv->row_count)
5357 if (tree_view->priv->fixed_height_set)
5360 if (tree_view->priv->fixed_height < 0)
5367 path = _pspp_sheet_view_find_path (tree_view, node);
5368 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5370 tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5372 gtk_tree_path_free (path);
5374 g_object_notify (G_OBJECT (tree_view), "fixed-height");
5378 /* Our strategy for finding nodes to validate is a little convoluted. We find
5379 * the left-most uninvalidated node. We then try walking right, validating
5380 * nodes. Once we find a valid node, we repeat the previous process of finding
5381 * the first invalid node.
5385 validate_rows_handler (PsppSheetView *tree_view)
5387 initialize_fixed_height_mode (tree_view);
5388 if (tree_view->priv->validate_rows_timer)
5390 g_source_remove (tree_view->priv->validate_rows_timer);
5391 tree_view->priv->validate_rows_timer = 0;
5398 do_presize_handler (PsppSheetView *tree_view)
5400 GtkRequisition requisition;
5402 validate_visible_area (tree_view);
5403 tree_view->priv->presize_handler_timer = 0;
5405 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5408 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5410 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5411 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5412 gtk_adjustment_changed (tree_view->priv->hadjustment);
5413 gtk_adjustment_changed (tree_view->priv->vadjustment);
5414 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5420 presize_handler_callback (gpointer data)
5422 do_presize_handler (PSPP_SHEET_VIEW (data));
5428 install_presize_handler (PsppSheetView *tree_view)
5430 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5433 if (! tree_view->priv->presize_handler_timer)
5435 tree_view->priv->presize_handler_timer =
5436 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5438 if (! tree_view->priv->validate_rows_timer)
5440 tree_view->priv->validate_rows_timer =
5441 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5446 scroll_sync_handler (PsppSheetView *tree_view)
5448 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5449 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5450 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5451 pspp_sheet_view_top_row_to_dy (tree_view);
5453 pspp_sheet_view_dy_to_top_row (tree_view);
5455 tree_view->priv->scroll_sync_timer = 0;
5461 install_scroll_sync_handler (PsppSheetView *tree_view)
5463 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5466 if (!tree_view->priv->scroll_sync_timer)
5468 tree_view->priv->scroll_sync_timer =
5469 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5474 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5478 gtk_tree_row_reference_free (tree_view->priv->top_row);
5482 tree_view->priv->top_row = NULL;
5483 tree_view->priv->top_row_dy = 0;
5487 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5488 tree_view->priv->top_row_dy = offset;
5492 /* Always call this iff dy is in the visible range. If the tree is empty, then
5493 * it's set to be NULL, and top_row_dy is 0;
5496 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5502 if (tree_view->priv->row_count == 0)
5504 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5508 offset = pspp_sheet_view_find_offset (tree_view,
5509 tree_view->priv->dy,
5514 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5518 path = _pspp_sheet_view_find_path (tree_view, node);
5519 pspp_sheet_view_set_top_row (tree_view, path, offset);
5520 gtk_tree_path_free (path);
5526 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5532 /* Avoid recursive calls */
5533 if (tree_view->priv->in_top_row_to_dy)
5536 if (tree_view->priv->top_row)
5537 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5544 _pspp_sheet_view_find_node (tree_view, path, &node);
5547 gtk_tree_path_free (path);
5551 /* keep dy and set new toprow */
5552 gtk_tree_row_reference_free (tree_view->priv->top_row);
5553 tree_view->priv->top_row = NULL;
5554 tree_view->priv->top_row_dy = 0;
5555 /* DO NOT install the idle handler */
5556 pspp_sheet_view_dy_to_top_row (tree_view);
5560 if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5562 /* new top row -- do NOT install the idle handler */
5563 pspp_sheet_view_dy_to_top_row (tree_view);
5567 new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5568 new_dy += tree_view->priv->top_row_dy;
5570 if (new_dy + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
5571 new_dy = tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
5573 new_dy = MAX (0, new_dy);
5575 tree_view->priv->in_top_row_to_dy = TRUE;
5576 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5577 tree_view->priv->in_top_row_to_dy = FALSE;
5582 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5584 install_presize_handler (tree_view);
5590 set_source_row (GdkDragContext *context,
5591 GtkTreeModel *model,
5592 GtkTreePath *source_row)
5594 g_object_set_data_full (G_OBJECT (context),
5595 "gtk-tree-view-source-row",
5596 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5597 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5601 get_source_row (GdkDragContext *context)
5603 GtkTreeRowReference *ref =
5604 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5607 return gtk_tree_row_reference_get_path (ref);
5614 GtkTreeRowReference *dest_row;
5615 guint path_down_mode : 1;
5616 guint empty_view_drop : 1;
5617 guint drop_append_mode : 1;
5622 dest_row_free (gpointer data)
5624 DestRow *dr = (DestRow *)data;
5626 gtk_tree_row_reference_free (dr->dest_row);
5627 g_slice_free (DestRow, dr);
5631 set_dest_row (GdkDragContext *context,
5632 GtkTreeModel *model,
5633 GtkTreePath *dest_row,
5634 gboolean path_down_mode,
5635 gboolean empty_view_drop,
5636 gboolean drop_append_mode)
5642 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5647 dr = g_slice_new (DestRow);
5649 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5650 dr->path_down_mode = path_down_mode != FALSE;
5651 dr->empty_view_drop = empty_view_drop != FALSE;
5652 dr->drop_append_mode = drop_append_mode != FALSE;
5654 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5655 dr, (GDestroyNotify) dest_row_free);
5659 get_dest_row (GdkDragContext *context,
5660 gboolean *path_down_mode)
5663 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5667 GtkTreePath *path = NULL;
5670 *path_down_mode = dr->path_down_mode;
5673 path = gtk_tree_row_reference_get_path (dr->dest_row);
5674 else if (dr->empty_view_drop)
5675 path = gtk_tree_path_new_from_indices (0, -1);
5679 if (path && dr->drop_append_mode)
5680 gtk_tree_path_next (path);
5688 /* Get/set whether drag_motion requested the drag data and
5689 * drag_data_received should thus not actually insert the data,
5690 * since the data doesn't result from a drop.
5693 set_status_pending (GdkDragContext *context,
5694 GdkDragAction suggested_action)
5696 g_object_set_data (G_OBJECT (context),
5697 "gtk-tree-view-status-pending",
5698 GINT_TO_POINTER (suggested_action));
5701 static GdkDragAction
5702 get_status_pending (GdkDragContext *context)
5704 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5705 "gtk-tree-view-status-pending"));
5708 static TreeViewDragInfo*
5709 get_info (PsppSheetView *tree_view)
5711 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5715 destroy_info (TreeViewDragInfo *di)
5717 g_slice_free (TreeViewDragInfo, di);
5720 static TreeViewDragInfo*
5721 ensure_info (PsppSheetView *tree_view)
5723 TreeViewDragInfo *di;
5725 di = get_info (tree_view);
5729 di = g_slice_new0 (TreeViewDragInfo);
5731 g_object_set_data_full (G_OBJECT (tree_view),
5732 "gtk-tree-view-drag-info",
5734 (GDestroyNotify) destroy_info);
5741 remove_info (PsppSheetView *tree_view)
5743 g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5748 drag_scan_timeout (gpointer data)
5750 PsppSheetView *tree_view;
5752 GdkModifierType state;
5753 GtkTreePath *path = NULL;
5754 PsppSheetViewColumn *column = NULL;
5755 GdkRectangle visible_rect;
5757 GDK_THREADS_ENTER ();
5759 tree_view = PSPP_SHEET_VIEW (data);
5761 gdk_window_get_pointer (tree_view->priv->bin_window,
5764 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5766 /* See if we are near the edge. */
5767 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5768 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5769 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5770 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5772 pspp_sheet_view_get_path_at_pos (tree_view,
5773 tree_view->priv->bin_window,
5782 pspp_sheet_view_scroll_to_cell (tree_view,
5788 gtk_tree_path_free (path);
5792 GDK_THREADS_LEAVE ();
5799 add_scroll_timeout (PsppSheetView *tree_view)
5801 if (tree_view->priv->scroll_timeout == 0)
5803 tree_view->priv->scroll_timeout =
5804 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5809 remove_scroll_timeout (PsppSheetView *tree_view)
5811 if (tree_view->priv->scroll_timeout != 0)
5813 g_source_remove (tree_view->priv->scroll_timeout);
5814 tree_view->priv->scroll_timeout = 0;
5819 check_model_dnd (GtkTreeModel *model,
5820 GType required_iface,
5821 const gchar *signal)
5823 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5825 g_warning ("You must override the default '%s' handler "
5826 "on PsppSheetView when using models that don't support "
5827 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5828 "is to connect to '%s' and call "
5829 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5830 "the default handler from running. Look at the source code "
5831 "for the default handler in gtktreeview.c to get an idea what "
5832 "your handler should do. (gtktreeview.c is in the GTK source "
5833 "code.) If you're using GTK from a language other than C, "
5834 "there may be a more natural way to override default handlers, e.g. via derivation.",
5835 signal, g_type_name (required_iface), signal);
5843 scroll_row_timeout (gpointer data)
5845 PsppSheetView *tree_view = data;
5847 pspp_sheet_view_horizontal_autoscroll (tree_view);
5848 pspp_sheet_view_vertical_autoscroll (tree_view);
5850 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5851 pspp_sheet_view_update_rubber_band (tree_view);
5856 /* Returns TRUE if event should not be propagated to parent widgets */
5858 set_destination_row (PsppSheetView *tree_view,
5859 GdkDragContext *context,
5860 /* coordinates relative to the widget */
5863 GdkDragAction *suggested_action,
5866 GtkTreePath *path = NULL;
5867 PsppSheetViewDropPosition pos;
5868 PsppSheetViewDropPosition old_pos;
5869 TreeViewDragInfo *di;
5871 GtkTreePath *old_dest_path = NULL;
5872 gboolean can_drop = FALSE;
5874 *suggested_action = 0;
5877 widget = GTK_WIDGET (tree_view);
5879 di = get_info (tree_view);
5881 if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5883 /* someone unset us as a drag dest, note that if
5884 * we return FALSE drag_leave isn't called
5887 pspp_sheet_view_set_drag_dest_row (tree_view,
5889 PSPP_SHEET_VIEW_DROP_BEFORE);
5891 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5893 return FALSE; /* no longer a drop site */
5896 *target = gtk_drag_dest_find_target (widget, context,
5897 gtk_drag_dest_get_target_list (widget));
5898 if (*target == GDK_NONE)
5903 if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
5909 GtkTreeModel *model;
5911 /* the row got dropped on empty space, let's setup a special case
5915 gtk_tree_path_free (path);
5917 model = pspp_sheet_view_get_model (tree_view);
5919 n_children = gtk_tree_model_iter_n_children (model, NULL);
5922 pos = PSPP_SHEET_VIEW_DROP_AFTER;
5923 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
5927 pos = PSPP_SHEET_VIEW_DROP_BEFORE;
5928 path = gtk_tree_path_new_from_indices (0, -1);
5938 /* If we left the current row's "open" zone, unset the timeout for
5941 pspp_sheet_view_get_drag_dest_row (tree_view,
5946 gtk_tree_path_free (old_dest_path);
5948 if (TRUE /* FIXME if the location droppable predicate */)
5956 GtkWidget *source_widget;
5958 *suggested_action = gdk_drag_context_get_suggested_action (context);
5959 source_widget = gtk_drag_get_source_widget (context);
5961 if (source_widget == widget)
5963 /* Default to MOVE, unless the user has
5964 * pressed ctrl or shift to affect available actions
5966 if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
5967 *suggested_action = GDK_ACTION_MOVE;
5970 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5975 /* can't drop here */
5976 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5978 PSPP_SHEET_VIEW_DROP_BEFORE);
5982 gtk_tree_path_free (path);
5988 get_logical_dest_row (PsppSheetView *tree_view,
5989 gboolean *path_down_mode,
5990 gboolean *drop_append_mode)
5992 /* adjust path to point to the row the drop goes in front of */
5993 GtkTreePath *path = NULL;
5994 PsppSheetViewDropPosition pos;
5996 g_return_val_if_fail (path_down_mode != NULL, NULL);
5997 g_return_val_if_fail (drop_append_mode != NULL, NULL);
5999 *path_down_mode = FALSE;
6000 *drop_append_mode = 0;
6002 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6007 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
6009 else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
6010 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
6011 *path_down_mode = TRUE;
6015 GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
6017 g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
6019 if (!gtk_tree_model_get_iter (model, &iter, path) ||
6020 !gtk_tree_model_iter_next (model, &iter))
6021 *drop_append_mode = 1;
6024 *drop_append_mode = 0;
6025 gtk_tree_path_next (path);
6033 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
6034 GdkEventMotion *event)
6036 GtkWidget *widget = GTK_WIDGET (tree_view);
6037 GdkDragContext *context;
6038 TreeViewDragInfo *di;
6039 GtkTreePath *path = NULL;
6041 gint cell_x, cell_y;
6042 GtkTreeModel *model;
6043 gboolean retval = FALSE;
6045 di = get_info (tree_view);
6047 if (di == NULL || !di->source_set)
6050 if (tree_view->priv->pressed_button < 0)
6053 if (!gtk_drag_check_threshold (widget,
6054 tree_view->priv->press_start_x,
6055 tree_view->priv->press_start_y,
6056 event->x, event->y))
6059 model = pspp_sheet_view_get_model (tree_view);
6064 button = tree_view->priv->pressed_button;
6065 tree_view->priv->pressed_button = -1;
6067 pspp_sheet_view_get_path_at_pos (tree_view,
6068 tree_view->priv->press_start_x,
6069 tree_view->priv->press_start_y,
6078 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6079 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6083 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6086 /* Now we can begin the drag */
6090 context = gtk_drag_begin (widget,
6091 gtk_drag_source_get_target_list (widget),
6096 set_source_row (context, model, path);
6100 gtk_tree_path_free (path);
6108 pspp_sheet_view_drag_begin (GtkWidget *widget,
6109 GdkDragContext *context)
6112 PsppSheetView *tree_view;
6113 GtkTreePath *path = NULL;
6114 gint cell_x, cell_y;
6116 TreeViewDragInfo *di;
6118 tree_view = PSPP_SHEET_VIEW (widget);
6120 /* if the user uses a custom DND source impl, we don't set the icon here */
6121 di = get_info (tree_view);
6123 if (di == NULL || !di->source_set)
6126 pspp_sheet_view_get_path_at_pos (tree_view,
6127 tree_view->priv->press_start_x,
6128 tree_view->priv->press_start_y,
6134 g_return_if_fail (path != NULL);
6136 row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6139 gtk_drag_set_icon_pixmap (context,
6140 gdk_drawable_get_colormap (row_pix),
6143 /* the + 1 is for the black border in the icon */
6144 tree_view->priv->press_start_x + 1,
6147 g_object_unref (row_pix);
6148 gtk_tree_path_free (path);
6154 pspp_sheet_view_drag_end (GtkWidget *widget,
6155 GdkDragContext *context)
6160 /* Default signal implementations for the drag signals */
6162 pspp_sheet_view_drag_data_get (GtkWidget *widget,
6163 GdkDragContext *context,
6164 GtkSelectionData *selection_data,
6168 PsppSheetView *tree_view;
6169 GtkTreeModel *model;
6170 TreeViewDragInfo *di;
6171 GtkTreePath *source_row;
6173 tree_view = PSPP_SHEET_VIEW (widget);
6175 model = pspp_sheet_view_get_model (tree_view);
6180 di = get_info (PSPP_SHEET_VIEW (widget));
6185 source_row = get_source_row (context);
6187 if (source_row == NULL)
6190 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6191 * any model; for DragSource models there are some other targets
6195 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6196 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6201 /* If drag_data_get does nothing, try providing row data. */
6202 if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6204 gtk_tree_set_row_drag_data (selection_data,
6210 gtk_tree_path_free (source_row);
6215 pspp_sheet_view_drag_data_delete (GtkWidget *widget,
6216 GdkDragContext *context)
6218 TreeViewDragInfo *di;
6219 GtkTreeModel *model;
6220 PsppSheetView *tree_view;
6221 GtkTreePath *source_row;
6223 tree_view = PSPP_SHEET_VIEW (widget);
6224 model = pspp_sheet_view_get_model (tree_view);
6226 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6229 di = get_info (tree_view);
6234 source_row = get_source_row (context);
6236 if (source_row == NULL)
6239 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6242 gtk_tree_path_free (source_row);
6244 set_source_row (context, NULL, NULL);
6248 pspp_sheet_view_drag_leave (GtkWidget *widget,
6249 GdkDragContext *context,
6252 /* unset any highlight row */
6253 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6255 PSPP_SHEET_VIEW_DROP_BEFORE);
6257 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6262 pspp_sheet_view_drag_motion (GtkWidget *widget,
6263 GdkDragContext *context,
6264 /* coordinates relative to the widget */
6270 GtkTreePath *path = NULL;
6271 PsppSheetViewDropPosition pos;
6272 PsppSheetView *tree_view;
6273 GdkDragAction suggested_action = 0;
6276 tree_view = PSPP_SHEET_VIEW (widget);
6278 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6281 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6283 /* we only know this *after* set_desination_row */
6284 empty = tree_view->priv->empty_view_drop;
6286 if (path == NULL && !empty)
6288 /* Can't drop here. */
6289 gdk_drag_status (context, 0, time);
6293 if (tree_view->priv->open_dest_timeout == 0 &&
6294 (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6295 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6301 add_scroll_timeout (tree_view);
6304 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6306 /* Request data so we can use the source row when
6307 * determining whether to accept the drop
6309 set_status_pending (context, suggested_action);
6310 gtk_drag_get_data (widget, context, target, time);
6314 set_status_pending (context, 0);
6315 gdk_drag_status (context, suggested_action, time);
6320 gtk_tree_path_free (path);
6327 pspp_sheet_view_drag_drop (GtkWidget *widget,
6328 GdkDragContext *context,
6329 /* coordinates relative to the widget */
6334 PsppSheetView *tree_view;
6336 GdkDragAction suggested_action = 0;
6337 GdkAtom target = GDK_NONE;
6338 TreeViewDragInfo *di;
6339 GtkTreeModel *model;
6340 gboolean path_down_mode;
6341 gboolean drop_append_mode;
6343 tree_view = PSPP_SHEET_VIEW (widget);
6345 model = pspp_sheet_view_get_model (tree_view);
6347 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6349 di = get_info (tree_view);
6354 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6357 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6360 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6362 if (target != GDK_NONE && path != NULL)
6364 /* in case a motion had requested drag data, change things so we
6365 * treat drag data receives as a drop.
6367 set_status_pending (context, 0);
6368 set_dest_row (context, model, path,
6369 path_down_mode, tree_view->priv->empty_view_drop,
6374 gtk_tree_path_free (path);
6376 /* Unset this thing */
6377 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6379 PSPP_SHEET_VIEW_DROP_BEFORE);
6381 if (target != GDK_NONE)
6383 gtk_drag_get_data (widget, context, target, time);
6391 pspp_sheet_view_drag_data_received (GtkWidget *widget,
6392 GdkDragContext *context,
6393 /* coordinates relative to the widget */
6396 GtkSelectionData *selection_data,
6401 TreeViewDragInfo *di;
6402 gboolean accepted = FALSE;
6403 GtkTreeModel *model;
6404 PsppSheetView *tree_view;
6405 GtkTreePath *dest_row;
6406 GdkDragAction suggested_action;
6407 gboolean path_down_mode;
6408 gboolean drop_append_mode;
6410 tree_view = PSPP_SHEET_VIEW (widget);
6412 model = pspp_sheet_view_get_model (tree_view);
6414 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6417 di = get_info (tree_view);
6422 suggested_action = get_status_pending (context);
6424 if (suggested_action)
6426 /* We are getting this data due to a request in drag_motion,
6427 * rather than due to a request in drag_drop, so we are just
6428 * supposed to call drag_status, not actually paste in the
6431 path = get_logical_dest_row (tree_view, &path_down_mode,
6435 suggested_action = 0;
6436 else if (path_down_mode)
6437 gtk_tree_path_down (path);
6439 if (suggested_action)
6441 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6447 path_down_mode = FALSE;
6448 gtk_tree_path_up (path);
6450 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6453 suggested_action = 0;
6456 suggested_action = 0;
6460 gdk_drag_status (context, suggested_action, time);
6463 gtk_tree_path_free (path);
6465 /* If you can't drop, remove user drop indicator until the next motion */
6466 if (suggested_action == 0)
6467 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6469 PSPP_SHEET_VIEW_DROP_BEFORE);
6474 dest_row = get_dest_row (context, &path_down_mode);
6476 if (dest_row == NULL)
6479 if (gtk_selection_data_get_length (selection_data) >= 0)
6483 gtk_tree_path_down (dest_row);
6484 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6485 dest_row, selection_data))
6486 gtk_tree_path_up (dest_row);
6490 if (gtk_selection_data_get_length (selection_data) >= 0)
6492 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6498 gtk_drag_finish (context,
6500 (gdk_drag_context_get_actions (context) == GDK_ACTION_MOVE),
6503 if (gtk_tree_path_get_depth (dest_row) == 1
6504 && gtk_tree_path_get_indices (dest_row)[0] == 0)
6506 /* special special case drag to "0", scroll to first item */
6507 if (!tree_view->priv->scroll_to_path)
6508 pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6511 gtk_tree_path_free (dest_row);
6514 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6519 /* GtkContainer Methods
6524 pspp_sheet_view_remove (GtkContainer *container,
6527 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6528 PsppSheetViewChild *child = NULL;
6531 tmp_list = tree_view->priv->children;
6534 child = tmp_list->data;
6535 if (child->widget == widget)
6537 gtk_widget_unparent (widget);
6539 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6540 g_list_free_1 (tmp_list);
6541 g_slice_free (PsppSheetViewChild, child);
6545 tmp_list = tmp_list->next;
6548 tmp_list = tree_view->priv->columns;
6552 PsppSheetViewColumn *column;
6553 column = tmp_list->data;
6554 if (column->button == widget)
6556 gtk_widget_unparent (widget);
6559 tmp_list = tmp_list->next;
6564 pspp_sheet_view_forall (GtkContainer *container,
6565 gboolean include_internals,
6566 GtkCallback callback,
6567 gpointer callback_data)
6569 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6570 PsppSheetViewChild *child = NULL;
6571 PsppSheetViewColumn *column;
6574 tmp_list = tree_view->priv->children;
6577 child = tmp_list->data;
6578 tmp_list = tmp_list->next;
6580 (* callback) (child->widget, callback_data);
6582 if (include_internals == FALSE)
6585 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6587 column = tmp_list->data;
6590 (* callback) (column->button, callback_data);
6594 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6595 * cells. If so we draw one big row-spanning focus rectangle.
6598 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6602 if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6603 return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6605 for (list = tree_view->priv->columns; list; list = list->next)
6607 if (!((PsppSheetViewColumn *)list->data)->visible)
6609 if (_pspp_sheet_view_column_count_special_cells (list->data))
6617 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6618 PsppSheetViewColumn *focus_column,
6619 gboolean clamp_column_visible)
6621 g_return_if_fail (focus_column != NULL);
6623 tree_view->priv->focus_column = focus_column;
6625 if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view)) != focus_column->button)
6626 gtk_widget_grab_focus (focus_column->button);
6628 if (clamp_column_visible)
6629 pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6632 /* Returns TRUE if the focus is within the headers, after the focus operation is
6636 pspp_sheet_view_header_focus (PsppSheetView *tree_view,
6637 GtkDirectionType dir,
6638 gboolean clamp_column_visible)
6640 GtkWidget *focus_child;
6641 PsppSheetViewColumn *focus_column;
6642 GList *last_column, *first_column;
6646 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6649 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
6651 first_column = tree_view->priv->columns;
6652 while (first_column)
6654 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6656 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6658 first_column = first_column->next;
6661 /* No headers are visible, or are focusable. We can't focus in or out.
6663 if (first_column == NULL)
6666 last_column = g_list_last (tree_view->priv->columns);
6669 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6671 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6673 last_column = last_column->prev;
6677 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6681 case GTK_DIR_TAB_BACKWARD:
6682 case GTK_DIR_TAB_FORWARD:
6685 if (focus_child == NULL)
6687 if (tree_view->priv->focus_column != NULL &&
6688 pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6689 focus_column = tree_view->priv->focus_column;
6691 focus_column = first_column->data;
6692 pspp_sheet_view_focus_column (tree_view, focus_column,
6693 clamp_column_visible);
6700 if (focus_child == NULL)
6702 if (tree_view->priv->focus_column != NULL)
6703 focus_column = tree_view->priv->focus_column;
6704 else if (dir == GTK_DIR_LEFT)
6705 focus_column = last_column->data;
6707 focus_column = first_column->data;
6708 pspp_sheet_view_focus_column (tree_view, focus_column,
6709 clamp_column_visible);
6713 if (gtk_widget_child_focus (focus_child, dir))
6715 /* The focus moves inside the button. */
6716 /* This is probably a great example of bad UI */
6717 if (clamp_column_visible)
6718 pspp_sheet_view_clamp_column_visible (tree_view,
6719 tree_view->priv->focus_column,
6724 /* We need to move the focus among the row of buttons. */
6725 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6726 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6729 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6730 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6732 gtk_widget_error_bell (GTK_WIDGET (tree_view));
6738 PsppSheetViewColumn *column;
6740 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6741 tmp_list = tmp_list->next;
6743 tmp_list = tmp_list->prev;
6745 if (tmp_list == NULL)
6747 g_warning ("Internal button not found");
6750 column = tmp_list->data;
6751 if (column->visible &&
6752 pspp_sheet_view_column_can_focus (column))
6756 pspp_sheet_view_focus_column (tree_view, column,
6757 clamp_column_visible);
6765 g_assert_not_reached ();
6772 /* This function returns in 'path' the first focusable path, if the given path
6773 * is already focusable, it's the returned one.
6777 search_first_focusable_path (PsppSheetView *tree_view,
6779 gboolean search_forward,
6782 /* XXX this function is trivial given that the sheetview doesn't support
6786 if (!path || !*path)
6789 _pspp_sheet_view_find_node (tree_view, *path, &node);
6797 return (*path != NULL);
6801 pspp_sheet_view_focus (GtkWidget *widget,
6802 GtkDirectionType direction)
6804 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6805 GtkContainer *container = GTK_CONTAINER (widget);
6806 GtkWidget *focus_child;
6808 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6811 focus_child = gtk_container_get_focus_child (container);
6813 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6814 /* Case 1. Headers currently have focus. */
6821 pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6823 case GTK_DIR_TAB_BACKWARD:
6826 case GTK_DIR_TAB_FORWARD:
6828 gtk_widget_grab_focus (widget);
6831 g_assert_not_reached ();
6836 /* Case 2. We don't have focus at all. */
6837 if (!gtk_widget_has_focus (widget))
6839 if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6840 gtk_widget_grab_focus (widget);
6844 /* Case 3. We have focus already. */
6845 if (direction == GTK_DIR_TAB_BACKWARD)
6846 return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6847 else if (direction == GTK_DIR_TAB_FORWARD)
6850 /* Other directions caught by the keybindings */
6851 gtk_widget_grab_focus (widget);
6856 pspp_sheet_view_grab_focus (GtkWidget *widget)
6858 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6860 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6864 pspp_sheet_view_style_updated (GtkWidget *widget)
6866 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6868 PsppSheetViewColumn *column;
6869 GtkStyleContext *context;
6871 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->style_updated (widget);
6873 if (gtk_widget_get_realized (widget))
6875 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
6876 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
6877 gtk_style_context_set_background (context, tree_view->priv->header_window);
6878 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6881 gtk_widget_style_get (widget,
6882 "expander-size", &tree_view->priv->expander_size,
6884 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6886 for (list = tree_view->priv->columns; list; list = list->next)
6888 column = list->data;
6889 _pspp_sheet_view_column_cell_set_dirty (column);
6892 tree_view->priv->fixed_height = -1;
6894 /* Invalidate cached button style. */
6895 if (tree_view->priv->button_style)
6897 g_object_unref (tree_view->priv->button_style);
6898 tree_view->priv->button_style = NULL;
6901 gtk_widget_queue_resize (widget);
6906 pspp_sheet_view_set_focus_child (GtkContainer *container,
6909 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6912 for (list = tree_view->priv->columns; list; list = list->next)
6914 if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
6916 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
6921 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
6925 pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
6926 GtkAdjustment *hadj,
6927 GtkAdjustment *vadj)
6929 gboolean need_adjust = FALSE;
6931 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
6934 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
6936 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6938 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
6940 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6942 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
6944 g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
6945 pspp_sheet_view_adjustment_changed,
6947 g_object_unref (tree_view->priv->hadjustment);
6950 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
6952 g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
6953 pspp_sheet_view_adjustment_changed,
6955 g_object_unref (tree_view->priv->vadjustment);
6958 if (tree_view->priv->hadjustment != hadj)
6960 tree_view->priv->hadjustment = hadj;
6961 g_object_ref_sink (tree_view->priv->hadjustment);
6963 g_signal_connect (tree_view->priv->hadjustment, "value-changed",
6964 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6969 if (tree_view->priv->vadjustment != vadj)
6971 tree_view->priv->vadjustment = vadj;
6972 g_object_ref_sink (tree_view->priv->vadjustment);
6974 g_signal_connect (tree_view->priv->vadjustment, "value-changed",
6975 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6981 pspp_sheet_view_adjustment_changed (NULL, tree_view);
6986 pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
6987 GtkMovementStep step,
6990 PsppSheetSelectMode mode;
6991 GdkModifierType state;
6993 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
6994 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
6995 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
6996 step == GTK_MOVEMENT_DISPLAY_LINES ||
6997 step == GTK_MOVEMENT_PAGES ||
6998 step == GTK_MOVEMENT_BUFFER_ENDS ||
6999 step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
7001 if (tree_view->priv->row_count == 0)
7003 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7006 pspp_sheet_view_stop_editing (tree_view, FALSE);
7007 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7008 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7011 if (gtk_get_current_event_state (&state))
7013 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7014 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
7015 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
7016 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
7018 /* else we assume not pressed */
7022 case GTK_MOVEMENT_LOGICAL_POSITIONS:
7023 pspp_sheet_view_move_cursor_tab (tree_view, count);
7025 case GTK_MOVEMENT_VISUAL_POSITIONS:
7026 pspp_sheet_view_move_cursor_left_right (tree_view, count, mode);
7028 case GTK_MOVEMENT_DISPLAY_LINES:
7029 pspp_sheet_view_move_cursor_up_down (tree_view, count, mode);
7031 case GTK_MOVEMENT_PAGES:
7032 pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode);
7034 case GTK_MOVEMENT_BUFFER_ENDS:
7035 pspp_sheet_view_move_cursor_start_end (tree_view, count, mode);
7037 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7038 pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode);
7041 g_assert_not_reached ();
7048 pspp_sheet_view_put (PsppSheetView *tree_view,
7049 GtkWidget *child_widget,
7051 PsppSheetViewColumn *column)
7053 PsppSheetViewChild *child;
7055 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7056 g_return_if_fail (GTK_IS_WIDGET (child_widget));
7058 child = g_slice_new (PsppSheetViewChild);
7060 child->widget = child_widget;
7061 _pspp_sheet_view_find_node (tree_view, path, &child->node);
7062 if (child->node < 0)
7064 g_assert_not_reached ();
7066 child->column = column;
7068 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7070 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7071 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7073 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7076 /* TreeModel Callbacks
7080 pspp_sheet_view_row_changed (GtkTreeModel *model,
7085 PsppSheetView *tree_view = (PsppSheetView *)data;
7087 gboolean free_path = FALSE;
7088 GtkTreePath *cursor_path;
7090 g_return_if_fail (path != NULL || iter != NULL);
7092 if (tree_view->priv->cursor != NULL)
7093 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7097 if (tree_view->priv->edited_column &&
7098 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7099 pspp_sheet_view_stop_editing (tree_view, TRUE);
7101 if (cursor_path != NULL)
7102 gtk_tree_path_free (cursor_path);
7106 path = gtk_tree_model_get_path (model, iter);
7109 else if (iter == NULL)
7110 gtk_tree_model_get_iter (model, iter, path);
7112 _pspp_sheet_view_find_node (tree_view,
7118 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7119 pspp_sheet_view_node_queue_redraw (tree_view, node);
7123 gtk_tree_path_free (path);
7127 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7132 PsppSheetView *tree_view = (PsppSheetView *) data;
7135 gint height = tree_view->priv->fixed_height;
7136 gboolean free_path = FALSE;
7137 gboolean node_visible = TRUE;
7139 g_return_if_fail (path != NULL || iter != NULL);
7143 path = gtk_tree_model_get_path (model, iter);
7146 else if (iter == NULL)
7147 gtk_tree_model_get_iter (model, iter, path);
7149 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7151 /* Update all row-references */
7152 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7153 indices = gtk_tree_path_get_indices (path);
7154 tmpnode = indices[0];
7156 range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7160 if (node_visible && node_is_visible (tree_view, tmpnode))
7161 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7163 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7166 install_presize_handler (tree_view);
7168 gtk_tree_path_free (path);
7172 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7176 PsppSheetView *tree_view = (PsppSheetView *)data;
7179 g_return_if_fail (path != NULL);
7181 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7183 _pspp_sheet_view_find_node (tree_view, path, &node);
7188 range_tower_delete (tree_view->priv->selected, node, 1);
7190 /* Ensure we don't have a dangling pointer to a dead node */
7191 ensure_unprelighted (tree_view);
7193 /* Cancel editting if we've started */
7194 pspp_sheet_view_stop_editing (tree_view, TRUE);
7196 if (tree_view->priv->destroy_count_func)
7198 gint child_count = 0;
7199 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7202 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7204 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7206 gtk_tree_row_reference_free (tree_view->priv->top_row);
7207 tree_view->priv->top_row = NULL;
7210 install_scroll_sync_handler (tree_view);
7212 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7215 if (helper_data.changed)
7216 g_signal_emit_by_name (tree_view->priv->selection, "changed");
7221 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7222 GtkTreePath *parent,
7227 PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7230 /* XXX need to adjust selection */
7231 len = gtk_tree_model_iter_n_children (model, iter);
7236 gtk_tree_row_reference_reordered (G_OBJECT (data),
7241 if (gtk_tree_path_get_depth (parent) != 0)
7244 if (tree_view->priv->edited_column)
7245 pspp_sheet_view_stop_editing (tree_view, TRUE);
7247 /* we need to be unprelighted */
7248 ensure_unprelighted (tree_view);
7250 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7252 pspp_sheet_view_dy_to_top_row (tree_view);
7256 /* Internal tree functions
7261 pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view,
7262 PsppSheetViewColumn *column,
7266 PsppSheetViewColumn *tmp_column = NULL;
7277 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7280 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7282 list = (rtl ? list->prev : list->next))
7284 tmp_column = list->data;
7286 if (tmp_column == column)
7289 if (tmp_column->visible)
7290 total_width += tmp_column->width;
7293 if (tmp_column != column)
7295 g_warning (G_STRLOC": passed-in column isn't in the tree");
7304 if (column->visible)
7305 *x2 = total_width + column->width;
7307 *x2 = total_width; /* width of 0 */
7311 /* Make sure the node is visible vertically */
7313 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7316 gint node_dy, height;
7317 GtkTreePath *path = NULL;
7319 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7322 /* just return if the node is visible, avoiding a costly expose */
7323 node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7324 height = ROW_HEIGHT (tree_view);
7325 if (node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment)
7326 && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
7327 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
7330 path = _pspp_sheet_view_find_path (tree_view, node);
7333 /* We process updates because we want to clear old selected items when we scroll.
7334 * if this is removed, we get a "selection streak" at the bottom. */
7335 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7336 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7337 gtk_tree_path_free (path);
7342 pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
7343 PsppSheetViewColumn *column,
7344 gboolean focus_to_cell)
7351 x = column->allocation.x;
7352 width = column->allocation.width;
7354 if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7356 /* The column is larger than the horizontal page size. If the
7357 * column has cells which can be focussed individually, then we make
7358 * sure the cell which gets focus is fully visible (if even the
7359 * focus cell is bigger than the page size, we make sure the
7360 * left-hand side of the cell is visible).
7362 * If the column does not have those so-called special cells, we
7363 * make sure the left-hand side of the column is visible.
7366 if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7368 GtkTreePath *cursor_path;
7369 GdkRectangle background_area, cell_area, focus_area;
7371 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7373 pspp_sheet_view_get_cell_area (tree_view,
7374 cursor_path, column, &cell_area);
7375 pspp_sheet_view_get_background_area (tree_view,
7376 cursor_path, column,
7379 gtk_tree_path_free (cursor_path);
7381 _pspp_sheet_view_column_get_focus_area (column,
7387 width = focus_area.width;
7389 if (width < gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7391 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7392 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7393 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7394 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7395 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7399 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7401 gtk_adjustment_get_lower (tree_view->priv->hadjustment),
7402 gtk_adjustment_get_upper (tree_view->priv->hadjustment)
7403 - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)));
7407 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7408 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7409 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7410 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7411 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7416 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7421 path = gtk_tree_path_new ();
7423 gtk_tree_path_append_index (path, node);
7428 _pspp_sheet_view_find_node (PsppSheetView *tree_view,
7432 gint *indices = gtk_tree_path_get_indices (path);
7433 gint depth = gtk_tree_path_get_depth (path);
7436 if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7442 pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
7445 gboolean add_shifted_binding,
7446 GtkMovementStep step,
7450 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7455 if (add_shifted_binding)
7456 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7461 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7464 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7469 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7476 pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view,
7477 PsppSheetViewColumn *column)
7479 PsppSheetViewColumn *left_column;
7480 PsppSheetViewColumn *cur_column = NULL;
7481 PsppSheetViewColumnReorder *reorder;
7486 /* We want to precalculate the motion list such that we know what column slots
7490 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7492 /* First, identify all possible drop spots */
7494 tmp_list = g_list_last (tree_view->priv->columns);
7496 tmp_list = g_list_first (tree_view->priv->columns);
7500 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7501 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7503 if (cur_column->visible == FALSE)
7506 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7507 if (left_column != column && cur_column != column &&
7508 tree_view->priv->column_drop_func &&
7509 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7511 left_column = cur_column;
7514 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7515 reorder->left_column = left_column;
7516 left_column = reorder->right_column = cur_column;
7518 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7521 /* Add the last one */
7522 if (tree_view->priv->column_drop_func == NULL ||
7523 ((left_column != column) &&
7524 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7526 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7527 reorder->left_column = left_column;
7528 reorder->right_column = NULL;
7529 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7532 /* We quickly check to see if it even makes sense to reorder columns. */
7533 /* If there is nothing that can be moved, then we return */
7535 if (tree_view->priv->column_drag_info == NULL)
7538 /* We know there are always 2 slots possbile, as you can always return column. */
7539 /* If that's all there is, return */
7540 if (tree_view->priv->column_drag_info->next == NULL ||
7541 (tree_view->priv->column_drag_info->next->next == NULL &&
7542 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7543 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7545 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7546 g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7547 g_list_free (tree_view->priv->column_drag_info);
7548 tree_view->priv->column_drag_info = NULL;
7551 /* We fill in the ranges for the columns, now that we've isolated them */
7552 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7554 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7556 reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7558 reorder->left_align = left;
7559 if (tmp_list->next != NULL)
7561 g_assert (tmp_list->next->data);
7562 left = reorder->right_align = (reorder->right_column->allocation.x +
7563 reorder->right_column->allocation.width +
7564 ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7568 gint width = gdk_window_get_width (tree_view->priv->header_window);
7569 reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7575 _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view,
7576 PsppSheetViewColumn *column)
7578 GdkEvent *send_event;
7579 GtkAllocation allocation;
7581 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7582 GdkDisplay *display = gdk_screen_get_display (screen);
7584 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7585 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7586 g_return_if_fail (column->button);
7588 pspp_sheet_view_set_column_drag_info (tree_view, column);
7590 if (tree_view->priv->column_drag_info == NULL)
7593 if (tree_view->priv->drag_window == NULL)
7595 GdkWindowAttr attributes;
7596 guint attributes_mask;
7598 attributes.window_type = GDK_WINDOW_CHILD;
7599 attributes.wclass = GDK_INPUT_OUTPUT;
7600 attributes.x = column->allocation.x;
7602 attributes.width = column->allocation.width;
7603 attributes.height = column->allocation.height;
7604 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7605 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7606 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL ;
7608 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7611 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7614 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7615 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7617 gtk_grab_remove (column->button);
7619 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7620 send_event->crossing.send_event = TRUE;
7621 send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button)));
7622 send_event->crossing.subwindow = NULL;
7623 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7624 send_event->crossing.time = GDK_CURRENT_TIME;
7626 gtk_propagate_event (column->button, send_event);
7627 gdk_event_free (send_event);
7629 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7630 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7631 send_event->button.send_event = TRUE;
7632 send_event->button.time = GDK_CURRENT_TIME;
7633 send_event->button.x = -1;
7634 send_event->button.y = -1;
7635 send_event->button.axes = NULL;
7636 send_event->button.state = 0;
7637 send_event->button.button = 1;
7638 send_event->button.device =
7639 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
7641 send_event->button.x_root = 0;
7642 send_event->button.y_root = 0;
7644 gtk_propagate_event (column->button, send_event);
7645 gdk_event_free (send_event);
7647 /* Kids, don't try this at home */
7648 g_object_ref (column->button);
7649 gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7650 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7651 gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7652 g_object_unref (column->button);
7654 tree_view->priv->drag_column_x = column->allocation.x;
7655 allocation = column->allocation;
7657 gtk_widget_size_allocate (column->button, &allocation);
7658 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7660 tree_view->priv->drag_column = column;
7661 gdk_window_show (tree_view->priv->drag_window);
7663 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7665 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7666 while (gtk_events_pending ())
7667 gtk_main_iteration ();
7669 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7670 gdk_pointer_grab (tree_view->priv->drag_window,
7672 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7673 NULL, NULL, GDK_CURRENT_TIME);
7674 gdk_keyboard_grab (tree_view->priv->drag_window,
7680 _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view,
7682 const GdkRectangle *clip_rect)
7685 GtkAllocation allocation;
7687 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7690 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
7692 rect.width = MAX (tree_view->priv->width, allocation.width);
7694 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7695 rect.height = ROW_HEIGHT (tree_view);
7699 GdkRectangle new_rect;
7701 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7703 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7707 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7712 pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
7714 const GdkRectangle *clip_rect)
7718 _pspp_sheet_view_find_node (tree_view, path, &node);
7721 _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7725 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7728 GtkTreePath *cursor_path;
7730 if ((tree_view->priv->row_count == 0) ||
7731 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7735 if (tree_view->priv->cursor)
7736 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7738 if (cursor_path == NULL)
7740 /* There's no cursor. Move the cursor to the first selected row, if any
7741 * are selected, otherwise to the first row in the sheetview.
7743 GList *selected_rows;
7744 GtkTreeModel *model;
7745 PsppSheetSelection *selection;
7747 selection = pspp_sheet_view_get_selection (tree_view);
7748 selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7752 /* XXX we could avoid doing O(n) work to get this result */
7753 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7754 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7755 g_list_free (selected_rows);
7759 cursor_path = gtk_tree_path_new_first ();
7760 search_first_focusable_path (tree_view, &cursor_path,
7764 gtk_tree_row_reference_free (tree_view->priv->cursor);
7765 tree_view->priv->cursor = NULL;
7769 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7770 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7771 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0);
7773 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0);
7779 /* Now find a column for the cursor. */
7780 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7782 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7783 gtk_tree_path_free (cursor_path);
7785 if (tree_view->priv->focus_column == NULL)
7788 for (list = tree_view->priv->columns; list; list = list->next)
7790 if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7792 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7793 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7794 pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7804 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7806 PsppSheetSelectMode mode)
7808 gint selection_count;
7809 int cursor_node = -1;
7810 int new_cursor_node = -1;
7811 GtkTreePath *cursor_path = NULL;
7812 gboolean grab_focus = TRUE;
7814 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7818 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7819 /* FIXME: we lost the cursor; should we get the first? */
7822 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7823 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7825 if (cursor_node < 0)
7826 /* FIXME: we lost the cursor; should we get the first? */
7829 selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7831 if (selection_count == 0
7832 && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7833 && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
7835 /* Don't move the cursor, but just select the current node */
7836 new_cursor_node = cursor_node;
7841 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7843 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7846 gtk_tree_path_free (cursor_path);
7848 if (new_cursor_node)
7850 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7852 search_first_focusable_path (tree_view, &cursor_path,
7857 gtk_tree_path_free (cursor_path);
7861 * If the list has only one item and multi-selection is set then select
7862 * the row (if not yet selected).
7864 if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7865 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7866 new_cursor_node < 0)
7869 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7871 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7873 if (new_cursor_node < 0
7874 && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
7876 new_cursor_node = cursor_node;
7880 new_cursor_node = -1;
7884 if (new_cursor_node >= 0)
7886 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7887 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode);
7888 gtk_tree_path_free (cursor_path);
7892 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7894 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
7896 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
7898 GTK_DIR_UP : GTK_DIR_DOWN))
7900 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
7903 gtk_widget_child_focus (toplevel,
7905 GTK_DIR_TAB_BACKWARD :
7906 GTK_DIR_TAB_FORWARD);
7913 gtk_widget_error_bell (GTK_WIDGET (tree_view));
7918 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7920 return new_cursor_node >= 0;
7924 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
7926 PsppSheetSelectMode mode)
7928 int cursor_node = -1;
7929 GtkTreePath *old_cursor_path = NULL;
7930 GtkTreePath *cursor_path = NULL;
7931 int start_cursor_node = -1;
7934 gint vertical_separator;
7936 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7939 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
7940 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7942 /* This is sorta weird. Focus in should give us a cursor */
7945 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
7946 _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
7948 if (cursor_node < 0)
7950 /* FIXME: we lost the cursor. Should we try to get one? */
7951 gtk_tree_path_free (old_cursor_path);
7955 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7956 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
7957 y += tree_view->priv->cursor_offset;
7958 y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment);
7959 y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment), (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator);
7961 if (y >= tree_view->priv->height)
7962 y = tree_view->priv->height - 1;
7964 tree_view->priv->cursor_offset =
7965 pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
7967 if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
7969 cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7970 tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
7973 y -= tree_view->priv->cursor_offset;
7974 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7976 start_cursor_node = cursor_node;
7978 if (! search_first_focusable_path (tree_view, &cursor_path,
7982 /* It looks like we reached the end of the view without finding
7983 * a focusable row. We will step backwards to find the last
7986 cursor_node = start_cursor_node;
7987 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7989 search_first_focusable_path (tree_view, &cursor_path,
7998 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8000 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode);
8003 pspp_sheet_view_scroll_to_point (tree_view, -1, y);
8004 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8005 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8007 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
8008 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8010 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8013 gtk_tree_path_free (old_cursor_path);
8014 gtk_tree_path_free (cursor_path);
8018 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8020 PsppSheetSelectMode mode)
8022 int cursor_node = -1;
8023 GtkTreePath *cursor_path = NULL;
8024 PsppSheetViewColumn *column;
8027 gboolean found_column = FALSE;
8030 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8032 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8035 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8036 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8040 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8041 if (cursor_node < 0)
8043 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8045 gtk_tree_path_free (cursor_path);
8048 gtk_tree_path_free (cursor_path);
8050 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8051 if (tree_view->priv->focus_column)
8053 for (; list; list = (rtl ? list->prev : list->next))
8055 if (list->data == tree_view->priv->focus_column)
8062 gboolean left, right;
8064 column = list->data;
8065 if (column->visible == FALSE || column->row_head)
8068 pspp_sheet_view_column_cell_set_cell_data (column,
8069 tree_view->priv->model,
8074 right = list->prev ? TRUE : FALSE;
8075 left = list->next ? TRUE : FALSE;
8079 left = list->prev ? TRUE : FALSE;
8080 right = list->next ? TRUE : FALSE;
8083 if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8085 tree_view->priv->focus_column = column;
8086 found_column = TRUE;
8091 list = rtl ? list->prev : list->next;
8093 list = rtl ? list->next : list->prev;
8098 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8099 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8100 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8104 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8107 pspp_sheet_view_clamp_column_visible (tree_view,
8108 tree_view->priv->focus_column, TRUE);
8112 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8114 PsppSheetSelectMode mode)
8116 int cursor_node = -1;
8117 GtkTreePath *cursor_path = NULL;
8118 PsppSheetViewColumn *column;
8119 PsppSheetViewColumn *found_column;
8124 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8126 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8129 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8130 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8134 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8135 if (cursor_node < 0)
8137 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8139 gtk_tree_path_free (cursor_path);
8142 gtk_tree_path_free (cursor_path);
8144 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8145 if (tree_view->priv->focus_column)
8147 for (; list; list = (rtl ? list->prev : list->next))
8149 if (list->data == tree_view->priv->focus_column)
8154 found_column = NULL;
8157 gboolean left, right;
8159 column = list->data;
8160 if (column->visible == FALSE || column->row_head)
8163 pspp_sheet_view_column_cell_set_cell_data (column,
8164 tree_view->priv->model,
8169 right = list->prev ? TRUE : FALSE;
8170 left = list->next ? TRUE : FALSE;
8174 left = list->prev ? TRUE : FALSE;
8175 right = list->next ? TRUE : FALSE;
8178 if (column->tabbable
8179 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8180 found_column = column;
8184 list = rtl ? list->prev : list->next;
8186 list = rtl ? list->next : list->prev;
8191 tree_view->priv->focus_column = found_column;
8192 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8193 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8194 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8197 pspp_sheet_view_clamp_column_visible (tree_view,
8198 tree_view->priv->focus_column, TRUE);
8202 try_move_cursor_tab (PsppSheetView *tree_view,
8203 gboolean start_at_focus_column,
8206 PsppSheetViewColumn *column;
8208 int cursor_node = -1;
8209 GtkTreePath *cursor_path = NULL;
8213 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8214 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8218 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8219 if (cursor_node < 0)
8221 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8223 gtk_tree_path_free (cursor_path);
8226 gtk_tree_path_free (cursor_path);
8228 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8229 if (start_at_focus_column)
8232 ? g_list_last (tree_view->priv->columns)
8233 : g_list_first (tree_view->priv->columns));
8234 if (tree_view->priv->focus_column)
8236 for (; list; list = (rtl ? list->prev : list->next))
8238 if (list->data == tree_view->priv->focus_column)
8245 list = (rtl ^ (count == 1)
8246 ? g_list_first (tree_view->priv->columns)
8247 : g_list_last (tree_view->priv->columns));
8252 gboolean left, right;
8254 column = list->data;
8255 if (column->visible == FALSE || !column->tabbable)
8258 pspp_sheet_view_column_cell_set_cell_data (column,
8259 tree_view->priv->model,
8264 right = list->prev ? TRUE : FALSE;
8265 left = list->next ? TRUE : FALSE;
8269 left = list->prev ? TRUE : FALSE;
8270 right = list->next ? TRUE : FALSE;
8273 if (column->tabbable
8274 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8276 tree_view->priv->focus_column = column;
8277 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8278 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8279 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8284 list = rtl ? list->prev : list->next;
8286 list = rtl ? list->next : list->prev;
8293 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8296 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8299 if (!try_move_cursor_tab (tree_view, TRUE, count))
8301 if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0)
8302 && !try_move_cursor_tab (tree_view, FALSE, count))
8303 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8306 pspp_sheet_view_clamp_column_visible (tree_view,
8307 tree_view->priv->focus_column, TRUE);
8311 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8313 PsppSheetSelectMode mode)
8317 GtkTreePath *old_path;
8319 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8322 g_return_if_fail (tree_view->priv->row_count > 0);
8324 pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8328 /* Now go forward to find the first focusable row. */
8329 path = _pspp_sheet_view_find_path (tree_view, 0);
8330 search_first_focusable_path (tree_view, &path,
8331 TRUE, &cursor_node);
8335 /* Now go backwards to find last focusable row. */
8336 path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8337 search_first_focusable_path (tree_view, &path,
8338 FALSE, &cursor_node);
8344 if (gtk_tree_path_compare (old_path, path))
8346 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
8347 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8351 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8355 gtk_tree_path_free (old_path);
8356 gtk_tree_path_free (path);
8360 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8362 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8365 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8366 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8369 pspp_sheet_selection_select_all (tree_view->priv->selection);
8375 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8377 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8380 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8381 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8384 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8390 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8391 gboolean start_editing,
8392 PsppSheetSelectMode mode)
8395 int cursor_node = -1;
8396 GtkTreePath *cursor_path = NULL;
8398 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8401 if (tree_view->priv->cursor)
8402 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8404 if (cursor_path == NULL)
8407 _pspp_sheet_view_find_node (tree_view, cursor_path,
8410 if (cursor_node < 0)
8412 gtk_tree_path_free (cursor_path);
8416 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing &&
8417 tree_view->priv->focus_column)
8419 if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8421 gtk_tree_path_free (cursor_path);
8426 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8432 /* We bail out if the original (tree, node) don't exist anymore after
8433 * handling the selection-changed callback. We do return TRUE because
8434 * the key press has been handled at this point.
8436 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8438 if (cursor_node != new_node)
8441 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8443 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8444 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8446 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8447 pspp_sheet_view_row_activated (tree_view, cursor_path,
8448 tree_view->priv->focus_column);
8450 gtk_tree_path_free (cursor_path);
8456 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8459 int cursor_node = -1;
8460 GtkTreePath *cursor_path = NULL;
8462 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8466 if (tree_view->priv->cursor)
8467 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8469 if (cursor_path == NULL)
8472 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8473 if (cursor_node < 0)
8475 gtk_tree_path_free (cursor_path);
8479 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8482 PSPP_SHEET_SELECT_MODE_TOGGLE,
8485 /* We bail out if the original (tree, node) don't exist anymore after
8486 * handling the selection-changed callback. We do return TRUE because
8487 * the key press has been handled at this point.
8489 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8491 if (cursor_node != new_node)
8494 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8496 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8497 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8498 gtk_tree_path_free (cursor_path);
8504 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8506 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8507 tree_view->priv->typeselect_flush_timeout = 0;
8512 /* Cut and paste from gtkwindow.c */
8514 send_focus_change (GtkWidget *widget,
8517 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8519 fevent->focus_change.type = GDK_FOCUS_CHANGE;
8520 fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
8521 fevent->focus_change.in = in;
8523 gtk_widget_send_focus_change (widget, fevent);
8524 gdk_event_free (fevent);
8528 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8530 GtkWidget *frame, *vbox, *toplevel;
8533 if (tree_view->priv->search_custom_entry_set)
8536 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8537 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8539 if (tree_view->priv->search_window != NULL)
8541 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8542 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8543 GTK_WINDOW (tree_view->priv->search_window));
8544 else if (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)))
8545 gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
8546 GTK_WINDOW (tree_view->priv->search_window));
8547 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8551 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8552 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8554 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8555 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8556 GTK_WINDOW (tree_view->priv->search_window));
8558 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8559 GDK_WINDOW_TYPE_HINT_UTILITY);
8560 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8561 g_signal_connect (tree_view->priv->search_window, "delete-event",
8562 G_CALLBACK (pspp_sheet_view_search_delete_event),
8564 g_signal_connect (tree_view->priv->search_window, "key-press-event",
8565 G_CALLBACK (pspp_sheet_view_search_key_press_event),
8567 g_signal_connect (tree_view->priv->search_window, "button-press-event",
8568 G_CALLBACK (pspp_sheet_view_search_button_press_event),
8570 g_signal_connect (tree_view->priv->search_window, "scroll-event",
8571 G_CALLBACK (pspp_sheet_view_search_scroll_event),
8574 frame = gtk_frame_new (NULL);
8575 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8576 gtk_widget_show (frame);
8577 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8579 vbox = gtk_vbox_new (FALSE, 0);
8580 gtk_widget_show (vbox);
8581 gtk_container_add (GTK_CONTAINER (frame), vbox);
8582 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8585 tree_view->priv->search_entry = gtk_entry_new ();
8586 gtk_widget_show (tree_view->priv->search_entry);
8587 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8588 G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8590 g_signal_connect (tree_view->priv->search_entry,
8591 "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8595 g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8597 G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8601 gtk_container_add (GTK_CONTAINER (vbox),
8602 tree_view->priv->search_entry);
8604 gtk_widget_realize (tree_view->priv->search_entry);
8607 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8608 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8611 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8612 gboolean keybinding)
8614 /* We only start interactive search if we have focus or the columns
8615 * have focus. If one of our children have focus, we don't want to
8619 gboolean found_focus = FALSE;
8620 GtkWidgetClass *entry_parent_class;
8622 if (!tree_view->priv->enable_search && !keybinding)
8625 if (tree_view->priv->search_custom_entry_set)
8628 if (tree_view->priv->search_window != NULL &&
8629 gtk_widget_get_visible (tree_view->priv->search_window))
8632 for (list = tree_view->priv->columns; list; list = list->next)
8634 PsppSheetViewColumn *column;
8636 column = list->data;
8637 if (! column->visible)
8640 if (column->button && gtk_widget_has_focus (column->button))
8647 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8653 if (tree_view->priv->search_column < 0)
8656 pspp_sheet_view_ensure_interactive_directory (tree_view);
8659 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8662 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8663 gtk_widget_show (tree_view->priv->search_window);
8664 if (tree_view->priv->search_entry_changed_id == 0)
8666 tree_view->priv->search_entry_changed_id =
8667 g_signal_connect (tree_view->priv->search_entry, "changed",
8668 G_CALLBACK (pspp_sheet_view_search_init),
8672 tree_view->priv->typeselect_flush_timeout =
8673 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8674 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8677 /* Grab focus will select all the text. We don't want that to happen, so we
8678 * call the parent instance and bypass the selection change. This is probably
8679 * really non-kosher. */
8680 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8681 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8683 /* send focus-in event */
8684 send_focus_change (tree_view->priv->search_entry, TRUE);
8686 /* search first matching iter */
8687 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8693 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8695 return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8698 /* this function returns the new width of the column being resized given
8699 * the column and x position of the cursor; the x cursor position is passed
8700 * in as a pointer and automagicly corrected if it's beyond min/max limits
8703 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8707 PsppSheetViewColumn *column;
8711 /* first translate the x position from gtk_widget_get_window (widget)
8712 * to clist->clist_window
8714 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8715 column = g_list_nth (tree_view->priv->columns, i)->data;
8716 width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8718 /* Clamp down the value */
8719 if (column->min_width == -1)
8720 width = MAX (column->button_request, width);
8722 width = MAX (column->min_width, width);
8723 if (column->max_width != -1)
8724 width = MIN (width, column->max_width);
8726 *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8732 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8736 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8737 PsppSheetView *tree_view)
8739 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8743 gdk_window_move (tree_view->priv->bin_window,
8744 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8745 TREE_VIEW_HEADER_HEIGHT (tree_view));
8746 gdk_window_move (tree_view->priv->header_window,
8747 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8749 dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8751 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8755 /* update our dy and top_row */
8756 tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8758 update_prelight (tree_view,
8759 tree_view->priv->event_last_x,
8760 tree_view->priv->event_last_y);
8762 if (!tree_view->priv->in_top_row_to_dy)
8763 pspp_sheet_view_dy_to_top_row (tree_view);
8766 update_childrens_allocation(tree_view);
8774 * pspp_sheet_view_new:
8776 * Creates a new #PsppSheetView widget.
8778 * Return value: A newly created #PsppSheetView widget.
8781 pspp_sheet_view_new (void)
8783 return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
8787 * pspp_sheet_view_new_with_model:
8788 * @model: the model.
8790 * Creates a new #PsppSheetView widget with the model initialized to @model.
8792 * Return value: A newly created #PsppSheetView widget.
8795 pspp_sheet_view_new_with_model (GtkTreeModel *model)
8797 return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
8804 * pspp_sheet_view_get_model:
8805 * @tree_view: a #PsppSheetView
8807 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
8810 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
8813 pspp_sheet_view_get_model (PsppSheetView *tree_view)
8815 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8817 return tree_view->priv->model;
8821 * pspp_sheet_view_set_model:
8822 * @tree_view: A #GtkTreeNode.
8823 * @model: (allow-none): The model.
8825 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
8826 * set, it will remove it before setting the new model. If @model is %NULL,
8827 * then it will unset the old model.
8830 pspp_sheet_view_set_model (PsppSheetView *tree_view,
8831 GtkTreeModel *model)
8833 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8834 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
8836 if (model == tree_view->priv->model)
8839 if (tree_view->priv->scroll_to_path)
8841 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8842 tree_view->priv->scroll_to_path = NULL;
8845 if (tree_view->priv->model)
8847 GList *tmplist = tree_view->priv->columns;
8849 if (tree_view->priv->selected)
8850 range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
8851 pspp_sheet_view_stop_editing (tree_view, TRUE);
8853 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8854 pspp_sheet_view_row_changed,
8856 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8857 pspp_sheet_view_row_inserted,
8859 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8860 pspp_sheet_view_row_deleted,
8862 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8863 pspp_sheet_view_rows_reordered,
8866 for (; tmplist; tmplist = tmplist->next)
8867 _pspp_sheet_view_column_unset_model (tmplist->data,
8868 tree_view->priv->model);
8870 tree_view->priv->prelight_node = -1;
8872 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
8873 tree_view->priv->drag_dest_row = NULL;
8874 gtk_tree_row_reference_free (tree_view->priv->cursor);
8875 tree_view->priv->cursor = NULL;
8876 gtk_tree_row_reference_free (tree_view->priv->anchor);
8877 tree_view->priv->anchor = NULL;
8878 gtk_tree_row_reference_free (tree_view->priv->top_row);
8879 tree_view->priv->top_row = NULL;
8880 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8881 tree_view->priv->scroll_to_path = NULL;
8883 tree_view->priv->scroll_to_column = NULL;
8885 g_object_unref (tree_view->priv->model);
8887 tree_view->priv->search_column = -1;
8888 tree_view->priv->fixed_height = -1;
8889 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
8890 tree_view->priv->last_button_x = -1;
8891 tree_view->priv->last_button_y = -1;
8894 tree_view->priv->model = model;
8896 if (tree_view->priv->model)
8900 if (tree_view->priv->search_column == -1)
8902 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
8904 GType type = gtk_tree_model_get_column_type (model, i);
8906 if (g_value_type_transformable (type, G_TYPE_STRING))
8908 tree_view->priv->search_column = i;
8914 g_object_ref (tree_view->priv->model);
8915 g_signal_connect (tree_view->priv->model,
8917 G_CALLBACK (pspp_sheet_view_row_changed),
8919 g_signal_connect (tree_view->priv->model,
8921 G_CALLBACK (pspp_sheet_view_row_inserted),
8923 g_signal_connect (tree_view->priv->model,
8925 G_CALLBACK (pspp_sheet_view_row_deleted),
8927 g_signal_connect (tree_view->priv->model,
8929 G_CALLBACK (pspp_sheet_view_rows_reordered),
8932 tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
8934 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
8935 install_presize_handler (tree_view);
8938 g_object_notify (G_OBJECT (tree_view), "model");
8940 if (tree_view->priv->selection)
8941 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
8943 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8944 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
8948 * pspp_sheet_view_get_selection:
8949 * @tree_view: A #PsppSheetView.
8951 * Gets the #PsppSheetSelection associated with @tree_view.
8953 * Return value: A #PsppSheetSelection object.
8955 PsppSheetSelection *
8956 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
8958 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8960 return tree_view->priv->selection;
8964 * pspp_sheet_view_get_hadjustment:
8965 * @tree_view: A #PsppSheetView
8967 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
8969 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
8973 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
8975 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8977 return pspp_sheet_view_do_get_hadjustment (tree_view);
8980 static GtkAdjustment *
8981 pspp_sheet_view_do_get_hadjustment (PsppSheetView *tree_view)
8983 return tree_view->priv->hadjustment;
8987 * pspp_sheet_view_set_hadjustment:
8988 * @tree_view: A #PsppSheetView
8989 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
8991 * Sets the #GtkAdjustment for the current horizontal aspect.
8994 pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view,
8995 GtkAdjustment *adjustment)
8997 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8999 pspp_sheet_view_set_adjustments (tree_view,
9001 tree_view->priv->vadjustment);
9003 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9007 pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
9008 GtkAdjustment *adjustment)
9010 PsppSheetViewPrivate *priv = tree_view->priv;
9012 if (adjustment && priv->hadjustment == adjustment)
9015 if (priv->hadjustment != NULL)
9017 g_signal_handlers_disconnect_by_func (priv->hadjustment,
9018 pspp_sheet_view_adjustment_changed,
9020 g_object_unref (priv->hadjustment);
9023 if (adjustment == NULL)
9024 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9027 g_signal_connect (adjustment, "value-changed",
9028 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9029 priv->hadjustment = g_object_ref_sink (adjustment);
9030 /* FIXME: Adjustment should probably be populated here with fresh values, but
9031 * internal details are too complicated for me to decipher right now.
9033 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9035 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9039 * pspp_sheet_view_get_vadjustment:
9040 * @tree_view: A #PsppSheetView
9042 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9044 * Return value: (transfer none): A #GtkAdjustment object, or %NULL
9045 * if none is currently being used.
9047 * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
9050 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9052 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9054 return pspp_sheet_view_do_get_vadjustment (tree_view);
9057 static GtkAdjustment *
9058 pspp_sheet_view_do_get_vadjustment (PsppSheetView *tree_view)
9060 return tree_view->priv->vadjustment;
9064 * pspp_sheet_view_set_vadjustment:
9065 * @tree_view: A #PsppSheetView
9066 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9068 * Sets the #GtkAdjustment for the current vertical aspect.
9070 * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
9073 pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view,
9074 GtkAdjustment *adjustment)
9076 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9077 g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
9079 pspp_sheet_view_do_set_vadjustment (tree_view, adjustment);
9083 pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
9084 GtkAdjustment *adjustment)
9086 PsppSheetViewPrivate *priv = tree_view->priv;
9088 if (adjustment && priv->vadjustment == adjustment)
9091 if (priv->vadjustment != NULL)
9093 g_signal_handlers_disconnect_by_func (priv->vadjustment,
9094 pspp_sheet_view_adjustment_changed,
9096 g_object_unref (priv->vadjustment);
9099 if (adjustment == NULL)
9100 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9103 g_signal_connect (adjustment, "value-changed",
9104 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9105 priv->vadjustment = g_object_ref_sink (adjustment);
9106 /* FIXME: Adjustment should probably be populated here with fresh values, but
9107 * internal details are too complicated for me to decipher right now.
9109 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9110 g_object_notify (G_OBJECT (tree_view), "vadjustment");
9113 /* Column and header operations */
9116 * pspp_sheet_view_get_headers_visible:
9117 * @tree_view: A #PsppSheetView.
9119 * Returns %TRUE if the headers on the @tree_view are visible.
9121 * Return value: Whether the headers are visible or not.
9124 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9126 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9128 return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9132 * pspp_sheet_view_set_headers_visible:
9133 * @tree_view: A #PsppSheetView.
9134 * @headers_visible: %TRUE if the headers are visible
9136 * Sets the visibility state of the headers.
9139 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9140 gboolean headers_visible)
9144 PsppSheetViewColumn *column;
9145 GtkAllocation allocation;
9147 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9149 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9151 headers_visible = !! headers_visible;
9153 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9156 if (headers_visible)
9157 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9159 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9161 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9163 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9164 if (headers_visible)
9166 gdk_window_move_resize (tree_view->priv->bin_window, x, y + TREE_VIEW_HEADER_HEIGHT (tree_view),
9167 tree_view->priv->width, allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view));
9169 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9170 pspp_sheet_view_map_buttons (tree_view);
9174 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9176 for (list = tree_view->priv->columns; list; list = list->next)
9178 column = list->data;
9180 gtk_widget_unmap (column->button);
9182 gdk_window_hide (tree_view->priv->header_window);
9186 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view));
9187 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2);
9188 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
9189 gtk_adjustment_set_upper (tree_view->priv->vadjustment, tree_view->priv->height);
9190 gtk_adjustment_changed (tree_view->priv->vadjustment);
9192 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9194 g_object_notify (G_OBJECT (tree_view), "headers-visible");
9198 * pspp_sheet_view_columns_autosize:
9199 * @tree_view: A #PsppSheetView.
9201 * Resizes all columns to their optimal width. Only works after the
9202 * treeview has been realized.
9205 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9207 gboolean dirty = FALSE;
9209 PsppSheetViewColumn *column;
9211 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9213 for (list = tree_view->priv->columns; list; list = list->next)
9215 column = list->data;
9216 _pspp_sheet_view_column_cell_set_dirty (column);
9221 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9225 * pspp_sheet_view_set_headers_clickable:
9226 * @tree_view: A #PsppSheetView.
9227 * @setting: %TRUE if the columns are clickable.
9229 * Allow the column title buttons to be clicked.
9232 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9237 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9239 for (list = tree_view->priv->columns; list; list = list->next)
9240 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9242 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9247 * pspp_sheet_view_get_headers_clickable:
9248 * @tree_view: A #PsppSheetView.
9250 * Returns whether all header columns are clickable.
9252 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9257 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9261 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9263 for (list = tree_view->priv->columns; list; list = list->next)
9264 if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9271 * pspp_sheet_view_set_rules_hint
9272 * @tree_view: a #PsppSheetView
9273 * @setting: %TRUE if the tree requires reading across rows
9275 * This function tells GTK+ that the user interface for your
9276 * application requires users to read across tree rows and associate
9277 * cells with one another. By default, GTK+ will then render the tree
9278 * with alternating row colors. Do <emphasis>not</emphasis> use it
9279 * just because you prefer the appearance of the ruled tree; that's a
9280 * question for the theme. Some themes will draw tree rows in
9281 * alternating colors even when rules are turned off, and users who
9282 * prefer that appearance all the time can choose those themes. You
9283 * should call this function only as a <emphasis>semantic</emphasis>
9284 * hint to the theme engine that your tree makes alternating colors
9285 * useful from a functional standpoint (since it has lots of columns,
9290 pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view,
9293 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9295 setting = setting != FALSE;
9297 if (tree_view->priv->has_rules != setting)
9299 tree_view->priv->has_rules = setting;
9300 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9303 g_object_notify (G_OBJECT (tree_view), "rules-hint");
9307 * pspp_sheet_view_get_rules_hint
9308 * @tree_view: a #PsppSheetView
9310 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9312 * Return value: %TRUE if rules are useful for the user of this tree
9315 pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view)
9317 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9319 return tree_view->priv->has_rules;
9322 /* Public Column functions
9326 * pspp_sheet_view_append_column:
9327 * @tree_view: A #PsppSheetView.
9328 * @column: The #PsppSheetViewColumn to add.
9330 * Appends @column to the list of columns.
9332 * Return value: The number of columns in @tree_view after appending.
9335 pspp_sheet_view_append_column (PsppSheetView *tree_view,
9336 PsppSheetViewColumn *column)
9338 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9339 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9340 g_return_val_if_fail (column->tree_view == NULL, -1);
9342 return pspp_sheet_view_insert_column (tree_view, column, -1);
9347 * pspp_sheet_view_remove_column:
9348 * @tree_view: A #PsppSheetView.
9349 * @column: The #PsppSheetViewColumn to remove.
9351 * Removes @column from @tree_view.
9353 * Return value: The number of columns in @tree_view after removing.
9356 pspp_sheet_view_remove_column (PsppSheetView *tree_view,
9357 PsppSheetViewColumn *column)
9359 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9360 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9361 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9363 if (tree_view->priv->focus_column == column)
9364 tree_view->priv->focus_column = NULL;
9366 if (tree_view->priv->edited_column == column)
9368 pspp_sheet_view_stop_editing (tree_view, TRUE);
9370 /* no need to, but just to be sure ... */
9371 tree_view->priv->edited_column = NULL;
9374 _pspp_sheet_view_column_unset_tree_view (column);
9376 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9377 tree_view->priv->n_columns--;
9379 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9383 _pspp_sheet_view_column_unrealize_button (column);
9384 for (list = tree_view->priv->columns; list; list = list->next)
9386 PsppSheetViewColumn *tmp_column;
9388 tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9389 if (tmp_column->visible)
9390 _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9393 if (tree_view->priv->n_columns == 0 &&
9394 pspp_sheet_view_get_headers_visible (tree_view) &&
9395 tree_view->priv->header_window)
9396 gdk_window_hide (tree_view->priv->header_window);
9398 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9401 g_object_unref (column);
9402 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9404 return tree_view->priv->n_columns;
9408 * pspp_sheet_view_insert_column:
9409 * @tree_view: A #PsppSheetView.
9410 * @column: The #PsppSheetViewColumn to be inserted.
9411 * @position: The position to insert @column in.
9413 * This inserts the @column into the @tree_view at @position. If @position is
9414 * -1, then the column is inserted at the end.
9416 * Return value: The number of columns in @tree_view after insertion.
9419 pspp_sheet_view_insert_column (PsppSheetView *tree_view,
9420 PsppSheetViewColumn *column,
9423 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9424 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9425 g_return_val_if_fail (column->tree_view == NULL, -1);
9427 g_object_ref_sink (column);
9429 if (tree_view->priv->n_columns == 0 &&
9430 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9431 pspp_sheet_view_get_headers_visible (tree_view))
9433 gdk_window_show (tree_view->priv->header_window);
9436 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9438 tree_view->priv->n_columns++;
9440 _pspp_sheet_view_column_set_tree_view (column, tree_view);
9442 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9446 _pspp_sheet_view_column_realize_button (column);
9448 for (list = tree_view->priv->columns; list; list = list->next)
9450 column = PSPP_SHEET_VIEW_COLUMN (list->data);
9451 if (column->visible)
9452 _pspp_sheet_view_column_cell_set_dirty (column);
9454 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9457 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9459 return tree_view->priv->n_columns;
9463 * pspp_sheet_view_insert_column_with_attributes:
9464 * @tree_view: A #PsppSheetView
9465 * @position: The position to insert the new column in.
9466 * @title: The title to set the header to.
9467 * @cell: The #GtkCellRenderer.
9468 * @Varargs: A %NULL-terminated list of attributes.
9470 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9471 * @position. If @position is -1, then the newly created column is inserted at
9472 * the end. The column is initialized with the attributes given.
9474 * Return value: The number of columns in @tree_view after insertion.
9477 pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view,
9480 GtkCellRenderer *cell,
9483 PsppSheetViewColumn *column;
9488 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9490 column = pspp_sheet_view_column_new ();
9491 pspp_sheet_view_column_set_title (column, title);
9492 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9494 va_start (args, cell);
9496 attribute = va_arg (args, gchar *);
9498 while (attribute != NULL)
9500 column_id = va_arg (args, gint);
9501 pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9502 attribute = va_arg (args, gchar *);
9507 pspp_sheet_view_insert_column (tree_view, column, position);
9509 return tree_view->priv->n_columns;
9513 * pspp_sheet_view_insert_column_with_data_func:
9514 * @tree_view: a #PsppSheetView
9515 * @position: Position to insert, -1 for append
9516 * @title: column title
9517 * @cell: cell renderer for column
9518 * @func: function to set attributes of cell renderer
9519 * @data: data for @func
9520 * @dnotify: destroy notifier for @data
9522 * Convenience function that inserts a new column into the #PsppSheetView
9523 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9524 * attributes (normally using data from the model). See also
9525 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9527 * Return value: number of columns in the tree view post-insert
9530 pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view,
9533 GtkCellRenderer *cell,
9534 PsppSheetCellDataFunc func,
9536 GDestroyNotify dnotify)
9538 PsppSheetViewColumn *column;
9540 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9542 column = pspp_sheet_view_column_new ();
9543 pspp_sheet_view_column_set_title (column, title);
9544 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9545 pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9547 pspp_sheet_view_insert_column (tree_view, column, position);
9549 return tree_view->priv->n_columns;
9553 * pspp_sheet_view_get_column:
9554 * @tree_view: A #PsppSheetView.
9555 * @n: The position of the column, counting from 0.
9557 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9559 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9562 PsppSheetViewColumn *
9563 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9566 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9568 if (n < 0 || n >= tree_view->priv->n_columns)
9571 if (tree_view->priv->columns == NULL)
9574 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9578 * pspp_sheet_view_get_columns:
9579 * @tree_view: A #PsppSheetView
9581 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9582 * The returned list must be freed with g_list_free ().
9584 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9587 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9589 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9591 return g_list_copy (tree_view->priv->columns);
9595 * pspp_sheet_view_move_column_after:
9596 * @tree_view: A #PsppSheetView
9597 * @column: The #PsppSheetViewColumn to be moved.
9598 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9600 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9601 * @column is placed in the first position.
9604 pspp_sheet_view_move_column_after (PsppSheetView *tree_view,
9605 PsppSheetViewColumn *column,
9606 PsppSheetViewColumn *base_column)
9608 GList *column_list_el, *base_el = NULL;
9610 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9612 column_list_el = g_list_find (tree_view->priv->columns, column);
9613 g_return_if_fail (column_list_el != NULL);
9617 base_el = g_list_find (tree_view->priv->columns, base_column);
9618 g_return_if_fail (base_el != NULL);
9621 if (column_list_el->prev == base_el)
9624 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9625 if (base_el == NULL)
9627 column_list_el->prev = NULL;
9628 column_list_el->next = tree_view->priv->columns;
9629 if (column_list_el->next)
9630 column_list_el->next->prev = column_list_el;
9631 tree_view->priv->columns = column_list_el;
9635 column_list_el->prev = base_el;
9636 column_list_el->next = base_el->next;
9637 if (column_list_el->next)
9638 column_list_el->next->prev = column_list_el;
9639 base_el->next = column_list_el;
9642 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9644 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9645 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9648 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9652 * pspp_sheet_view_set_column_drag_function:
9653 * @tree_view: A #PsppSheetView.
9654 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9655 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9656 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9658 * Sets a user function for determining where a column may be dropped when
9659 * dragged. This function is called on every column pair in turn at the
9660 * beginning of a column drag to determine where a drop can take place. The
9661 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9662 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9663 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9664 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9665 * @tree_view reverts to the default behavior of allowing all columns to be
9666 * dropped everywhere.
9669 pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view,
9670 PsppSheetViewColumnDropFunc func,
9672 GDestroyNotify destroy)
9674 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9676 if (tree_view->priv->column_drop_func_data_destroy)
9677 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9679 tree_view->priv->column_drop_func = func;
9680 tree_view->priv->column_drop_func_data = user_data;
9681 tree_view->priv->column_drop_func_data_destroy = destroy;
9685 * pspp_sheet_view_scroll_to_point:
9686 * @tree_view: a #PsppSheetView
9687 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9688 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9690 * Scrolls the tree view such that the top-left corner of the visible
9691 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9692 * in tree coordinates. The @tree_view must be realized before
9693 * this function is called. If it isn't, you probably want to be
9694 * using pspp_sheet_view_scroll_to_cell().
9696 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9699 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9703 GtkAdjustment *hadj;
9704 GtkAdjustment *vadj;
9706 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9707 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9709 hadj = tree_view->priv->hadjustment;
9710 vadj = tree_view->priv->vadjustment;
9713 gtk_adjustment_set_value (hadj, CLAMP (tree_x, gtk_adjustment_get_lower (hadj), gtk_adjustment_get_upper (hadj) - gtk_adjustment_get_page_size (hadj)));
9715 gtk_adjustment_set_value (vadj, CLAMP (tree_y, gtk_adjustment_get_lower (vadj), gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj)));
9719 * pspp_sheet_view_scroll_to_cell:
9720 * @tree_view: A #PsppSheetView.
9721 * @path: (allow-none): The path of the row to move to, or %NULL.
9722 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9723 * @use_align: whether to use alignment arguments, or %FALSE.
9724 * @row_align: The vertical alignment of the row specified by @path.
9725 * @col_align: The horizontal alignment of the column specified by @column.
9727 * Moves the alignments of @tree_view to the position specified by @column and
9728 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9729 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9730 * or @path need to be non-%NULL. @row_align determines where the row is
9731 * placed, and @col_align determines where @column is placed. Both are expected
9732 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9733 * right/bottom alignment, 0.5 means center.
9735 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9736 * tree does the minimum amount of work to scroll the cell onto the screen.
9737 * This means that the cell will be scrolled to the edge closest to its current
9738 * position. If the cell is currently visible on the screen, nothing is done.
9740 * This function only works if the model is set, and @path is a valid row on the
9741 * model. If the model changes before the @tree_view is realized, the centered
9742 * path will be modified to reflect this change.
9745 pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view,
9747 PsppSheetViewColumn *column,
9752 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9753 g_return_if_fail (tree_view->priv->model != NULL);
9754 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9755 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9756 g_return_if_fail (path != NULL || column != NULL);
9759 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9760 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9762 row_align = CLAMP (row_align, 0.0, 1.0);
9763 col_align = CLAMP (col_align, 0.0, 1.0);
9766 /* Note: Despite the benefits that come from having one code path for the
9767 * scrolling code, we short-circuit validate_visible_area's immplementation as
9768 * it is much slower than just going to the point.
9770 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9771 !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9772 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9774 if (tree_view->priv->scroll_to_path)
9775 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9777 tree_view->priv->scroll_to_path = NULL;
9778 tree_view->priv->scroll_to_column = NULL;
9781 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
9783 tree_view->priv->scroll_to_column = column;
9784 tree_view->priv->scroll_to_use_align = use_align;
9785 tree_view->priv->scroll_to_row_align = row_align;
9786 tree_view->priv->scroll_to_col_align = col_align;
9788 install_presize_handler (tree_view);
9792 GdkRectangle cell_rect;
9793 GdkRectangle vis_rect;
9794 gint dest_x, dest_y;
9796 pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
9797 pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
9799 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
9801 dest_x = vis_rect.x;
9802 dest_y = vis_rect.y;
9808 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
9812 if (cell_rect.x < vis_rect.x)
9813 dest_x = cell_rect.x;
9814 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
9815 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
9823 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
9824 dest_y = MAX (dest_y, 0);
9828 if (cell_rect.y < vis_rect.y)
9829 dest_y = cell_rect.y;
9830 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
9831 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
9835 pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
9840 * pspp_sheet_view_row_activated:
9841 * @tree_view: A #PsppSheetView
9842 * @path: The #GtkTreePath to be activated.
9843 * @column: The #PsppSheetViewColumn to be activated.
9845 * Activates the cell determined by @path and @column.
9848 pspp_sheet_view_row_activated (PsppSheetView *tree_view,
9850 PsppSheetViewColumn *column)
9852 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9854 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
9859 * pspp_sheet_view_get_reorderable:
9860 * @tree_view: a #PsppSheetView
9862 * Retrieves whether the user can reorder the tree via drag-and-drop. See
9863 * pspp_sheet_view_set_reorderable().
9865 * Return value: %TRUE if the tree can be reordered.
9868 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
9870 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9872 return tree_view->priv->reorderable;
9876 * pspp_sheet_view_set_reorderable:
9877 * @tree_view: A #PsppSheetView.
9878 * @reorderable: %TRUE, if the tree can be reordered.
9880 * This function is a convenience function to allow you to reorder
9881 * models that support the #GtkDragSourceIface and the
9882 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
9883 * these. If @reorderable is %TRUE, then the user can reorder the
9884 * model by dragging and dropping rows. The developer can listen to
9885 * these changes by connecting to the model's row_inserted and
9886 * row_deleted signals. The reordering is implemented by setting up
9887 * the tree view as a drag source and destination. Therefore, drag and
9888 * drop can not be used in a reorderable view for any other purpose.
9890 * This function does not give you any degree of control over the order -- any
9891 * reordering is allowed. If more control is needed, you should probably
9892 * handle drag and drop manually.
9895 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
9896 gboolean reorderable)
9898 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9900 reorderable = reorderable != FALSE;
9902 if (tree_view->priv->reorderable == reorderable)
9907 const GtkTargetEntry row_targets[] = {
9908 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
9911 pspp_sheet_view_enable_model_drag_source (tree_view,
9914 G_N_ELEMENTS (row_targets),
9916 pspp_sheet_view_enable_model_drag_dest (tree_view,
9918 G_N_ELEMENTS (row_targets),
9923 pspp_sheet_view_unset_rows_drag_source (tree_view);
9924 pspp_sheet_view_unset_rows_drag_dest (tree_view);
9927 tree_view->priv->reorderable = reorderable;
9929 g_object_notify (G_OBJECT (tree_view), "reorderable");
9932 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
9933 is pressed, other rows will be unselected.
9935 If CLAMP_NODE is true, then the sheetview will scroll to make the row
9938 pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
9940 gboolean clear_and_select,
9941 gboolean clamp_node,
9942 PsppSheetSelectMode mode)
9946 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
9948 GtkTreePath *cursor_path;
9949 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
9950 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
9951 gtk_tree_path_free (cursor_path);
9954 gtk_tree_row_reference_free (tree_view->priv->cursor);
9955 tree_view->priv->cursor = NULL;
9957 _pspp_sheet_view_find_node (tree_view, path, &node);
9958 tree_view->priv->cursor =
9959 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
9960 tree_view->priv->model,
9963 if (tree_view->priv->row_count > 0)
9967 if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
9968 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
9972 /* We have to re-find tree and node here again, somebody might have
9973 * cleared the node or the whole tree in the PsppSheetSelection::changed
9974 * callback. If the nodes differ we bail out here.
9976 _pspp_sheet_view_find_node (tree_view, path, &new_node);
9978 if (node != new_node)
9983 pspp_sheet_view_clamp_node_visible (tree_view, node);
9984 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
9988 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
9992 * pspp_sheet_view_get_cursor:
9993 * @tree_view: A #PsppSheetView
9994 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
9995 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
9997 * Fills in @path and @focus_column with the current path and focus column. If
9998 * the cursor isn't currently set, then *@path will be %NULL. If no column
9999 * currently has focus, then *@focus_column will be %NULL.
10001 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
10002 * you are done with it.
10005 pspp_sheet_view_get_cursor (PsppSheetView *tree_view,
10006 GtkTreePath **path,
10007 PsppSheetViewColumn **focus_column)
10009 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10013 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10014 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10021 *focus_column = tree_view->priv->focus_column;
10026 * pspp_sheet_view_set_cursor:
10027 * @tree_view: A #PsppSheetView
10028 * @path: A #GtkTreePath
10029 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10030 * @start_editing: %TRUE if the specified cell should start being edited.
10032 * Sets the current keyboard focus to be at @path, and selects it. This is
10033 * useful when you want to focus the user's attention on a particular row. If
10034 * @focus_column is not %NULL, then focus is given to the column specified by
10035 * it. Additionally, if @focus_column is specified, and @start_editing is
10036 * %TRUE, then editing should be started in the specified cell.
10037 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10038 * in order to give keyboard focus to the widget. Please note that editing
10039 * can only happen when the widget is realized.
10041 * If @path is invalid for @model, the current cursor (if any) will be unset
10042 * and the function will return without failing.
10045 pspp_sheet_view_set_cursor (PsppSheetView *tree_view,
10047 PsppSheetViewColumn *focus_column,
10048 gboolean start_editing)
10050 pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10051 NULL, start_editing);
10055 * pspp_sheet_view_set_cursor_on_cell:
10056 * @tree_view: A #PsppSheetView
10057 * @path: A #GtkTreePath
10058 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10059 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10060 * @start_editing: %TRUE if the specified cell should start being edited.
10062 * Sets the current keyboard focus to be at @path, and selects it. This is
10063 * useful when you want to focus the user's attention on a particular row. If
10064 * @focus_column is not %NULL, then focus is given to the column specified by
10065 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10066 * contains 2 or more editable or activatable cells, then focus is given to
10067 * the cell specified by @focus_cell. Additionally, if @focus_column is
10068 * specified, and @start_editing is %TRUE, then editing should be started in
10069 * the specified cell. This function is often followed by
10070 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10071 * widget. Please note that editing can only happen when the widget is
10074 * If @path is invalid for @model, the current cursor (if any) will be unset
10075 * and the function will return without failing.
10080 pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view,
10082 PsppSheetViewColumn *focus_column,
10083 GtkCellRenderer *focus_cell,
10084 gboolean start_editing)
10086 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10087 g_return_if_fail (path != NULL);
10088 g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10090 if (!tree_view->priv->model)
10095 g_return_if_fail (focus_column);
10096 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10099 /* cancel the current editing, if it exists */
10100 if (tree_view->priv->edited_column &&
10101 tree_view->priv->edited_column->editable_widget)
10102 pspp_sheet_view_stop_editing (tree_view, TRUE);
10104 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
10106 if (focus_column && focus_column->visible)
10109 gboolean column_in_tree = FALSE;
10111 for (list = tree_view->priv->columns; list; list = list->next)
10112 if (list->data == focus_column)
10114 column_in_tree = TRUE;
10117 g_return_if_fail (column_in_tree);
10118 tree_view->priv->focus_column = focus_column;
10120 pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10122 pspp_sheet_view_start_editing (tree_view, path);
10124 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10125 pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10131 * pspp_sheet_view_get_bin_window:
10132 * @tree_view: A #PsppSheetView
10134 * Returns the window that @tree_view renders to. This is used primarily to
10135 * compare to <literal>event->window</literal> to confirm that the event on
10136 * @tree_view is on the right window.
10138 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10141 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10143 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10145 return tree_view->priv->bin_window;
10149 * pspp_sheet_view_get_path_at_pos:
10150 * @tree_view: A #PsppSheetView.
10151 * @x: The x position to be identified (relative to bin_window).
10152 * @y: The y position to be identified (relative to bin_window).
10153 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10154 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10155 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10156 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10158 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10159 * (please see pspp_sheet_view_get_bin_window()).
10160 * That is, @x and @y are relative to an events coordinates. @x and @y must
10161 * come from an event on the @tree_view only where <literal>event->window ==
10162 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10163 * things like popup menus. If @path is non-%NULL, then it will be filled
10164 * with the #GtkTreePath at that point. This path should be freed with
10165 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10166 * with the column at that point. @cell_x and @cell_y return the coordinates
10167 * relative to the cell background (i.e. the @background_area passed to
10168 * gtk_cell_renderer_render()). This function is only meaningful if
10169 * @tree_view is realized. Therefore this function will always return %FALSE
10170 * if @tree_view is not realized or does not have a model.
10172 * For converting widget coordinates (eg. the ones you get from
10173 * GtkWidget::query-tooltip), please see
10174 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10176 * Return value: %TRUE if a row exists at that coordinate.
10179 pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view,
10182 GtkTreePath **path,
10183 PsppSheetViewColumn **column,
10190 g_return_val_if_fail (tree_view != NULL, FALSE);
10197 if (tree_view->priv->bin_window == NULL)
10200 if (tree_view->priv->row_count == 0)
10203 if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment))
10206 if (x < 0 || y < 0)
10209 if (column || cell_x)
10211 PsppSheetViewColumn *tmp_column;
10212 PsppSheetViewColumn *last_column = NULL;
10214 gint remaining_x = x;
10215 gboolean found = FALSE;
10218 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10219 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10221 list = (rtl ? list->prev : list->next))
10223 tmp_column = list->data;
10225 if (tmp_column->visible == FALSE)
10228 last_column = tmp_column;
10229 if (remaining_x <= tmp_column->width)
10234 *column = tmp_column;
10237 *cell_x = remaining_x;
10241 remaining_x -= tmp_column->width;
10244 /* If found is FALSE and there is a last_column, then it the remainder
10245 * space is in that area
10252 *column = last_column;
10255 *cell_x = last_column->width + remaining_x;
10264 y_offset = pspp_sheet_view_find_offset (tree_view,
10265 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10272 *cell_y = y_offset;
10275 *path = _pspp_sheet_view_find_path (tree_view, node);
10280 /* Computes 'cell_area' from 'background_area', which must be the background
10281 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10282 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10283 the cell area as passed to _pspp_sheet_view_column_cell_render().
10285 'column' is required to properly adjust 'cell_area->x' and
10286 'cell_area->width'. It may be set to NULL if these values are not of
10287 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10290 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
10291 PsppSheetViewColumn *column,
10292 const GdkRectangle *background_area,
10293 gboolean subtract_focus_rect,
10294 GdkRectangle *cell_area)
10296 gint vertical_separator;
10297 gint horizontal_separator;
10299 *cell_area = *background_area;
10301 gtk_widget_style_get (GTK_WIDGET (tree_view),
10302 "vertical-separator", &vertical_separator,
10303 "horizontal-separator", &horizontal_separator,
10305 cell_area->x += horizontal_separator / 2;
10306 cell_area->y += vertical_separator / 2;
10307 cell_area->width -= horizontal_separator;
10308 cell_area->height -= vertical_separator;
10310 if (subtract_focus_rect)
10312 int focus_line_width;
10314 gtk_widget_style_get (GTK_WIDGET (tree_view),
10315 "focus-line-width", &focus_line_width,
10317 cell_area->x += focus_line_width;
10318 cell_area->y += focus_line_width;
10319 cell_area->width -= 2 * focus_line_width;
10320 cell_area->height -= 2 * focus_line_width;
10323 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10325 gint grid_line_width;
10326 gtk_widget_style_get (GTK_WIDGET (tree_view),
10327 "grid-line-width", &grid_line_width,
10330 if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10331 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10334 PsppSheetViewColumn *first_column, *last_column;
10337 /* Find the last visible column. */
10338 last_column = NULL;
10339 for (list = g_list_last (tree_view->priv->columns);
10343 PsppSheetViewColumn *c = list->data;
10351 /* Find the first visible column. */
10352 first_column = NULL;
10353 for (list = g_list_first (tree_view->priv->columns);
10357 PsppSheetViewColumn *c = list->data;
10365 if (column == first_column)
10367 cell_area->width -= grid_line_width / 2;
10369 else if (column == last_column)
10371 cell_area->x += grid_line_width / 2;
10372 cell_area->width -= grid_line_width / 2;
10376 cell_area->x += grid_line_width / 2;
10377 cell_area->width -= grid_line_width;
10381 if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10382 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10384 cell_area->y += grid_line_width / 2;
10385 cell_area->height -= grid_line_width;
10389 if (column == NULL)
10392 cell_area->width = 0;
10397 * pspp_sheet_view_get_cell_area:
10398 * @tree_view: a #PsppSheetView
10399 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10400 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10401 * @rect: rectangle to fill with cell rect
10403 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10404 * row specified by @path and the column specified by @column. If @path is
10405 * %NULL, or points to a path not currently displayed, the @y and @height fields
10406 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10407 * fields will be filled with 0. The sum of all cell rects does not cover the
10408 * entire tree; there are extra pixels in between rows, for example. The
10409 * returned rectangle is equivalent to the @cell_area passed to
10410 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10414 pspp_sheet_view_get_cell_area (PsppSheetView *tree_view,
10416 PsppSheetViewColumn *column,
10417 GdkRectangle *rect)
10419 GdkRectangle background_area;
10421 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10422 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10423 g_return_if_fail (rect != NULL);
10424 g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10425 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10427 pspp_sheet_view_get_background_area (tree_view, path, column,
10429 pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10434 * pspp_sheet_view_get_background_area:
10435 * @tree_view: a #PsppSheetView
10436 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10437 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10438 * @rect: rectangle to fill with cell background rect
10440 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10441 * row specified by @path and the column specified by @column. If @path is
10442 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10443 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10444 * fields will be filled with 0. The returned rectangle is equivalent to the
10445 * @background_area passed to gtk_cell_renderer_render(). These background
10446 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10447 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10448 * itself, excluding surrounding borders.
10452 pspp_sheet_view_get_background_area (PsppSheetView *tree_view,
10454 PsppSheetViewColumn *column,
10455 GdkRectangle *rect)
10459 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10460 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10461 g_return_if_fail (rect != NULL);
10470 /* Get vertical coords */
10472 _pspp_sheet_view_find_node (tree_view, path, &node);
10476 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10478 rect->height = ROW_HEIGHT (tree_view);
10485 pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10486 rect->width = x2 - rect->x;
10491 * pspp_sheet_view_get_visible_rect:
10492 * @tree_view: a #PsppSheetView
10493 * @visible_rect: rectangle to fill
10495 * Fills @visible_rect with the currently-visible region of the
10496 * buffer, in tree coordinates. Convert to bin_window coordinates with
10497 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10498 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10499 * scrollable area of the tree.
10502 pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view,
10503 GdkRectangle *visible_rect)
10507 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10509 widget = GTK_WIDGET (tree_view);
10513 GtkAllocation allocation;
10514 gtk_widget_get_allocation (widget, &allocation);
10515 visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
10516 visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
10517 visible_rect->width = allocation.width;
10518 visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10523 * pspp_sheet_view_widget_to_tree_coords:
10524 * @tree_view: a #PsppSheetView
10525 * @wx: X coordinate relative to bin_window
10526 * @wy: Y coordinate relative to bin_window
10527 * @tx: return location for tree X coordinate
10528 * @ty: return location for tree Y coordinate
10530 * Converts bin_window coordinates to coordinates for the
10531 * tree (the full scrollable area of the tree).
10533 * Deprecated: 2.12: Due to historial reasons the name of this function is
10534 * incorrect. For converting coordinates relative to the widget to
10535 * bin_window coordinates, please see
10536 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10540 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10546 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10549 *tx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10551 *ty = wy + tree_view->priv->dy;
10555 * pspp_sheet_view_tree_to_widget_coords:
10556 * @tree_view: a #PsppSheetView
10557 * @tx: tree X coordinate
10558 * @ty: tree Y coordinate
10559 * @wx: return location for X coordinate relative to bin_window
10560 * @wy: return location for Y coordinate relative to bin_window
10562 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10563 * to bin_window coordinates.
10565 * Deprecated: 2.12: Due to historial reasons the name of this function is
10566 * incorrect. For converting bin_window coordinates to coordinates relative
10567 * to bin_window, please see
10568 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10572 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10578 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10581 *wx = tx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10583 *wy = ty - tree_view->priv->dy;
10588 * pspp_sheet_view_convert_widget_to_tree_coords:
10589 * @tree_view: a #PsppSheetView
10590 * @wx: X coordinate relative to the widget
10591 * @wy: Y coordinate relative to the widget
10592 * @tx: return location for tree X coordinate
10593 * @ty: return location for tree Y coordinate
10595 * Converts widget coordinates to coordinates for the
10596 * tree (the full scrollable area of the tree).
10601 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10609 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10611 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10614 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10620 * pspp_sheet_view_convert_tree_to_widget_coords:
10621 * @tree_view: a #PsppSheetView
10622 * @tx: X coordinate relative to the tree
10623 * @ty: Y coordinate relative to the tree
10624 * @wx: return location for widget X coordinate
10625 * @wy: return location for widget Y coordinate
10627 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10628 * to widget coordinates.
10633 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10641 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10643 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10646 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10652 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10653 * @tree_view: a #PsppSheetView
10654 * @wx: X coordinate relative to the widget
10655 * @wy: Y coordinate relative to the widget
10656 * @bx: return location for bin_window X coordinate
10657 * @by: return location for bin_window Y coordinate
10659 * Converts widget coordinates to coordinates for the bin_window
10660 * (see pspp_sheet_view_get_bin_window()).
10665 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10671 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10674 *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10676 *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10680 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10681 * @tree_view: a #PsppSheetView
10682 * @bx: bin_window X coordinate
10683 * @by: bin_window Y coordinate
10684 * @wx: return location for widget X coordinate
10685 * @wy: return location for widget Y coordinate
10687 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10688 * to widget relative coordinates.
10693 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10699 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10702 *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10704 *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10708 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10709 * @tree_view: a #PsppSheetView
10710 * @tx: tree X coordinate
10711 * @ty: tree Y coordinate
10712 * @bx: return location for X coordinate relative to bin_window
10713 * @by: return location for Y coordinate relative to bin_window
10715 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10716 * to bin_window coordinates.
10721 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10727 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10732 *by = ty - tree_view->priv->dy;
10736 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10737 * @tree_view: a #PsppSheetView
10738 * @bx: X coordinate relative to bin_window
10739 * @by: Y coordinate relative to bin_window
10740 * @tx: return location for tree X coordinate
10741 * @ty: return location for tree Y coordinate
10743 * Converts bin_window coordinates to coordinates for the
10744 * tree (the full scrollable area of the tree).
10749 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10755 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10760 *ty = by + tree_view->priv->dy;
10766 * pspp_sheet_view_get_visible_range:
10767 * @tree_view: A #PsppSheetView
10768 * @start_path: (allow-none): Return location for start of region, or %NULL.
10769 * @end_path: (allow-none): Return location for end of region, or %NULL.
10771 * Sets @start_path and @end_path to be the first and last visible path.
10772 * Note that there may be invisible paths in between.
10774 * The paths should be freed with gtk_tree_path_free() after use.
10776 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
10781 pspp_sheet_view_get_visible_range (PsppSheetView *tree_view,
10782 GtkTreePath **start_path,
10783 GtkTreePath **end_path)
10788 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10790 if (!tree_view->priv->row_count)
10797 pspp_sheet_view_find_offset (tree_view,
10798 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
10801 *start_path = _pspp_sheet_view_find_path (tree_view, node);
10810 if (tree_view->priv->height < gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
10811 y = tree_view->priv->height - 1;
10813 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1;
10815 pspp_sheet_view_find_offset (tree_view, y, &node);
10817 *end_path = _pspp_sheet_view_find_path (tree_view, node);
10826 unset_reorderable (PsppSheetView *tree_view)
10828 if (tree_view->priv->reorderable)
10830 tree_view->priv->reorderable = FALSE;
10831 g_object_notify (G_OBJECT (tree_view), "reorderable");
10836 * pspp_sheet_view_enable_model_drag_source:
10837 * @tree_view: a #PsppSheetView
10838 * @start_button_mask: Mask of allowed buttons to start drag
10839 * @targets: the table of targets that the drag will support
10840 * @n_targets: the number of items in @targets
10841 * @actions: the bitmask of possible actions for a drag from this
10844 * Turns @tree_view into a drag source for automatic DND. Calling this
10845 * method sets #PsppSheetView:reorderable to %FALSE.
10848 pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view,
10849 GdkModifierType start_button_mask,
10850 const GtkTargetEntry *targets,
10852 GdkDragAction actions)
10854 TreeViewDragInfo *di;
10856 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10858 gtk_drag_source_set (GTK_WIDGET (tree_view),
10864 di = ensure_info (tree_view);
10866 di->start_button_mask = start_button_mask;
10867 di->source_actions = actions;
10868 di->source_set = TRUE;
10870 unset_reorderable (tree_view);
10874 * pspp_sheet_view_enable_model_drag_dest:
10875 * @tree_view: a #PsppSheetView
10876 * @targets: the table of targets that the drag will support
10877 * @n_targets: the number of items in @targets
10878 * @actions: the bitmask of possible actions for a drag from this
10881 * Turns @tree_view into a drop destination for automatic DND. Calling
10882 * this method sets #PsppSheetView:reorderable to %FALSE.
10885 pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view,
10886 const GtkTargetEntry *targets,
10888 GdkDragAction actions)
10890 TreeViewDragInfo *di;
10892 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10894 gtk_drag_dest_set (GTK_WIDGET (tree_view),
10900 di = ensure_info (tree_view);
10901 di->dest_set = TRUE;
10903 unset_reorderable (tree_view);
10907 * pspp_sheet_view_unset_rows_drag_source:
10908 * @tree_view: a #PsppSheetView
10910 * Undoes the effect of
10911 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
10912 * #PsppSheetView:reorderable to %FALSE.
10915 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
10917 TreeViewDragInfo *di;
10919 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10921 di = get_info (tree_view);
10925 if (di->source_set)
10927 gtk_drag_source_unset (GTK_WIDGET (tree_view));
10928 di->source_set = FALSE;
10931 if (!di->dest_set && !di->source_set)
10932 remove_info (tree_view);
10935 unset_reorderable (tree_view);
10939 * pspp_sheet_view_unset_rows_drag_dest:
10940 * @tree_view: a #PsppSheetView
10942 * Undoes the effect of
10943 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
10944 * #PsppSheetView:reorderable to %FALSE.
10947 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
10949 TreeViewDragInfo *di;
10951 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10953 di = get_info (tree_view);
10959 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
10960 di->dest_set = FALSE;
10963 if (!di->dest_set && !di->source_set)
10964 remove_info (tree_view);
10967 unset_reorderable (tree_view);
10971 * pspp_sheet_view_set_drag_dest_row:
10972 * @tree_view: a #PsppSheetView
10973 * @path: (allow-none): The path of the row to highlight, or %NULL.
10974 * @pos: Specifies whether to drop before, after or into the row
10976 * Sets the row that is highlighted for feedback.
10979 pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view,
10981 PsppSheetViewDropPosition pos)
10983 GtkTreePath *current_dest;
10985 /* Note; this function is exported to allow a custom DND
10986 * implementation, so it can't touch TreeViewDragInfo
10989 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10991 current_dest = NULL;
10993 if (tree_view->priv->drag_dest_row)
10995 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
10996 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
10999 /* special case a drop on an empty model */
11000 tree_view->priv->empty_view_drop = 0;
11002 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
11003 && gtk_tree_path_get_depth (path) == 1
11004 && gtk_tree_path_get_indices (path)[0] == 0)
11008 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
11012 tree_view->priv->empty_view_drop = 1;
11015 tree_view->priv->drag_dest_pos = pos;
11019 tree_view->priv->drag_dest_row =
11020 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
11021 pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
11024 tree_view->priv->drag_dest_row = NULL;
11028 int node, new_node;
11030 _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11031 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11035 new_node = pspp_sheet_view_node_next (tree_view, node);
11037 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11039 new_node = pspp_sheet_view_node_prev (tree_view, node);
11041 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11043 gtk_tree_path_free (current_dest);
11048 * pspp_sheet_view_get_drag_dest_row:
11049 * @tree_view: a #PsppSheetView
11050 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11051 * @pos: (allow-none): Return location for the drop position, or %NULL
11053 * Gets information about the row that is highlighted for feedback.
11056 pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view,
11057 GtkTreePath **path,
11058 PsppSheetViewDropPosition *pos)
11060 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11064 if (tree_view->priv->drag_dest_row)
11065 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11068 if (tree_view->priv->empty_view_drop)
11069 *path = gtk_tree_path_new_from_indices (0, -1);
11076 *pos = tree_view->priv->drag_dest_pos;
11080 * pspp_sheet_view_get_dest_row_at_pos:
11081 * @tree_view: a #PsppSheetView
11082 * @drag_x: the position to determine the destination row for
11083 * @drag_y: the position to determine the destination row for
11084 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11085 * @pos: (allow-none): Return location for the drop position, or %NULL
11087 * Determines the destination row for a given position. @drag_x and
11088 * @drag_y are expected to be in widget coordinates. This function is only
11089 * meaningful if @tree_view is realized. Therefore this function will always
11090 * return %FALSE if @tree_view is not realized or does not have a model.
11092 * Return value: whether there is a row at the given position, %TRUE if this
11093 * is indeed the case.
11096 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view,
11099 GtkTreePath **path,
11100 PsppSheetViewDropPosition *pos)
11104 gdouble offset_into_row;
11107 PsppSheetViewColumn *column = NULL;
11108 GtkTreePath *tmp_path = NULL;
11110 /* Note; this function is exported to allow a custom DND
11111 * implementation, so it can't touch TreeViewDragInfo
11114 g_return_val_if_fail (tree_view != NULL, FALSE);
11115 g_return_val_if_fail (drag_x >= 0, FALSE);
11116 g_return_val_if_fail (drag_y >= 0, FALSE);
11121 if (tree_view->priv->bin_window == NULL)
11124 if (tree_view->priv->row_count == 0)
11127 /* If in the top third of a row, we drop before that row; if
11128 * in the bottom third, drop after that row; if in the middle,
11129 * and the row has children, drop into the row.
11131 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11134 if (!pspp_sheet_view_get_path_at_pos (tree_view,
11143 pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11146 offset_into_row = cell_y;
11151 gtk_tree_path_free (tmp_path);
11155 third = cell.height / 3.0;
11159 if (offset_into_row < third)
11161 *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11163 else if (offset_into_row < (cell.height / 2.0))
11165 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11167 else if (offset_into_row < third * 2.0)
11169 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11173 *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11181 #if GTK3_TRANSITION
11182 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11184 * pspp_sheet_view_create_row_drag_icon:
11185 * @tree_view: a #PsppSheetView
11186 * @path: a #GtkTreePath in @tree_view
11188 * Creates a #GdkPixmap representation of the row at @path.
11189 * This image is used for a drag icon.
11191 * Return value: a newly-allocated pixmap of the drag icon.
11194 pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view,
11201 GdkRectangle background_area;
11202 GdkRectangle expose_area;
11204 /* start drawing inside the black outline */
11206 GdkDrawable *drawable;
11207 gint bin_window_width;
11210 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11211 g_return_val_if_fail (path != NULL, NULL);
11213 widget = GTK_WIDGET (tree_view);
11215 if (!gtk_widget_get_realized (widget))
11218 _pspp_sheet_view_find_node (tree_view,
11225 if (!gtk_tree_model_get_iter (tree_view->priv->model,
11232 background_area.y = y;
11233 background_area.height = ROW_HEIGHT (tree_view);
11235 bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
11237 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11238 bin_window_width + 2,
11239 background_area.height + 2,
11244 expose_area.width = bin_window_width + 2;
11245 expose_area.height = background_area.height + 2;
11247 #if GTK3_TRANSITION
11248 gdk_draw_rectangle (drawable,
11249 widget->style->base_gc [gtk_widget_get_state (widget)],
11252 bin_window_width + 2,
11253 background_area.height + 2);
11256 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11258 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11260 list = (rtl ? list->prev : list->next))
11262 PsppSheetViewColumn *column = list->data;
11263 GdkRectangle cell_area;
11264 gint vertical_separator;
11266 if (!column->visible)
11269 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11271 background_area.x = cell_offset;
11272 background_area.width = column->width;
11274 gtk_widget_style_get (widget,
11275 "vertical-separator", &vertical_separator,
11278 cell_area = background_area;
11280 cell_area.y += vertical_separator / 2;
11281 cell_area.height -= vertical_separator;
11283 if (pspp_sheet_view_column_cell_is_visible (column))
11284 _pspp_sheet_view_column_cell_render (column,
11290 cell_offset += column->width;
11293 #if GTK3_TRANSITION
11294 gdk_draw_rectangle (drawable,
11295 widget->style->black_gc,
11298 bin_window_width + 1,
11299 background_area.height + 1);
11307 * pspp_sheet_view_set_destroy_count_func:
11308 * @tree_view: A #PsppSheetView
11309 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11310 * @data: (allow-none): User data to be passed to @func, or %NULL
11311 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11313 * This function should almost never be used. It is meant for private use by
11314 * ATK for determining the number of visible children that are removed when a row is deleted.
11317 pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view,
11318 PsppSheetDestroyCountFunc func,
11320 GDestroyNotify destroy)
11322 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11324 if (tree_view->priv->destroy_count_destroy)
11325 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11327 tree_view->priv->destroy_count_func = func;
11328 tree_view->priv->destroy_count_data = data;
11329 tree_view->priv->destroy_count_destroy = destroy;
11334 * Interactive search
11338 * pspp_sheet_view_set_enable_search:
11339 * @tree_view: A #PsppSheetView
11340 * @enable_search: %TRUE, if the user can search interactively
11342 * If @enable_search is set, then the user can type in text to search through
11343 * the tree interactively (this is sometimes called "typeahead find").
11345 * Note that even if this is %FALSE, the user can still initiate a search
11346 * using the "start-interactive-search" key binding.
11349 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11350 gboolean enable_search)
11352 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11354 enable_search = !!enable_search;
11356 if (tree_view->priv->enable_search != enable_search)
11358 tree_view->priv->enable_search = enable_search;
11359 g_object_notify (G_OBJECT (tree_view), "enable-search");
11364 * pspp_sheet_view_get_enable_search:
11365 * @tree_view: A #PsppSheetView
11367 * Returns whether or not the tree allows to start interactive searching
11368 * by typing in text.
11370 * Return value: whether or not to let the user search interactively
11373 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11375 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11377 return tree_view->priv->enable_search;
11382 * pspp_sheet_view_get_search_column:
11383 * @tree_view: A #PsppSheetView
11385 * Gets the column searched on by the interactive search code.
11387 * Return value: the column the interactive search code searches in.
11390 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11392 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11394 return (tree_view->priv->search_column);
11398 * pspp_sheet_view_set_search_column:
11399 * @tree_view: A #PsppSheetView
11400 * @column: the column of the model to search in, or -1 to disable searching
11402 * Sets @column as the column where the interactive search code should
11403 * search in for the current model.
11405 * If the search column is set, users can use the "start-interactive-search"
11406 * key binding to bring up search popup. The enable-search property controls
11407 * whether simply typing text will also start an interactive search.
11409 * Note that @column refers to a column of the current model. The search
11410 * column is reset to -1 when the model is changed.
11413 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11416 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11417 g_return_if_fail (column >= -1);
11419 if (tree_view->priv->search_column == column)
11422 tree_view->priv->search_column = column;
11423 g_object_notify (G_OBJECT (tree_view), "search-column");
11427 * pspp_sheet_view_get_search_equal_func:
11428 * @tree_view: A #PsppSheetView
11430 * Returns the compare function currently in use.
11432 * Return value: the currently used compare function for the search code.
11435 PsppSheetViewSearchEqualFunc
11436 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11438 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11440 return tree_view->priv->search_equal_func;
11444 * pspp_sheet_view_set_search_equal_func:
11445 * @tree_view: A #PsppSheetView
11446 * @search_equal_func: the compare function to use during the search
11447 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11448 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11450 * Sets the compare function for the interactive search capabilities; note
11451 * that somewhat like strcmp() returning 0 for equality
11452 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11455 pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view,
11456 PsppSheetViewSearchEqualFunc search_equal_func,
11457 gpointer search_user_data,
11458 GDestroyNotify search_destroy)
11460 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11461 g_return_if_fail (search_equal_func != NULL);
11463 if (tree_view->priv->search_destroy)
11464 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11466 tree_view->priv->search_equal_func = search_equal_func;
11467 tree_view->priv->search_user_data = search_user_data;
11468 tree_view->priv->search_destroy = search_destroy;
11469 if (tree_view->priv->search_equal_func == NULL)
11470 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11474 * pspp_sheet_view_get_search_entry:
11475 * @tree_view: A #PsppSheetView
11477 * Returns the #GtkEntry which is currently in use as interactive search
11478 * entry for @tree_view. In case the built-in entry is being used, %NULL
11479 * will be returned.
11481 * Return value: the entry currently in use as search entry.
11486 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11488 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11490 if (tree_view->priv->search_custom_entry_set)
11491 return GTK_ENTRY (tree_view->priv->search_entry);
11497 * pspp_sheet_view_set_search_entry:
11498 * @tree_view: A #PsppSheetView
11499 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11501 * Sets the entry which the interactive search code will use for this
11502 * @tree_view. This is useful when you want to provide a search entry
11503 * in our interface at all time at a fixed position. Passing %NULL for
11504 * @entry will make the interactive search code use the built-in popup
11510 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11513 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11514 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11516 if (tree_view->priv->search_custom_entry_set)
11518 if (tree_view->priv->search_entry_changed_id)
11520 g_signal_handler_disconnect (tree_view->priv->search_entry,
11521 tree_view->priv->search_entry_changed_id);
11522 tree_view->priv->search_entry_changed_id = 0;
11524 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11525 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11528 g_object_unref (tree_view->priv->search_entry);
11530 else if (tree_view->priv->search_window)
11532 gtk_widget_destroy (tree_view->priv->search_window);
11534 tree_view->priv->search_window = NULL;
11539 tree_view->priv->search_entry = g_object_ref (entry);
11540 tree_view->priv->search_custom_entry_set = TRUE;
11542 if (tree_view->priv->search_entry_changed_id == 0)
11544 tree_view->priv->search_entry_changed_id =
11545 g_signal_connect (tree_view->priv->search_entry, "changed",
11546 G_CALLBACK (pspp_sheet_view_search_init),
11550 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11551 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11554 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11558 tree_view->priv->search_entry = NULL;
11559 tree_view->priv->search_custom_entry_set = FALSE;
11564 * pspp_sheet_view_set_search_position_func:
11565 * @tree_view: A #PsppSheetView
11566 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11567 * to use the default search position function
11568 * @data: (allow-none): user data to pass to @func, or %NULL
11569 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11571 * Sets the function to use when positioning the search dialog.
11576 pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view,
11577 PsppSheetViewSearchPositionFunc func,
11578 gpointer user_data,
11579 GDestroyNotify destroy)
11581 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11583 if (tree_view->priv->search_position_destroy)
11584 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11586 tree_view->priv->search_position_func = func;
11587 tree_view->priv->search_position_user_data = user_data;
11588 tree_view->priv->search_position_destroy = destroy;
11589 if (tree_view->priv->search_position_func == NULL)
11590 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11594 * pspp_sheet_view_get_search_position_func:
11595 * @tree_view: A #PsppSheetView
11597 * Returns the positioning function currently in use.
11599 * Return value: the currently used function for positioning the search dialog.
11603 PsppSheetViewSearchPositionFunc
11604 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11606 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11608 return tree_view->priv->search_position_func;
11613 pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
11614 PsppSheetView *tree_view)
11616 if (tree_view->priv->disable_popdown)
11619 if (tree_view->priv->search_entry_changed_id)
11621 g_signal_handler_disconnect (tree_view->priv->search_entry,
11622 tree_view->priv->search_entry_changed_id);
11623 tree_view->priv->search_entry_changed_id = 0;
11625 if (tree_view->priv->typeselect_flush_timeout)
11627 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11628 tree_view->priv->typeselect_flush_timeout = 0;
11631 if (gtk_widget_get_visible (search_dialog))
11633 /* send focus-in event */
11634 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11635 gtk_widget_hide (search_dialog);
11636 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11637 send_focus_change (GTK_WIDGET (tree_view), TRUE);
11642 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11643 GtkWidget *search_dialog,
11644 gpointer user_data)
11647 gint tree_x, tree_y;
11648 gint tree_width, tree_height;
11649 GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
11650 GdkScreen *screen = gdk_window_get_screen (tree_window);
11651 GtkRequisition requisition;
11653 GdkRectangle monitor;
11655 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11656 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11658 gtk_widget_realize (search_dialog);
11660 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11661 tree_width = gdk_window_get_width (tree_window);
11662 tree_height = gdk_window_get_height (tree_window);
11664 gtk_widget_size_request (search_dialog, &requisition);
11666 if (tree_x + tree_width > gdk_screen_get_width (screen))
11667 x = gdk_screen_get_width (screen) - requisition.width;
11668 else if (tree_x + tree_width - requisition.width < 0)
11671 x = tree_x + tree_width - requisition.width;
11673 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11674 y = gdk_screen_get_height (screen) - requisition.height;
11675 else if (tree_y + tree_height < 0) /* isn't really possible ... */
11678 y = tree_y + tree_height;
11680 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11684 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11688 PsppSheetView *tree_view = (PsppSheetView *)data;
11690 tree_view->priv->disable_popdown = 1;
11691 g_signal_connect (menu, "hide",
11692 G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11695 #if GTK3_TRANSITION
11696 /* Because we're visible but offscreen, we just set a flag in the preedit
11700 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11701 PsppSheetView *tree_view)
11703 tree_view->priv->imcontext_changed = 1;
11704 if (tree_view->priv->typeselect_flush_timeout)
11706 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11707 tree_view->priv->typeselect_flush_timeout =
11708 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11709 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11717 pspp_sheet_view_search_activate (GtkEntry *entry,
11718 PsppSheetView *tree_view)
11723 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11726 /* If we have a row selected and it's the cursor row, we activate
11728 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11730 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11732 _pspp_sheet_view_find_node (tree_view, path, &node);
11734 if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11735 pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11737 gtk_tree_path_free (path);
11742 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11744 PsppSheetView *tree_view = (PsppSheetView *)data;
11746 tree_view->priv->disable_popdown = 0;
11752 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11755 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11759 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11760 GdkEventAny *event,
11761 PsppSheetView *tree_view)
11763 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11765 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11771 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11772 GdkEventButton *event,
11773 PsppSheetView *tree_view)
11775 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11777 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11779 if (event->window == tree_view->priv->bin_window)
11780 pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
11786 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
11787 GdkEventScroll *event,
11788 PsppSheetView *tree_view)
11790 gboolean retval = FALSE;
11792 if (event->direction == GDK_SCROLL_UP)
11794 pspp_sheet_view_search_move (widget, tree_view, TRUE);
11797 else if (event->direction == GDK_SCROLL_DOWN)
11799 pspp_sheet_view_search_move (widget, tree_view, FALSE);
11803 /* renew the flush timeout */
11804 if (retval && tree_view->priv->typeselect_flush_timeout
11805 && !tree_view->priv->search_custom_entry_set)
11807 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11808 tree_view->priv->typeselect_flush_timeout =
11809 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11810 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11818 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
11819 GdkEventKey *event,
11820 PsppSheetView *tree_view)
11822 gboolean retval = FALSE;
11824 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11825 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11827 /* close window and cancel the search */
11828 if (!tree_view->priv->search_custom_entry_set
11829 && (event->keyval == GDK_Escape ||
11830 event->keyval == GDK_Tab ||
11831 event->keyval == GDK_KP_Tab ||
11832 event->keyval == GDK_ISO_Left_Tab))
11834 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11838 /* select previous matching iter */
11839 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
11841 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11842 gtk_widget_error_bell (widget);
11847 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
11848 && (event->keyval == GDK_g || event->keyval == GDK_G))
11850 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11851 gtk_widget_error_bell (widget);
11856 /* select next matching iter */
11857 if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
11859 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11860 gtk_widget_error_bell (widget);
11865 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
11866 && (event->keyval == GDK_g || event->keyval == GDK_G))
11868 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11869 gtk_widget_error_bell (widget);
11874 /* renew the flush timeout */
11875 if (retval && tree_view->priv->typeselect_flush_timeout
11876 && !tree_view->priv->search_custom_entry_set)
11878 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11879 tree_view->priv->typeselect_flush_timeout =
11880 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11881 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11888 /* this function returns FALSE if there is a search string but
11889 * nothing was found, and TRUE otherwise.
11892 pspp_sheet_view_search_move (GtkWidget *window,
11893 PsppSheetView *tree_view,
11901 GtkTreeModel *model;
11902 PsppSheetSelection *selection;
11904 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
11906 g_return_val_if_fail (text != NULL, FALSE);
11908 len = strlen (text);
11910 if (up && tree_view->priv->selected_iter == 1)
11911 return strlen (text) < 1;
11913 len = strlen (text);
11918 model = pspp_sheet_view_get_model (tree_view);
11919 selection = pspp_sheet_view_get_selection (tree_view);
11922 pspp_sheet_selection_unselect_all (selection);
11923 if (!gtk_tree_model_get_iter_first (model, &iter))
11926 ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
11927 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
11932 tree_view->priv->selected_iter += up?(-1):(1);
11937 /* return to old iter */
11939 gtk_tree_model_get_iter_first (model, &iter);
11940 pspp_sheet_view_search_iter (model, selection,
11942 &count, tree_view->priv->selected_iter);
11948 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
11952 gpointer search_data)
11954 gboolean retval = TRUE;
11956 gchar *normalized_string;
11957 gchar *normalized_key;
11958 gchar *case_normalized_string = NULL;
11959 gchar *case_normalized_key = NULL;
11960 GValue value = {0,};
11961 GValue transformed = {0,};
11963 gtk_tree_model_get_value (model, iter, column, &value);
11965 g_value_init (&transformed, G_TYPE_STRING);
11967 if (!g_value_transform (&value, &transformed))
11969 g_value_unset (&value);
11973 g_value_unset (&value);
11975 str = g_value_get_string (&transformed);
11978 g_value_unset (&transformed);
11982 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
11983 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
11985 if (normalized_string && normalized_key)
11987 case_normalized_string = g_utf8_casefold (normalized_string, -1);
11988 case_normalized_key = g_utf8_casefold (normalized_key, -1);
11990 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
11994 g_value_unset (&transformed);
11995 g_free (normalized_key);
11996 g_free (normalized_string);
11997 g_free (case_normalized_key);
11998 g_free (case_normalized_string);
12004 pspp_sheet_view_search_iter (GtkTreeModel *model,
12005 PsppSheetSelection *selection,
12014 PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
12016 path = gtk_tree_model_get_path (model, iter);
12017 _pspp_sheet_view_find_node (tree_view, path, &node);
12021 gboolean done = FALSE;
12023 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
12028 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
12030 pspp_sheet_selection_select_iter (selection, iter);
12031 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12034 gtk_tree_path_free (path);
12043 node = pspp_sheet_view_node_next (tree_view, node);
12049 has_next = gtk_tree_model_iter_next (model, iter);
12052 gtk_tree_path_next (path);
12055 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12060 gtk_tree_path_free (path);
12062 /* we've run out of tree, done with this func */
12074 pspp_sheet_view_search_init (GtkWidget *entry,
12075 PsppSheetView *tree_view)
12081 GtkTreeModel *model;
12082 PsppSheetSelection *selection;
12084 g_return_if_fail (GTK_IS_ENTRY (entry));
12085 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12087 text = gtk_entry_get_text (GTK_ENTRY (entry));
12089 model = pspp_sheet_view_get_model (tree_view);
12090 selection = pspp_sheet_view_get_selection (tree_view);
12093 pspp_sheet_selection_unselect_all (selection);
12094 if (tree_view->priv->typeselect_flush_timeout
12095 && !tree_view->priv->search_custom_entry_set)
12097 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12098 tree_view->priv->typeselect_flush_timeout =
12099 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12100 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12107 if (!gtk_tree_model_get_iter_first (model, &iter))
12110 ret = pspp_sheet_view_search_iter (model, selection,
12115 tree_view->priv->selected_iter = 1;
12119 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12120 PsppSheetView *tree_view)
12122 if (tree_view->priv->edited_column == NULL)
12125 _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12126 tree_view->priv->edited_column = NULL;
12128 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12129 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12131 g_signal_handlers_disconnect_by_func (cell_editable,
12132 pspp_sheet_view_remove_widget,
12134 g_signal_handlers_disconnect_by_func (cell_editable,
12135 pspp_sheet_view_editable_button_press_event,
12137 g_signal_handlers_disconnect_by_func (cell_editable,
12138 pspp_sheet_view_editable_clicked,
12141 gtk_container_remove (GTK_CONTAINER (tree_view),
12142 GTK_WIDGET (cell_editable));
12144 /* FIXME should only redraw a single node */
12145 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12149 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12150 GtkTreePath *cursor_path)
12153 GdkRectangle background_area;
12154 GdkRectangle cell_area;
12155 GtkCellEditable *editable_widget = NULL;
12156 gchar *path_string;
12157 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12158 gint retval = FALSE;
12161 g_assert (tree_view->priv->focus_column);
12163 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12166 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12167 if (cursor_node < 0)
12170 path_string = gtk_tree_path_to_string (cursor_path);
12171 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12173 pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12174 tree_view->priv->model,
12176 pspp_sheet_view_get_background_area (tree_view,
12178 tree_view->priv->focus_column,
12180 pspp_sheet_view_get_cell_area (tree_view,
12182 tree_view->priv->focus_column,
12185 if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12194 if (editable_widget != NULL)
12198 GtkCellRenderer *cell;
12201 cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12203 _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12206 area.width -= right + left;
12208 pspp_sheet_view_real_start_editing (tree_view,
12209 tree_view->priv->focus_column,
12218 g_free (path_string);
12223 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12224 GdkEventButton *event,
12225 PsppSheetView *sheet_view)
12229 node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12230 "pspp-sheet-view-node"));
12231 return pspp_sheet_view_row_head_clicked (sheet_view,
12233 sheet_view->priv->edited_column,
12238 pspp_sheet_view_editable_clicked (GtkButton *button,
12239 PsppSheetView *sheet_view)
12241 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12246 is_all_selected (GtkWidget *widget)
12248 GtkEntryBuffer *buffer;
12249 gint start_pos, end_pos;
12251 if (!GTK_IS_ENTRY (widget))
12254 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12255 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12256 &start_pos, &end_pos)
12258 && end_pos == gtk_entry_buffer_get_length (buffer));
12262 is_at_left (GtkWidget *widget)
12264 return (GTK_IS_ENTRY (widget)
12265 && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12269 is_at_right (GtkWidget *widget)
12271 GtkEntryBuffer *buffer;
12274 if (!GTK_IS_ENTRY (widget))
12277 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12278 length = gtk_entry_buffer_get_length (buffer);
12279 return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12283 pspp_sheet_view_event (GtkWidget *widget,
12284 GdkEventKey *event,
12285 PsppSheetView *tree_view)
12287 PsppSheetViewColumn *column;
12294 /* Intercept only key press events.
12295 It would make sense to use "key-press-event" instead of "event", but
12296 GtkEntry attaches its own signal handler to "key-press-event" that runs
12297 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12299 if (event->type != GDK_KEY_PRESS)
12302 keyval = event->keyval;
12304 switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12307 switch (event->keyval)
12309 case GDK_Left: case GDK_KP_Left:
12310 case GDK_Home: case GDK_KP_Home:
12311 if (!is_all_selected (widget) && !is_at_left (widget))
12315 case GDK_Right: case GDK_KP_Right:
12316 case GDK_End: case GDK_KP_End:
12317 if (!is_all_selected (widget) && !is_at_right (widget))
12321 case GDK_Up: case GDK_KP_Up:
12322 case GDK_Down: case GDK_KP_Down:
12325 case GDK_Page_Up: case GDK_KP_Page_Up:
12326 case GDK_Page_Down: case GDK_KP_Page_Down:
12337 case GDK_Tab: case GDK_KP_Tab:
12338 case GDK_ISO_Left_Tab:
12347 case GDK_SHIFT_MASK:
12348 switch (event->keyval)
12351 case GDK_ISO_Left_Tab:
12360 case GDK_CONTROL_MASK:
12361 switch (event->keyval)
12363 case GDK_Left: case GDK_KP_Left:
12364 if (!is_all_selected (widget) && !is_at_left (widget))
12368 case GDK_Right: case GDK_KP_Right:
12369 if (!is_all_selected (widget) && !is_at_right (widget))
12373 case GDK_Up: case GDK_KP_Up:
12374 case GDK_Down: case GDK_KP_Down:
12386 row = tree_view->priv->edited_row;
12387 column = tree_view->priv->edited_column;
12388 path = gtk_tree_path_new_from_indices (row, -1);
12390 pspp_sheet_view_stop_editing (tree_view, cancel);
12391 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12393 pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12394 gtk_tree_path_free (path);
12396 handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12397 G_OBJECT (tree_view));
12399 g_signal_stop_emission_by_name (widget, "event");
12401 pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12402 pspp_sheet_view_start_editing (tree_view, path);
12403 gtk_tree_path_free (path);
12409 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12412 PsppSheetView *sheet_view = data;
12414 g_signal_connect (widget, "event",
12415 G_CALLBACK (pspp_sheet_view_event),
12418 if (GTK_IS_CONTAINER (widget))
12419 gtk_container_foreach (GTK_CONTAINER (widget),
12420 pspp_sheet_view_override_cell_keypresses,
12425 pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
12426 PsppSheetViewColumn *column,
12428 GtkCellEditable *cell_editable,
12429 GdkRectangle *cell_area,
12433 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12434 gint pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
12435 GtkRequisition requisition;
12438 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12440 tree_view->priv->edited_column = column;
12441 _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12443 row = gtk_tree_path_get_indices (path)[0];
12444 tree_view->priv->edited_row = row;
12445 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12446 cell_area->y += pre_val - (int)gtk_adjustment_get_value (tree_view->priv->vadjustment);
12448 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12449 pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12450 tree_view->priv->anchor_column = column;
12452 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12454 pspp_sheet_view_put (tree_view,
12455 GTK_WIDGET (cell_editable),
12459 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12460 (GdkEvent *)event);
12462 gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12463 g_signal_connect (cell_editable, "remove-widget",
12464 G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12465 if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12466 GTK_IS_BUTTON (cell_editable))
12468 g_signal_connect (cell_editable, "button-press-event",
12469 G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12471 g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12472 GINT_TO_POINTER (row));
12473 g_signal_connect (cell_editable, "clicked",
12474 G_CALLBACK (pspp_sheet_view_editable_clicked),
12478 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12483 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12484 gboolean cancel_editing)
12486 PsppSheetViewColumn *column;
12487 GtkCellRenderer *cell;
12489 if (tree_view->priv->edited_column == NULL)
12493 * This is very evil. We need to do this, because
12494 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12495 * later on. If pspp_sheet_view_row_changed notices
12496 * tree_view->priv->edited_column != NULL, it'll call
12497 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12499 * Please read that again if you intend to modify anything here.
12502 column = tree_view->priv->edited_column;
12503 tree_view->priv->edited_column = NULL;
12505 cell = _pspp_sheet_view_column_get_edited_cell (column);
12506 gtk_cell_renderer_stop_editing (cell, cancel_editing);
12508 if (!cancel_editing)
12509 gtk_cell_editable_editing_done (column->editable_widget);
12511 tree_view->priv->edited_column = column;
12513 gtk_cell_editable_remove_widget (column->editable_widget);
12518 * pspp_sheet_view_set_hover_selection:
12519 * @tree_view: a #PsppSheetView
12520 * @hover: %TRUE to enable hover selection mode
12522 * Enables of disables the hover selection mode of @tree_view.
12523 * Hover selection makes the selected row follow the pointer.
12524 * Currently, this works only for the selection modes
12525 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12530 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12533 hover = hover != FALSE;
12535 if (hover != tree_view->priv->hover_selection)
12537 tree_view->priv->hover_selection = hover;
12539 g_object_notify (G_OBJECT (tree_view), "hover-selection");
12544 * pspp_sheet_view_get_hover_selection:
12545 * @tree_view: a #PsppSheetView
12547 * Returns whether hover selection mode is turned on for @tree_view.
12549 * Return value: %TRUE if @tree_view is in hover selection mode
12554 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12556 return tree_view->priv->hover_selection;
12560 * pspp_sheet_view_set_rubber_banding:
12561 * @tree_view: a #PsppSheetView
12562 * @enable: %TRUE to enable rubber banding
12564 * Enables or disables rubber banding in @tree_view. If the selection mode is
12565 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12566 * banding will allow the user to select multiple rows by dragging the mouse.
12571 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12574 enable = enable != FALSE;
12576 if (enable != tree_view->priv->rubber_banding_enable)
12578 tree_view->priv->rubber_banding_enable = enable;
12580 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12585 * pspp_sheet_view_get_rubber_banding:
12586 * @tree_view: a #PsppSheetView
12588 * Returns whether rubber banding is turned on for @tree_view. If the
12589 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12590 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12591 * select multiple rows by dragging the mouse.
12593 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12598 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12600 return tree_view->priv->rubber_banding_enable;
12604 * pspp_sheet_view_is_rubber_banding_active:
12605 * @tree_view: a #PsppSheetView
12607 * Returns whether a rubber banding operation is currently being done
12610 * Return value: %TRUE if a rubber banding operation is currently being
12611 * done in @tree_view.
12616 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12618 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12620 if (tree_view->priv->rubber_banding_enable
12621 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12628 pspp_sheet_view_grab_notify (GtkWidget *widget,
12629 gboolean was_grabbed)
12631 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12633 tree_view->priv->in_grab = !was_grabbed;
12637 tree_view->priv->pressed_button = -1;
12639 if (tree_view->priv->rubber_band_status)
12640 pspp_sheet_view_stop_rubber_band (tree_view);
12645 pspp_sheet_view_state_changed (GtkWidget *widget,
12646 GtkStateType previous_state)
12648 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12650 if (gtk_widget_get_realized (widget))
12652 GtkStyle *style = gtk_widget_get_style (widget);
12653 gdk_window_set_background (tree_view->priv->bin_window, &style->base[gtk_widget_get_state (widget)]);
12656 gtk_widget_queue_draw (widget);
12660 * pspp_sheet_view_get_grid_lines:
12661 * @tree_view: a #PsppSheetView
12663 * Returns which grid lines are enabled in @tree_view.
12665 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12670 PsppSheetViewGridLines
12671 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12673 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12675 return tree_view->priv->grid_lines;
12679 * pspp_sheet_view_set_grid_lines:
12680 * @tree_view: a #PsppSheetView
12681 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12684 * Sets which grid lines to draw in @tree_view.
12689 pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view,
12690 PsppSheetViewGridLines grid_lines)
12692 PsppSheetViewPrivate *priv;
12693 PsppSheetViewGridLines old_grid_lines;
12695 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12697 priv = tree_view->priv;
12699 old_grid_lines = priv->grid_lines;
12700 priv->grid_lines = grid_lines;
12702 if (old_grid_lines != grid_lines)
12704 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12706 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12711 * pspp_sheet_view_get_special_cells:
12712 * @tree_view: a #PsppSheetView
12714 * Returns which grid lines are enabled in @tree_view.
12716 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12717 * the sheet view contain special cells.
12719 PsppSheetViewSpecialCells
12720 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12722 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12724 return tree_view->priv->special_cells;
12728 * pspp_sheet_view_set_special_cells:
12729 * @tree_view: a #PsppSheetView
12730 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12731 * the sheet view contain special cells.
12733 * Sets whether rows in the sheet view contain special cells, controlling the
12734 * rendering of row selections.
12737 pspp_sheet_view_set_special_cells (PsppSheetView *tree_view,
12738 PsppSheetViewSpecialCells special_cells)
12740 PsppSheetViewPrivate *priv;
12742 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12744 priv = tree_view->priv;
12746 if (priv->special_cells != special_cells)
12748 priv->special_cells = special_cells;
12749 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12750 g_object_notify (G_OBJECT (tree_view), "special-cells");
12755 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12757 /* XXX (re)calculate fixed_height if necessary */
12758 return tree_view->priv->fixed_height;
12762 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
12765 g_return_if_fail (fixed_height > 0);
12767 if (tree_view->priv->fixed_height != fixed_height)
12769 tree_view->priv->fixed_height = fixed_height;
12770 g_object_notify (G_OBJECT (tree_view), "fixed-height");
12772 if (!tree_view->priv->fixed_height_set)
12774 tree_view->priv->fixed_height_set = TRUE;
12775 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
12780 * pspp_sheet_view_set_tooltip_row:
12781 * @tree_view: a #PsppSheetView
12782 * @tooltip: a #GtkTooltip
12783 * @path: a #GtkTreePath
12785 * Sets the tip area of @tooltip to be the area covered by the row at @path.
12786 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12787 * See also gtk_tooltip_set_tip_area().
12792 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
12793 GtkTooltip *tooltip,
12796 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12797 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12799 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
12803 * pspp_sheet_view_set_tooltip_cell:
12804 * @tree_view: a #PsppSheetView
12805 * @tooltip: a #GtkTooltip
12806 * @path: (allow-none): a #GtkTreePath or %NULL
12807 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
12808 * @cell: (allow-none): a #GtkCellRenderer or %NULL
12810 * Sets the tip area of @tooltip to the area @path, @column and @cell have
12811 * in common. For example if @path is %NULL and @column is set, the tip
12812 * area will be set to the full area covered by @column. See also
12813 * gtk_tooltip_set_tip_area().
12815 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12820 pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view,
12821 GtkTooltip *tooltip,
12823 PsppSheetViewColumn *column,
12824 GtkCellRenderer *cell)
12828 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12829 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12830 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
12831 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
12833 /* Determine x values. */
12834 if (column && cell)
12839 pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
12840 pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
12842 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12845 rect.width = width;
12851 pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
12852 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12855 rect.width = tmp.width;
12859 GtkAllocation allocation;
12860 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
12862 rect.width = allocation.width;
12865 /* Determine y values. */
12870 pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
12871 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12874 rect.height = tmp.height;
12879 rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
12882 gtk_tooltip_set_tip_area (tooltip, &rect);
12886 * pspp_sheet_view_get_tooltip_context:
12887 * @tree_view: a #PsppSheetView
12888 * @x: the x coordinate (relative to widget coordinates)
12889 * @y: the y coordinate (relative to widget coordinates)
12890 * @keyboard_tip: whether this is a keyboard tooltip or not
12891 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
12892 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
12893 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
12895 * This function is supposed to be used in a #GtkWidget::query-tooltip
12896 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
12897 * which are received in the signal handler, should be passed to this
12898 * function without modification.
12900 * The return value indicates whether there is a tree view row at the given
12901 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
12902 * tooltips the row returned will be the cursor row. When %TRUE, then any of
12903 * @model, @path and @iter which have been provided will be set to point to
12904 * that row and the corresponding model. @x and @y will always be converted
12905 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
12907 * Return value: whether or not the given tooltip context points to a row.
12912 pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view,
12915 gboolean keyboard_tip,
12916 GtkTreeModel **model,
12917 GtkTreePath **path,
12920 GtkTreePath *tmppath = NULL;
12922 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12923 g_return_val_if_fail (x != NULL, FALSE);
12924 g_return_val_if_fail (y != NULL, FALSE);
12928 pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
12935 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
12938 if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
12939 &tmppath, NULL, NULL, NULL))
12944 *model = pspp_sheet_view_get_model (tree_view);
12947 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
12953 gtk_tree_path_free (tmppath);
12959 pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget,
12962 gboolean keyboard_tip,
12963 GtkTooltip *tooltip,
12966 GValue value = { 0, };
12967 GValue transformed = { 0, };
12970 GtkTreeModel *model;
12971 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12973 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
12976 &model, &path, &iter))
12979 gtk_tree_model_get_value (model, &iter,
12980 tree_view->priv->tooltip_column, &value);
12982 g_value_init (&transformed, G_TYPE_STRING);
12984 if (!g_value_transform (&value, &transformed))
12986 g_value_unset (&value);
12987 gtk_tree_path_free (path);
12992 g_value_unset (&value);
12994 if (!g_value_get_string (&transformed))
12996 g_value_unset (&transformed);
12997 gtk_tree_path_free (path);
13002 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
13003 pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
13005 gtk_tree_path_free (path);
13006 g_value_unset (&transformed);
13012 * pspp_sheet_view_set_tooltip_column:
13013 * @tree_view: a #PsppSheetView
13014 * @column: an integer, which is a valid column number for @tree_view's model
13016 * If you only plan to have simple (text-only) tooltips on full rows, you
13017 * can use this function to have #PsppSheetView handle these automatically
13018 * for you. @column should be set to the column in @tree_view's model
13019 * containing the tooltip texts, or -1 to disable this feature.
13021 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
13022 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
13024 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
13025 * so &, <, etc have to be escaped in the text.
13030 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
13033 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13035 if (column == tree_view->priv->tooltip_column)
13040 g_signal_handlers_disconnect_by_func (tree_view,
13041 pspp_sheet_view_set_tooltip_query_cb,
13043 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13047 if (tree_view->priv->tooltip_column == -1)
13049 g_signal_connect (tree_view, "query-tooltip",
13050 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13051 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13055 tree_view->priv->tooltip_column = column;
13056 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13060 * pspp_sheet_view_get_tooltip_column:
13061 * @tree_view: a #PsppSheetView
13063 * Returns the column of @tree_view's model which is being used for
13064 * displaying tooltips on @tree_view's rows.
13066 * Return value: the index of the tooltip column that is currently being
13067 * used, or -1 if this is disabled.
13072 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13074 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13076 return tree_view->priv->tooltip_column;
13080 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13081 GValue *return_accu,
13082 const GValue *handler_return,
13085 gboolean continue_emission;
13086 gboolean signal_handled;
13088 signal_handled = g_value_get_boolean (handler_return);
13089 g_value_set_boolean (return_accu, signal_handled);
13090 continue_emission = !signal_handled;
13092 return continue_emission;
13097 pspp_sheet_view_grid_lines_get_type (void)
13099 static GType etype = 0;
13100 if (G_UNLIKELY(etype == 0)) {
13101 static const GEnumValue values[] = {
13102 { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13103 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13104 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13105 { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13108 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13114 pspp_sheet_view_special_cells_get_type (void)
13116 static GType etype = 0;
13117 if (G_UNLIKELY(etype == 0)) {
13118 static const GEnumValue values[] = {
13119 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13120 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13121 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13124 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);