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
103 typedef struct _TreeViewDragInfo TreeViewDragInfo;
104 struct _TreeViewDragInfo
106 GdkModifierType start_button_mask;
107 GtkTargetList *_unused_source_target_list;
108 GdkDragAction source_actions;
110 GtkTargetList *_unused_dest_target_list;
112 guint source_set : 1;
128 START_INTERACTIVE_SEARCH,
140 PROP_HEADERS_VISIBLE,
141 PROP_HEADERS_CLICKABLE,
146 PROP_HOVER_SELECTION,
148 PROP_ENABLE_GRID_LINES,
152 PROP_FIXED_HEIGHT_SET
156 static void pspp_sheet_view_finalize (GObject *object);
157 static void pspp_sheet_view_set_property (GObject *object,
161 static void pspp_sheet_view_get_property (GObject *object,
166 static void pspp_sheet_view_dispose (GObject *object);
168 /* gtkwidget signals */
169 static void pspp_sheet_view_realize (GtkWidget *widget);
170 static void pspp_sheet_view_unrealize (GtkWidget *widget);
171 static void pspp_sheet_view_map (GtkWidget *widget);
172 static void pspp_sheet_view_size_request (GtkWidget *widget,
173 GtkRequisition *requisition);
174 static void pspp_sheet_view_size_allocate (GtkWidget *widget,
175 GtkAllocation *allocation);
176 static gboolean pspp_sheet_view_draw (GtkWidget *widget,
178 static gboolean pspp_sheet_view_key_press (GtkWidget *widget,
180 static gboolean pspp_sheet_view_key_release (GtkWidget *widget,
182 static gboolean pspp_sheet_view_motion (GtkWidget *widget,
183 GdkEventMotion *event);
184 static gboolean pspp_sheet_view_enter_notify (GtkWidget *widget,
185 GdkEventCrossing *event);
186 static gboolean pspp_sheet_view_leave_notify (GtkWidget *widget,
187 GdkEventCrossing *event);
188 static gboolean pspp_sheet_view_button_press (GtkWidget *widget,
189 GdkEventButton *event);
190 static gboolean pspp_sheet_view_button_release (GtkWidget *widget,
191 GdkEventButton *event);
192 static gboolean pspp_sheet_view_grab_broken (GtkWidget *widget,
193 GdkEventGrabBroken *event);
195 static void pspp_sheet_view_set_focus_child (GtkContainer *container,
197 static gint pspp_sheet_view_focus_out (GtkWidget *widget,
198 GdkEventFocus *event);
199 static gint pspp_sheet_view_focus (GtkWidget *widget,
200 GtkDirectionType direction);
201 static void pspp_sheet_view_grab_focus (GtkWidget *widget);
202 static void pspp_sheet_view_style_set (GtkWidget *widget,
203 GtkStyle *previous_style);
204 static void pspp_sheet_view_grab_notify (GtkWidget *widget,
205 gboolean was_grabbed);
206 static void pspp_sheet_view_state_changed (GtkWidget *widget,
207 GtkStateType previous_state);
209 /* container signals */
210 static void pspp_sheet_view_remove (GtkContainer *container,
212 static void pspp_sheet_view_forall (GtkContainer *container,
213 gboolean include_internals,
214 GtkCallback callback,
215 gpointer callback_data);
217 /* Source side drag signals */
218 static void pspp_sheet_view_drag_begin (GtkWidget *widget,
219 GdkDragContext *context);
220 static void pspp_sheet_view_drag_end (GtkWidget *widget,
221 GdkDragContext *context);
222 static void pspp_sheet_view_drag_data_get (GtkWidget *widget,
223 GdkDragContext *context,
224 GtkSelectionData *selection_data,
227 static void pspp_sheet_view_drag_data_delete (GtkWidget *widget,
228 GdkDragContext *context);
230 /* Target side drag signals */
231 static void pspp_sheet_view_drag_leave (GtkWidget *widget,
232 GdkDragContext *context,
234 static gboolean pspp_sheet_view_drag_motion (GtkWidget *widget,
235 GdkDragContext *context,
239 static gboolean pspp_sheet_view_drag_drop (GtkWidget *widget,
240 GdkDragContext *context,
244 static void pspp_sheet_view_drag_data_received (GtkWidget *widget,
245 GdkDragContext *context,
248 GtkSelectionData *selection_data,
252 /* tree_model signals */
253 static void pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
255 GtkAdjustment *vadj);
256 static gboolean pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
257 GtkMovementStep step,
259 static gboolean pspp_sheet_view_real_select_all (PsppSheetView *tree_view);
260 static gboolean pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view);
261 static gboolean pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
262 gboolean start_editing,
263 PsppSheetSelectMode mode);
264 static gboolean pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view);
265 static void pspp_sheet_view_row_changed (GtkTreeModel *model,
269 static void pspp_sheet_view_row_inserted (GtkTreeModel *model,
273 static void pspp_sheet_view_row_deleted (GtkTreeModel *model,
276 static void pspp_sheet_view_rows_reordered (GtkTreeModel *model,
282 /* Incremental reflow */
283 static gint validate_row (PsppSheetView *tree_view,
287 static void validate_visible_area (PsppSheetView *tree_view);
288 static gboolean validate_rows_handler (PsppSheetView *tree_view);
289 static gboolean presize_handler_callback (gpointer data);
290 static void install_presize_handler (PsppSheetView *tree_view);
291 static void install_scroll_sync_handler (PsppSheetView *tree_view);
292 static void pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
295 static void pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view);
296 static void pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view);
297 static void invalidate_empty_focus (PsppSheetView *tree_view);
299 /* Internal functions */
300 static GtkAdjustment *pspp_sheet_view_do_get_hadjustment (PsppSheetView *);
301 static GtkAdjustment *pspp_sheet_view_do_get_vadjustment (PsppSheetView *);
302 static void pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
303 GtkAdjustment *adjustment);
304 static void pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
305 GtkAdjustment *adjustment);
306 static void pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
309 gboolean add_shifted_binding,
310 GtkMovementStep step,
312 static void pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
314 const GdkRectangle *clip_rect);
315 static gint pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
318 static void pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
319 PsppSheetView *tree_view);
320 static void pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
322 static void pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
323 PsppSheetViewColumn *column,
324 gboolean focus_to_cell);
325 static gboolean pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
326 GdkEventMotion *event);
327 static void pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view);
328 static gboolean pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
330 PsppSheetSelectMode mode);
331 static void pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
333 PsppSheetSelectMode mode);
334 static void pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
336 PsppSheetSelectMode mode);
337 static void pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
339 PsppSheetSelectMode mode);
340 static void pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
342 static void pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
344 PsppSheetSelectMode mode);
345 static void pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
347 gboolean clear_and_select,
349 PsppSheetSelectMode mode);
350 static gboolean pspp_sheet_view_has_special_cell (PsppSheetView *tree_view);
351 static void pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view);
352 static void update_prelight (PsppSheetView *tree_view,
355 static void initialize_fixed_height_mode (PsppSheetView *tree_view);
357 /* interactive search */
358 static void pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view);
359 static void pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
360 PsppSheetView *tree_view);
361 static void pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
362 GtkWidget *search_dialog,
364 static void pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
368 static void pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
369 PsppSheetView *tree_view);
371 static void pspp_sheet_view_search_activate (GtkEntry *entry,
372 PsppSheetView *tree_view);
373 static gboolean pspp_sheet_view_real_search_enable_popdown(gpointer data);
374 static void pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
376 static gboolean pspp_sheet_view_search_delete_event (GtkWidget *widget,
378 PsppSheetView *tree_view);
379 static gboolean pspp_sheet_view_search_button_press_event (GtkWidget *widget,
380 GdkEventButton *event,
381 PsppSheetView *tree_view);
382 static gboolean pspp_sheet_view_search_scroll_event (GtkWidget *entry,
383 GdkEventScroll *event,
384 PsppSheetView *tree_view);
385 static gboolean pspp_sheet_view_search_key_press_event (GtkWidget *entry,
387 PsppSheetView *tree_view);
388 static gboolean pspp_sheet_view_search_move (GtkWidget *window,
389 PsppSheetView *tree_view,
391 static gboolean pspp_sheet_view_search_equal_func (GtkTreeModel *model,
395 gpointer search_data);
396 static gboolean pspp_sheet_view_search_iter (GtkTreeModel *model,
397 PsppSheetSelection *selection,
402 static void pspp_sheet_view_search_init (GtkWidget *entry,
403 PsppSheetView *tree_view);
404 static void pspp_sheet_view_put (PsppSheetView *tree_view,
405 GtkWidget *child_widget,
410 static gboolean pspp_sheet_view_start_editing (PsppSheetView *tree_view,
411 GtkTreePath *cursor_path);
412 static gboolean pspp_sheet_view_editable_button_press_event (GtkWidget *,
415 static void pspp_sheet_view_editable_clicked (GtkButton *, PsppSheetView *);
416 static void pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
417 PsppSheetViewColumn *column,
419 GtkCellEditable *cell_editable,
420 GdkRectangle *cell_area,
423 static gboolean pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
424 gboolean keybinding);
425 static gboolean pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view);
426 static PsppSheetViewColumn *pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
427 PsppSheetViewColumn *column,
430 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
431 PsppSheetViewColumn *column,
432 const GdkRectangle *background_area,
433 gboolean subtract_focus_rect,
434 GdkRectangle *cell_area);
435 static gint pspp_sheet_view_find_offset (PsppSheetView *tree_view,
440 static void pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
444 static void pspp_sheet_view_buildable_init (GtkBuildableIface *iface);
447 static gboolean scroll_row_timeout (gpointer data);
448 static void add_scroll_timeout (PsppSheetView *tree_view);
449 static void remove_scroll_timeout (PsppSheetView *tree_view);
451 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
453 static GtkBindingSet *edit_bindings;
460 G_DEFINE_TYPE_WITH_CODE (PsppSheetView, pspp_sheet_view, GTK_TYPE_CONTAINER,
461 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
462 pspp_sheet_view_buildable_init)
463 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
466 pspp_sheet_view_get_preferred_width (GtkWidget *widget,
470 GtkRequisition requisition;
472 pspp_sheet_view_size_request (widget, &requisition);
474 *minimal_width = *natural_width = requisition.width;
478 pspp_sheet_view_get_preferred_height (GtkWidget *widget,
479 gint *minimal_height,
480 gint *natural_height)
482 GtkRequisition requisition;
484 pspp_sheet_view_size_request (widget, &requisition);
486 *minimal_height = *natural_height = requisition.height;
490 pspp_sheet_view_class_init (PsppSheetViewClass *class)
492 GObjectClass *o_class;
493 GtkWidgetClass *widget_class;
494 GtkContainerClass *container_class;
495 GtkBindingSet *binding_set[2];
498 binding_set[0] = gtk_binding_set_by_class (class);
500 binding_set[1] = gtk_binding_set_new ("PsppSheetViewEditing");
501 edit_bindings = binding_set[1];
503 o_class = (GObjectClass *) class;
504 widget_class = (GtkWidgetClass *) class;
505 container_class = (GtkContainerClass *) class;
507 /* GObject signals */
508 o_class->set_property = pspp_sheet_view_set_property;
509 o_class->get_property = pspp_sheet_view_get_property;
510 o_class->finalize = pspp_sheet_view_finalize;
511 o_class->dispose = pspp_sheet_view_dispose;
513 /* GtkWidget signals */
514 widget_class->map = pspp_sheet_view_map;
515 widget_class->realize = pspp_sheet_view_realize;
516 widget_class->unrealize = pspp_sheet_view_unrealize;
517 widget_class->get_preferred_width = pspp_sheet_view_get_preferred_width;
518 widget_class->get_preferred_height = pspp_sheet_view_get_preferred_height;
519 widget_class->size_allocate = pspp_sheet_view_size_allocate;
520 widget_class->button_press_event = pspp_sheet_view_button_press;
521 widget_class->button_release_event = pspp_sheet_view_button_release;
522 widget_class->grab_broken_event = pspp_sheet_view_grab_broken;
523 /*widget_class->configure_event = pspp_sheet_view_configure;*/
524 widget_class->motion_notify_event = pspp_sheet_view_motion;
525 widget_class->draw = pspp_sheet_view_draw;
526 widget_class->key_press_event = pspp_sheet_view_key_press;
527 widget_class->key_release_event = pspp_sheet_view_key_release;
528 widget_class->enter_notify_event = pspp_sheet_view_enter_notify;
529 widget_class->leave_notify_event = pspp_sheet_view_leave_notify;
530 widget_class->focus_out_event = pspp_sheet_view_focus_out;
531 widget_class->drag_begin = pspp_sheet_view_drag_begin;
532 widget_class->drag_end = pspp_sheet_view_drag_end;
533 widget_class->drag_data_get = pspp_sheet_view_drag_data_get;
534 widget_class->drag_data_delete = pspp_sheet_view_drag_data_delete;
535 widget_class->drag_leave = pspp_sheet_view_drag_leave;
536 widget_class->drag_motion = pspp_sheet_view_drag_motion;
537 widget_class->drag_drop = pspp_sheet_view_drag_drop;
538 widget_class->drag_data_received = pspp_sheet_view_drag_data_received;
539 widget_class->focus = pspp_sheet_view_focus;
540 widget_class->grab_focus = pspp_sheet_view_grab_focus;
541 widget_class->style_set = pspp_sheet_view_style_set;
542 widget_class->grab_notify = pspp_sheet_view_grab_notify;
543 widget_class->state_changed = pspp_sheet_view_state_changed;
545 /* GtkContainer signals */
546 container_class->remove = pspp_sheet_view_remove;
547 container_class->forall = pspp_sheet_view_forall;
548 container_class->set_focus_child = pspp_sheet_view_set_focus_child;
550 class->set_scroll_adjustments = pspp_sheet_view_set_adjustments;
551 class->move_cursor = pspp_sheet_view_real_move_cursor;
552 class->select_all = pspp_sheet_view_real_select_all;
553 class->unselect_all = pspp_sheet_view_real_unselect_all;
554 class->select_cursor_row = pspp_sheet_view_real_select_cursor_row;
555 class->toggle_cursor_row = pspp_sheet_view_real_toggle_cursor_row;
556 class->start_interactive_search = pspp_sheet_view_start_interactive_search;
560 g_object_class_install_property (o_class,
562 g_param_spec_object ("model",
563 P_("TreeView Model"),
564 P_("The model for the tree view"),
566 GTK_PARAM_READWRITE));
568 g_object_class_override_property (o_class, PROP_HADJUSTMENT, "hadjustment");
569 g_object_class_override_property (o_class, PROP_VADJUSTMENT, "vadjustment");
570 g_object_class_override_property (o_class, PROP_HSCROLL_POLICY, "hscroll-policy");
571 g_object_class_override_property (o_class, PROP_VSCROLL_POLICY, "vscroll-policy");
573 g_object_class_install_property (o_class,
574 PROP_HEADERS_VISIBLE,
575 g_param_spec_boolean ("headers-visible",
576 P_("Headers Visible"),
577 P_("Show the column header buttons"),
579 GTK_PARAM_READWRITE));
581 g_object_class_install_property (o_class,
582 PROP_HEADERS_CLICKABLE,
583 g_param_spec_boolean ("headers-clickable",
584 P_("Headers Clickable"),
585 P_("Column headers respond to click events"),
587 GTK_PARAM_READWRITE));
589 g_object_class_install_property (o_class,
591 g_param_spec_boolean ("reorderable",
593 P_("View is reorderable"),
595 GTK_PARAM_READWRITE));
597 g_object_class_install_property (o_class,
599 g_param_spec_boolean ("rules-hint",
601 P_("Set a hint to the theme engine to draw rows in alternating colors"),
603 GTK_PARAM_READWRITE));
605 g_object_class_install_property (o_class,
607 g_param_spec_boolean ("enable-search",
609 P_("View allows user to search through columns interactively"),
611 GTK_PARAM_READWRITE));
613 g_object_class_install_property (o_class,
615 g_param_spec_int ("search-column",
617 P_("Model column to search through during interactive search"),
621 GTK_PARAM_READWRITE));
624 * PsppSheetView:hover-selection:
626 * Enables of disables the hover selection mode of @tree_view.
627 * Hover selection makes the selected row follow the pointer.
628 * Currently, this works only for the selection modes
629 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
631 * This mode is primarily intended for treeviews in popups, e.g.
632 * in #GtkComboBox or #GtkEntryCompletion.
636 g_object_class_install_property (o_class,
637 PROP_HOVER_SELECTION,
638 g_param_spec_boolean ("hover-selection",
639 P_("Hover Selection"),
640 P_("Whether the selection should follow the pointer"),
642 GTK_PARAM_READWRITE));
644 g_object_class_install_property (o_class,
646 g_param_spec_boolean ("rubber-banding",
647 P_("Rubber Banding"),
648 P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
650 GTK_PARAM_READWRITE));
652 g_object_class_install_property (o_class,
653 PROP_ENABLE_GRID_LINES,
654 g_param_spec_enum ("enable-grid-lines",
655 P_("Enable Grid Lines"),
656 P_("Whether grid lines should be drawn in the tree view"),
657 PSPP_TYPE_SHEET_VIEW_GRID_LINES,
658 PSPP_SHEET_VIEW_GRID_LINES_NONE,
659 GTK_PARAM_READWRITE));
661 g_object_class_install_property (o_class,
663 g_param_spec_int ("tooltip-column",
664 P_("Tooltip Column"),
665 P_("The column in the model containing the tooltip texts for the rows"),
669 GTK_PARAM_READWRITE));
671 g_object_class_install_property (o_class,
673 g_param_spec_enum ("special-cells",
675 P_("Whether rows have special cells."),
676 PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS,
677 PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT,
678 GTK_PARAM_READWRITE));
680 g_object_class_install_property (o_class,
682 g_param_spec_int ("fixed-height",
684 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."),
688 GTK_PARAM_READWRITE));
690 g_object_class_install_property (o_class,
691 PROP_FIXED_HEIGHT_SET,
692 g_param_spec_boolean ("fixed-height-set",
693 P_("Fixed Height Set"),
694 P_("Whether fixed-height was set externally."),
696 GTK_PARAM_READWRITE));
698 /* Style properties */
699 #define _TREE_VIEW_EXPANDER_SIZE 12
700 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
701 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
703 gtk_widget_class_install_style_property (widget_class,
704 g_param_spec_int ("expander-size",
706 P_("Size of the expander arrow"),
709 _TREE_VIEW_EXPANDER_SIZE,
710 GTK_PARAM_READABLE));
712 gtk_widget_class_install_style_property (widget_class,
713 g_param_spec_int ("vertical-separator",
714 P_("Vertical Separator Width"),
715 P_("Vertical space between cells. Must be an even number"),
718 _TREE_VIEW_VERTICAL_SEPARATOR,
719 GTK_PARAM_READABLE));
721 gtk_widget_class_install_style_property (widget_class,
722 g_param_spec_int ("horizontal-separator",
723 P_("Horizontal Separator Width"),
724 P_("Horizontal space between cells. Must be an even number"),
727 _TREE_VIEW_HORIZONTAL_SEPARATOR,
728 GTK_PARAM_READABLE));
730 gtk_widget_class_install_style_property (widget_class,
731 g_param_spec_boolean ("allow-rules",
733 P_("Allow drawing of alternating color rows"),
735 GTK_PARAM_READABLE));
737 gtk_widget_class_install_style_property (widget_class,
738 g_param_spec_boxed ("even-row-color",
739 P_("Even Row Color"),
740 P_("Color to use for even rows"),
742 GTK_PARAM_READABLE));
744 gtk_widget_class_install_style_property (widget_class,
745 g_param_spec_boxed ("odd-row-color",
747 P_("Color to use for odd rows"),
749 GTK_PARAM_READABLE));
751 gtk_widget_class_install_style_property (widget_class,
752 g_param_spec_boolean ("row-ending-details",
753 P_("Row Ending details"),
754 P_("Enable extended row background theming"),
756 GTK_PARAM_READABLE));
758 gtk_widget_class_install_style_property (widget_class,
759 g_param_spec_int ("grid-line-width",
760 P_("Grid line width"),
761 P_("Width, in pixels, of the tree view grid lines"),
763 GTK_PARAM_READABLE));
765 gtk_widget_class_install_style_property (widget_class,
766 g_param_spec_int ("tree-line-width",
767 P_("Tree line width"),
768 P_("Width, in pixels, of the tree view lines"),
770 GTK_PARAM_READABLE));
772 gtk_widget_class_install_style_property (widget_class,
773 g_param_spec_string ("tree-line-pattern",
774 P_("Tree line pattern"),
775 P_("Dash pattern used to draw the tree view lines"),
777 GTK_PARAM_READABLE));
782 * PsppSheetView::set-scroll-adjustments
783 * @horizontal: the horizontal #GtkAdjustment
784 * @vertical: the vertical #GtkAdjustment
786 * Set the scroll adjustments for the tree view. Usually scrolled containers
787 * like #GtkScrolledWindow will emit this signal to connect two instances
788 * of #GtkScrollbar to the scroll directions of the #PsppSheetView.
790 widget_class->set_scroll_adjustments_signal =
791 g_signal_new ("set-scroll-adjustments",
792 G_TYPE_FROM_CLASS (o_class),
793 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
794 G_STRUCT_OFFSET (PsppSheetViewClass, set_scroll_adjustments),
796 psppire_marshal_VOID__OBJECT_OBJECT,
799 GTK_TYPE_ADJUSTMENT);
803 * PsppSheetView::row-activated:
804 * @tree_view: the object on which the signal is emitted
805 * @path: the #GtkTreePath for the activated row
806 * @column: the #PsppSheetViewColumn in which the activation occurred
808 * The "row-activated" signal is emitted when the method
809 * pspp_sheet_view_row_activated() is called or the user double clicks
810 * a treeview row. It is also emitted when a non-editable row is
811 * selected and one of the keys: Space, Shift+Space, Return or
814 * For selection handling refer to the <link linkend="TreeWidget">tree
815 * widget conceptual overview</link> as well as #PsppSheetSelection.
817 tree_view_signals[ROW_ACTIVATED] =
818 g_signal_new ("row-activated",
819 G_TYPE_FROM_CLASS (o_class),
820 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
821 G_STRUCT_OFFSET (PsppSheetViewClass, row_activated),
823 psppire_marshal_VOID__BOXED_OBJECT,
826 PSPP_TYPE_SHEET_VIEW_COLUMN);
829 * PsppSheetView::columns-changed:
830 * @tree_view: the object on which the signal is emitted
832 * The number of columns of the treeview has changed.
834 tree_view_signals[COLUMNS_CHANGED] =
835 g_signal_new ("columns-changed",
836 G_TYPE_FROM_CLASS (o_class),
838 G_STRUCT_OFFSET (PsppSheetViewClass, columns_changed),
840 g_cclosure_marshal_VOID__VOID,
844 * PsppSheetView::cursor-changed:
845 * @tree_view: the object on which the signal is emitted
847 * The position of the cursor (focused cell) has changed.
849 tree_view_signals[CURSOR_CHANGED] =
850 g_signal_new ("cursor-changed",
851 G_TYPE_FROM_CLASS (o_class),
853 G_STRUCT_OFFSET (PsppSheetViewClass, cursor_changed),
855 g_cclosure_marshal_VOID__VOID,
858 tree_view_signals[MOVE_CURSOR] =
859 g_signal_new ("move-cursor",
860 G_TYPE_FROM_CLASS (o_class),
861 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
862 G_STRUCT_OFFSET (PsppSheetViewClass, move_cursor),
864 psppire_marshal_BOOLEAN__ENUM_INT,
866 GTK_TYPE_MOVEMENT_STEP,
869 tree_view_signals[SELECT_ALL] =
870 g_signal_new ("select-all",
871 G_TYPE_FROM_CLASS (o_class),
872 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
873 G_STRUCT_OFFSET (PsppSheetViewClass, select_all),
875 psppire_marshal_BOOLEAN__VOID,
878 tree_view_signals[UNSELECT_ALL] =
879 g_signal_new ("unselect-all",
880 G_TYPE_FROM_CLASS (o_class),
881 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
882 G_STRUCT_OFFSET (PsppSheetViewClass, unselect_all),
884 psppire_marshal_BOOLEAN__VOID,
887 tree_view_signals[SELECT_CURSOR_ROW] =
888 g_signal_new ("select-cursor-row",
889 G_TYPE_FROM_CLASS (o_class),
890 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
891 G_STRUCT_OFFSET (PsppSheetViewClass, select_cursor_row),
893 psppire_marshal_BOOLEAN__BOOLEAN,
895 G_TYPE_BOOLEAN, G_TYPE_INT);
897 tree_view_signals[TOGGLE_CURSOR_ROW] =
898 g_signal_new ("toggle-cursor-row",
899 G_TYPE_FROM_CLASS (o_class),
900 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
901 G_STRUCT_OFFSET (PsppSheetViewClass, toggle_cursor_row),
903 psppire_marshal_BOOLEAN__VOID,
906 tree_view_signals[START_INTERACTIVE_SEARCH] =
907 g_signal_new ("start-interactive-search",
908 G_TYPE_FROM_CLASS (o_class),
909 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
910 G_STRUCT_OFFSET (PsppSheetViewClass, start_interactive_search),
912 psppire_marshal_BOOLEAN__VOID,
916 for (i = 0; i < 2; i++)
918 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Up, 0, TRUE,
919 GTK_MOVEMENT_DISPLAY_LINES, -1);
920 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Up, 0, TRUE,
921 GTK_MOVEMENT_DISPLAY_LINES, -1);
923 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Down, 0, TRUE,
924 GTK_MOVEMENT_DISPLAY_LINES, 1);
925 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Down, 0, TRUE,
926 GTK_MOVEMENT_DISPLAY_LINES, 1);
928 pspp_sheet_view_add_move_binding (binding_set[i], GDK_p, GDK_CONTROL_MASK, FALSE,
929 GTK_MOVEMENT_DISPLAY_LINES, -1);
931 pspp_sheet_view_add_move_binding (binding_set[i], GDK_n, GDK_CONTROL_MASK, FALSE,
932 GTK_MOVEMENT_DISPLAY_LINES, 1);
934 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Home, 0, TRUE,
935 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
936 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Home, 0, TRUE,
937 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
939 pspp_sheet_view_add_move_binding (binding_set[i], GDK_End, 0, TRUE,
940 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
941 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_End, 0, TRUE,
942 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
944 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Up, 0, TRUE,
945 GTK_MOVEMENT_PAGES, -1);
946 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Up, 0, TRUE,
947 GTK_MOVEMENT_PAGES, -1);
949 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Down, 0, TRUE,
950 GTK_MOVEMENT_PAGES, 1);
951 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Down, 0, TRUE,
952 GTK_MOVEMENT_PAGES, 1);
955 gtk_binding_entry_add_signal (binding_set[i], GDK_Up, GDK_CONTROL_MASK, "move-cursor", 2,
956 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
959 gtk_binding_entry_add_signal (binding_set[i], GDK_Down, GDK_CONTROL_MASK, "move-cursor", 2,
960 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
963 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, 0, "move-cursor", 2,
964 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
967 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, 0, "move-cursor", 2,
968 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
971 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, 0, "move-cursor", 2,
972 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
975 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, GDK_SHIFT_MASK, "move-cursor", 2,
976 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
979 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, 0, "move-cursor", 2,
980 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
983 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, 0, "move-cursor", 2,
984 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
987 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, GDK_CONTROL_MASK,
989 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
992 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, GDK_CONTROL_MASK,
994 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
997 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, GDK_CONTROL_MASK,
999 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1002 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, GDK_CONTROL_MASK,
1004 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1007 gtk_binding_entry_add_signal (binding_set[i], GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
1009 gtk_binding_entry_add_signal (binding_set[i], GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
1012 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1013 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1015 gtk_binding_entry_add_signal (binding_set[0], GDK_a, GDK_CONTROL_MASK, "select-all", 0);
1016 gtk_binding_entry_add_signal (binding_set[0], GDK_slash, GDK_CONTROL_MASK, "select-all", 0);
1018 gtk_binding_entry_add_signal (binding_set[0], GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
1019 gtk_binding_entry_add_signal (binding_set[0], GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0);
1021 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1022 G_TYPE_BOOLEAN, TRUE,
1023 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1024 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1025 G_TYPE_BOOLEAN, TRUE,
1026 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1028 gtk_binding_entry_add_signal (binding_set[0], GDK_space, 0, "select-cursor-row", 1,
1029 G_TYPE_BOOLEAN, TRUE,
1031 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, 0, "select-cursor-row", 1,
1032 G_TYPE_BOOLEAN, TRUE,
1034 gtk_binding_entry_add_signal (binding_set[0], GDK_Return, 0, "select-cursor-row", 1,
1035 G_TYPE_BOOLEAN, TRUE,
1037 gtk_binding_entry_add_signal (binding_set[0], GDK_ISO_Enter, 0, "select-cursor-row", 1,
1038 G_TYPE_BOOLEAN, TRUE,
1040 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Enter, 0, "select-cursor-row", 1,
1041 G_TYPE_BOOLEAN, TRUE,
1044 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, 0, "select-cursor-parent", 0);
1045 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0);
1047 g_type_class_add_private (o_class, sizeof (PsppSheetViewPrivate));
1051 pspp_sheet_view_buildable_init (GtkBuildableIface *iface)
1053 iface->add_child = pspp_sheet_view_buildable_add_child;
1057 pspp_sheet_view_init (PsppSheetView *tree_view)
1059 tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, PSPP_TYPE_SHEET_VIEW, PsppSheetViewPrivate);
1061 gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
1062 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE);
1064 tree_view->priv->flags = PSPP_SHEET_VIEW_DRAW_KEYFOCUS
1065 | PSPP_SHEET_VIEW_HEADERS_VISIBLE;
1067 /* We need some padding */
1068 tree_view->priv->selected = range_tower_create ();
1069 tree_view->priv->dy = 0;
1070 tree_view->priv->cursor_offset = 0;
1071 tree_view->priv->n_columns = 0;
1072 tree_view->priv->header_height = 1;
1073 tree_view->priv->x_drag = 0;
1074 tree_view->priv->drag_pos = -1;
1075 tree_view->priv->header_has_focus = FALSE;
1076 tree_view->priv->pressed_button = -1;
1077 tree_view->priv->press_start_x = -1;
1078 tree_view->priv->press_start_y = -1;
1079 tree_view->priv->reorderable = FALSE;
1080 tree_view->priv->presize_handler_timer = 0;
1081 tree_view->priv->scroll_sync_timer = 0;
1082 tree_view->priv->fixed_height = -1;
1083 tree_view->priv->fixed_height_set = FALSE;
1084 pspp_sheet_view_set_adjustments (tree_view, NULL, NULL);
1085 tree_view->priv->selection = _pspp_sheet_selection_new_with_tree_view (tree_view);
1086 tree_view->priv->enable_search = TRUE;
1087 tree_view->priv->search_column = -1;
1088 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
1089 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
1090 tree_view->priv->search_custom_entry_set = FALSE;
1091 tree_view->priv->typeselect_flush_timeout = 0;
1092 tree_view->priv->init_hadjust_value = TRUE;
1093 tree_view->priv->width = 0;
1095 tree_view->priv->hover_selection = FALSE;
1097 tree_view->priv->rubber_banding_enable = FALSE;
1099 tree_view->priv->grid_lines = PSPP_SHEET_VIEW_GRID_LINES_NONE;
1101 tree_view->priv->tooltip_column = -1;
1103 tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT;
1105 tree_view->priv->post_validation_flag = FALSE;
1107 tree_view->priv->last_button_x = -1;
1108 tree_view->priv->last_button_y = -1;
1110 tree_view->priv->event_last_x = -10000;
1111 tree_view->priv->event_last_y = -10000;
1113 tree_view->priv->prelight_node = -1;
1114 tree_view->priv->rubber_band_start_node = -1;
1115 tree_view->priv->rubber_band_end_node = -1;
1117 tree_view->priv->anchor_column = NULL;
1119 tree_view->priv->button_style = NULL;
1121 tree_view->dispose_has_run = FALSE;
1123 pspp_sheet_view_do_set_vadjustment (tree_view, NULL);
1124 pspp_sheet_view_do_set_hadjustment (tree_view, NULL);
1133 pspp_sheet_view_set_property (GObject *object,
1135 const GValue *value,
1138 PsppSheetView *tree_view;
1140 tree_view = PSPP_SHEET_VIEW (object);
1145 pspp_sheet_view_set_model (tree_view, g_value_get_object (value));
1147 case PROP_HADJUSTMENT:
1148 pspp_sheet_view_do_set_hadjustment (tree_view, g_value_get_object (value));
1150 case PROP_VADJUSTMENT:
1151 pspp_sheet_view_do_set_vadjustment (tree_view, g_value_get_object (value));
1153 case PROP_HSCROLL_POLICY:
1154 tree_view->priv->hscroll_policy = g_value_get_enum (value);
1155 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1157 case PROP_VSCROLL_POLICY:
1158 tree_view->priv->vscroll_policy = g_value_get_enum (value);
1159 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1161 case PROP_HEADERS_VISIBLE:
1162 pspp_sheet_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1164 case PROP_HEADERS_CLICKABLE:
1165 pspp_sheet_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1167 case PROP_REORDERABLE:
1168 pspp_sheet_view_set_reorderable (tree_view, g_value_get_boolean (value));
1170 case PROP_RULES_HINT:
1171 pspp_sheet_view_set_rules_hint (tree_view, g_value_get_boolean (value));
1173 case PROP_ENABLE_SEARCH:
1174 pspp_sheet_view_set_enable_search (tree_view, g_value_get_boolean (value));
1176 case PROP_SEARCH_COLUMN:
1177 pspp_sheet_view_set_search_column (tree_view, g_value_get_int (value));
1179 case PROP_HOVER_SELECTION:
1180 tree_view->priv->hover_selection = g_value_get_boolean (value);
1182 case PROP_RUBBER_BANDING:
1183 tree_view->priv->rubber_banding_enable = g_value_get_boolean (value);
1185 case PROP_ENABLE_GRID_LINES:
1186 pspp_sheet_view_set_grid_lines (tree_view, g_value_get_enum (value));
1188 case PROP_TOOLTIP_COLUMN:
1189 pspp_sheet_view_set_tooltip_column (tree_view, g_value_get_int (value));
1191 case PROP_SPECIAL_CELLS:
1192 pspp_sheet_view_set_special_cells (tree_view, g_value_get_enum (value));
1194 case PROP_FIXED_HEIGHT:
1195 pspp_sheet_view_set_fixed_height (tree_view, g_value_get_int (value));
1197 case PROP_FIXED_HEIGHT_SET:
1198 if (g_value_get_boolean (value))
1200 if (!tree_view->priv->fixed_height_set
1201 && tree_view->priv->fixed_height >= 0)
1203 tree_view->priv->fixed_height_set = true;
1204 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1209 if (tree_view->priv->fixed_height_set)
1211 tree_view->priv->fixed_height_set = false;
1212 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1213 install_presize_handler (tree_view);
1218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1224 pspp_sheet_view_get_property (GObject *object,
1229 PsppSheetView *tree_view;
1231 tree_view = PSPP_SHEET_VIEW (object);
1236 g_value_set_object (value, tree_view->priv->model);
1238 case PROP_HADJUSTMENT:
1239 g_value_set_object (value, tree_view->priv->hadjustment);
1241 case PROP_VADJUSTMENT:
1242 g_value_set_object (value, tree_view->priv->vadjustment);
1244 case PROP_HSCROLL_POLICY:
1245 g_value_set_enum (value, tree_view->priv->hscroll_policy);
1247 case PROP_VSCROLL_POLICY:
1248 g_value_set_enum (value, tree_view->priv->vscroll_policy);
1250 case PROP_HEADERS_VISIBLE:
1251 g_value_set_boolean (value, pspp_sheet_view_get_headers_visible (tree_view));
1253 case PROP_HEADERS_CLICKABLE:
1254 g_value_set_boolean (value, pspp_sheet_view_get_headers_clickable (tree_view));
1256 case PROP_REORDERABLE:
1257 g_value_set_boolean (value, tree_view->priv->reorderable);
1259 case PROP_RULES_HINT:
1260 g_value_set_boolean (value, tree_view->priv->has_rules);
1262 case PROP_ENABLE_SEARCH:
1263 g_value_set_boolean (value, tree_view->priv->enable_search);
1265 case PROP_SEARCH_COLUMN:
1266 g_value_set_int (value, tree_view->priv->search_column);
1268 case PROP_HOVER_SELECTION:
1269 g_value_set_boolean (value, tree_view->priv->hover_selection);
1271 case PROP_RUBBER_BANDING:
1272 g_value_set_boolean (value, tree_view->priv->rubber_banding_enable);
1274 case PROP_ENABLE_GRID_LINES:
1275 g_value_set_enum (value, tree_view->priv->grid_lines);
1277 case PROP_TOOLTIP_COLUMN:
1278 g_value_set_int (value, tree_view->priv->tooltip_column);
1280 case PROP_SPECIAL_CELLS:
1281 g_value_set_enum (value, tree_view->priv->special_cells);
1283 case PROP_FIXED_HEIGHT:
1284 g_value_set_int (value, pspp_sheet_view_get_fixed_height (tree_view));
1286 case PROP_FIXED_HEIGHT_SET:
1287 g_value_set_boolean (value, tree_view->priv->fixed_height_set);
1290 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1296 pspp_sheet_view_dispose (GObject *object)
1298 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1300 if (tree_view->dispose_has_run)
1303 tree_view->dispose_has_run = TRUE;
1305 if (tree_view->priv->selection != NULL)
1307 _pspp_sheet_selection_set_tree_view (tree_view->priv->selection, NULL);
1308 g_object_unref (tree_view->priv->selection);
1309 tree_view->priv->selection = NULL;
1312 if (tree_view->priv->hadjustment)
1314 g_object_unref (tree_view->priv->hadjustment);
1315 tree_view->priv->hadjustment = NULL;
1317 if (tree_view->priv->vadjustment)
1319 g_object_unref (tree_view->priv->vadjustment);
1320 tree_view->priv->vadjustment = NULL;
1323 if (tree_view->priv->button_style)
1325 g_object_unref (tree_view->priv->button_style);
1326 tree_view->priv->button_style = NULL;
1330 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->dispose (object);
1336 pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
1337 GtkBuilder *builder,
1341 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), PSPP_SHEET_VIEW_COLUMN (child));
1345 pspp_sheet_view_finalize (GObject *object)
1347 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1349 pspp_sheet_view_stop_editing (tree_view, TRUE);
1351 if (tree_view->priv->selected != NULL)
1353 range_tower_destroy (tree_view->priv->selected);
1354 tree_view->priv->selected = NULL;
1358 tree_view->priv->prelight_node = -1;
1361 if (tree_view->priv->scroll_to_path != NULL)
1363 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
1364 tree_view->priv->scroll_to_path = NULL;
1367 if (tree_view->priv->drag_dest_row != NULL)
1369 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
1370 tree_view->priv->drag_dest_row = NULL;
1373 if (tree_view->priv->top_row != NULL)
1375 gtk_tree_row_reference_free (tree_view->priv->top_row);
1376 tree_view->priv->top_row = NULL;
1379 if (tree_view->priv->column_drop_func_data &&
1380 tree_view->priv->column_drop_func_data_destroy)
1382 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
1383 tree_view->priv->column_drop_func_data = NULL;
1386 if (tree_view->priv->destroy_count_destroy &&
1387 tree_view->priv->destroy_count_data)
1389 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
1390 tree_view->priv->destroy_count_data = NULL;
1393 gtk_tree_row_reference_free (tree_view->priv->cursor);
1394 tree_view->priv->cursor = NULL;
1396 gtk_tree_row_reference_free (tree_view->priv->anchor);
1397 tree_view->priv->anchor = NULL;
1399 /* destroy interactive search dialog */
1400 if (tree_view->priv->search_window)
1402 gtk_widget_destroy (tree_view->priv->search_window);
1403 tree_view->priv->search_window = NULL;
1404 tree_view->priv->search_entry = NULL;
1405 if (tree_view->priv->typeselect_flush_timeout)
1407 g_source_remove (tree_view->priv->typeselect_flush_timeout);
1408 tree_view->priv->typeselect_flush_timeout = 0;
1412 if (tree_view->priv->search_destroy && tree_view->priv->search_user_data)
1414 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
1415 tree_view->priv->search_user_data = NULL;
1418 if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data)
1420 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
1421 tree_view->priv->search_position_user_data = NULL;
1424 pspp_sheet_view_set_model (tree_view, NULL);
1427 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->finalize (object);
1432 /* GtkWidget Methods
1435 /* GtkWidget::map helper */
1437 pspp_sheet_view_map_buttons (PsppSheetView *tree_view)
1441 g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
1443 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
1445 PsppSheetViewColumn *column;
1447 for (list = tree_view->priv->columns; list; list = list->next)
1449 column = list->data;
1450 if (column->button != NULL &&
1451 gtk_widget_get_visible (column->button) &&
1452 !gtk_widget_get_mapped (column->button))
1453 gtk_widget_map (column->button);
1455 for (list = tree_view->priv->columns; list; list = list->next)
1457 column = list->data;
1458 if (column->visible == FALSE || column->window == NULL)
1460 if (column->resizable)
1462 gdk_window_raise (column->window);
1463 gdk_window_show (column->window);
1466 gdk_window_hide (column->window);
1468 gdk_window_show (tree_view->priv->header_window);
1473 pspp_sheet_view_map (GtkWidget *widget)
1475 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1478 gtk_widget_set_mapped (widget, TRUE);
1480 tmp_list = tree_view->priv->children;
1483 PsppSheetViewChild *child = tmp_list->data;
1484 tmp_list = tmp_list->next;
1486 if (gtk_widget_get_visible (child->widget))
1488 if (!gtk_widget_get_mapped (child->widget))
1489 gtk_widget_map (child->widget);
1492 gdk_window_show (tree_view->priv->bin_window);
1494 pspp_sheet_view_map_buttons (tree_view);
1496 gdk_window_show (gtk_widget_get_window (widget));
1500 pspp_sheet_view_realize (GtkWidget *widget)
1502 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1504 GdkWindowAttr attributes;
1505 gint attributes_mask;
1506 GtkAllocation allocation;
1507 GtkAllocation old_allocation;
1509 gtk_widget_set_realized (widget, TRUE);
1511 gtk_widget_get_allocation (widget, &allocation);
1512 gtk_widget_get_allocation (widget, &old_allocation);
1514 /* Make the main, clipping window */
1515 attributes.window_type = GDK_WINDOW_CHILD;
1516 attributes.x = allocation.x;
1517 attributes.y = allocation.y;
1518 attributes.width = allocation.width;
1519 attributes.height = allocation.height;
1520 attributes.wclass = GDK_INPUT_OUTPUT;
1521 attributes.visual = gtk_widget_get_visual (widget);
1522 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1524 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
1526 gtk_widget_set_window (widget,
1527 gdk_window_new (gtk_widget_get_parent_window (widget),
1528 &attributes, attributes_mask));
1529 gdk_window_set_user_data (gtk_widget_get_window (widget), widget);
1531 /* Make the window for the tree */
1533 attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
1534 attributes.width = MAX (tree_view->priv->width, old_allocation.width);
1535 attributes.height = old_allocation.height;
1536 attributes.event_mask = (GDK_EXPOSURE_MASK |
1538 GDK_POINTER_MOTION_MASK |
1539 GDK_ENTER_NOTIFY_MASK |
1540 GDK_LEAVE_NOTIFY_MASK |
1541 GDK_BUTTON_PRESS_MASK |
1542 GDK_BUTTON_RELEASE_MASK |
1543 gtk_widget_get_events (widget));
1545 tree_view->priv->bin_window = gdk_window_new (gtk_widget_get_window (widget),
1546 &attributes, attributes_mask);
1547 gdk_window_set_user_data (tree_view->priv->bin_window, widget);
1549 /* Make the column header window */
1552 attributes.width = MAX (tree_view->priv->width, old_allocation.width);
1553 attributes.height = tree_view->priv->header_height;
1554 attributes.event_mask = (GDK_EXPOSURE_MASK |
1556 GDK_BUTTON_PRESS_MASK |
1557 GDK_BUTTON_RELEASE_MASK |
1558 GDK_KEY_PRESS_MASK |
1559 GDK_KEY_RELEASE_MASK |
1560 gtk_widget_get_events (widget));
1562 tree_view->priv->header_window = gdk_window_new (gtk_widget_get_window (widget),
1563 &attributes, attributes_mask);
1564 gdk_window_set_user_data (tree_view->priv->header_window, widget);
1566 /* Add them all up. */
1567 gtk_widget_set_style (widget,
1568 gtk_style_attach (gtk_widget_get_style (widget), gtk_widget_get_window (widget)));
1569 gdk_window_set_background (tree_view->priv->bin_window, >k_widget_get_style (widget)->base[gtk_widget_get_state (widget)]);
1570 gtk_style_set_background (gtk_widget_get_style (widget), tree_view->priv->header_window, GTK_STATE_NORMAL);
1572 tmp_list = tree_view->priv->children;
1575 PsppSheetViewChild *child = tmp_list->data;
1576 tmp_list = tmp_list->next;
1578 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
1581 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
1582 _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list->data));
1584 /* Need to call those here, since they create GCs */
1585 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
1587 install_presize_handler (tree_view);
1591 pspp_sheet_view_unrealize (GtkWidget *widget)
1593 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1594 PsppSheetViewPrivate *priv = tree_view->priv;
1597 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget);
1599 if (priv->scroll_timeout != 0)
1601 g_source_remove (priv->scroll_timeout);
1602 priv->scroll_timeout = 0;
1605 if (priv->open_dest_timeout != 0)
1607 g_source_remove (priv->open_dest_timeout);
1608 priv->open_dest_timeout = 0;
1611 if (priv->presize_handler_timer != 0)
1613 g_source_remove (priv->presize_handler_timer);
1614 priv->presize_handler_timer = 0;
1617 if (priv->validate_rows_timer != 0)
1619 g_source_remove (priv->validate_rows_timer);
1620 priv->validate_rows_timer = 0;
1623 if (priv->scroll_sync_timer != 0)
1625 g_source_remove (priv->scroll_sync_timer);
1626 priv->scroll_sync_timer = 0;
1629 if (priv->typeselect_flush_timeout)
1631 g_source_remove (priv->typeselect_flush_timeout);
1632 priv->typeselect_flush_timeout = 0;
1635 for (list = priv->columns; list; list = list->next)
1636 _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list->data));
1638 gdk_window_set_user_data (priv->bin_window, NULL);
1639 gdk_window_destroy (priv->bin_window);
1640 priv->bin_window = NULL;
1642 gdk_window_set_user_data (priv->header_window, NULL);
1643 gdk_window_destroy (priv->header_window);
1644 priv->header_window = NULL;
1646 if (priv->drag_window)
1648 gdk_window_set_user_data (priv->drag_window, NULL);
1649 gdk_window_destroy (priv->drag_window);
1650 priv->drag_window = NULL;
1653 if (priv->drag_highlight_window)
1655 gdk_window_set_user_data (priv->drag_highlight_window, NULL);
1656 gdk_window_destroy (priv->drag_highlight_window);
1657 priv->drag_highlight_window = NULL;
1660 if (tree_view->priv->columns != NULL)
1662 list = tree_view->priv->columns;
1665 PsppSheetViewColumn *column;
1666 column = PSPP_SHEET_VIEW_COLUMN (list->data);
1668 pspp_sheet_view_remove_column (tree_view, column);
1670 tree_view->priv->columns = NULL;
1674 /* GtkWidget::size_request helper */
1676 pspp_sheet_view_size_request_columns (PsppSheetView *tree_view)
1680 tree_view->priv->header_height = 0;
1682 if (tree_view->priv->model)
1684 for (list = tree_view->priv->columns; list; list = list->next)
1686 GtkRequisition requisition;
1687 PsppSheetViewColumn *column = list->data;
1689 pspp_sheet_view_column_size_request (column, &requisition);
1690 column->button_request = requisition.width;
1691 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
1697 /* Called only by ::size_request */
1699 pspp_sheet_view_update_size (PsppSheetView *tree_view)
1702 PsppSheetViewColumn *column;
1705 if (tree_view->priv->model == NULL)
1707 tree_view->priv->width = 0;
1708 tree_view->priv->prev_width = 0;
1709 tree_view->priv->height = 0;
1713 tree_view->priv->prev_width = tree_view->priv->width;
1714 tree_view->priv->width = 0;
1716 /* keep this in sync with size_allocate below */
1717 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
1719 gint real_requested_width = 0;
1720 column = list->data;
1721 if (!column->visible)
1724 if (column->use_resized_width)
1726 real_requested_width = column->resized_width;
1730 real_requested_width = column->fixed_width;
1733 if (column->min_width != -1)
1734 real_requested_width = MAX (real_requested_width, column->min_width);
1735 if (column->max_width != -1)
1736 real_requested_width = MIN (real_requested_width, column->max_width);
1738 tree_view->priv->width += real_requested_width;
1741 tree_view->priv->height = tree_view->priv->fixed_height * tree_view->priv->row_count;
1745 pspp_sheet_view_size_request (GtkWidget *widget,
1746 GtkRequisition *requisition)
1748 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1751 /* we validate some rows initially just to make sure we have some size.
1752 * In practice, with a lot of static lists, this should get a good width.
1754 initialize_fixed_height_mode (tree_view);
1755 pspp_sheet_view_size_request_columns (tree_view);
1756 pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget));
1758 requisition->width = tree_view->priv->width;
1759 requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
1761 tmp_list = tree_view->priv->children;
1765 PsppSheetViewChild *child = tmp_list->data;
1766 GtkRequisition child_requisition;
1768 tmp_list = tmp_list->next;
1770 if (gtk_widget_get_visible (child->widget))
1771 gtk_widget_size_request (child->widget, &child_requisition);
1776 invalidate_column (PsppSheetView *tree_view,
1777 PsppSheetViewColumn *column)
1779 gint column_offset = 0;
1781 GtkWidget *widget = GTK_WIDGET (tree_view);
1784 if (!gtk_widget_get_realized (widget))
1787 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1788 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
1790 list = (rtl ? list->prev : list->next))
1792 PsppSheetViewColumn *tmpcolumn = list->data;
1793 if (tmpcolumn == column)
1795 GdkRectangle invalid_rect;
1796 GtkAllocation allocation;
1798 gtk_widget_get_allocation (widget, &allocation);
1799 invalid_rect.x = column_offset;
1801 invalid_rect.width = column->width;
1802 invalid_rect.height = allocation.height;
1804 gdk_window_invalidate_rect (gtk_widget_get_window (widget), &invalid_rect, TRUE);
1808 column_offset += tmpcolumn->width;
1813 invalidate_last_column (PsppSheetView *tree_view)
1818 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1820 for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
1822 last_column = (rtl ? last_column->next : last_column->prev))
1824 if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible)
1826 invalidate_column (tree_view, last_column->data);
1833 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_view,
1834 PsppSheetViewColumn *column)
1836 gint real_requested_width;
1838 if (column->use_resized_width)
1840 real_requested_width = column->resized_width;
1844 real_requested_width = column->fixed_width;
1847 if (column->min_width != -1)
1848 real_requested_width = MAX (real_requested_width, column->min_width);
1849 if (column->max_width != -1)
1850 real_requested_width = MIN (real_requested_width, column->max_width);
1852 return real_requested_width;
1856 span_intersects (int a0, int a_width,
1857 int b0, int b_width)
1859 int a1 = a0 + a_width;
1860 int b1 = b0 + b_width;
1861 return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1);
1864 /* GtkWidget::size_allocate helper */
1866 pspp_sheet_view_size_allocate_columns (GtkWidget *widget,
1867 gboolean *width_changed)
1869 PsppSheetView *tree_view;
1870 GList *list, *first_column, *last_column;
1871 PsppSheetViewColumn *column;
1872 GtkAllocation col_allocation;
1873 GtkAllocation allocation;
1875 gint extra, extra_per_column;
1876 gint full_requested_width = 0;
1877 gint number_of_expand_columns = 0;
1878 gboolean column_changed = FALSE;
1881 tree_view = PSPP_SHEET_VIEW (widget);
1883 for (last_column = g_list_last (tree_view->priv->columns);
1884 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
1885 last_column = last_column->prev)
1888 if (last_column == NULL)
1891 for (first_column = g_list_first (tree_view->priv->columns);
1892 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
1893 first_column = first_column->next)
1896 col_allocation.y = 0;
1897 col_allocation.height = tree_view->priv->header_height;
1899 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1901 /* find out how many extra space and expandable columns we have */
1902 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
1904 column = (PsppSheetViewColumn *)list->data;
1906 if (!column->visible)
1909 full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1912 number_of_expand_columns++;
1915 gtk_widget_get_allocation (widget, &allocation);
1916 extra = MAX (allocation.width - full_requested_width, 0);
1917 if (number_of_expand_columns > 0)
1918 extra_per_column = extra/number_of_expand_columns;
1920 extra_per_column = 0;
1922 for (list = (rtl ? last_column : first_column);
1923 list != (rtl ? first_column->prev : last_column->next);
1924 list = (rtl ? list->prev : list->next))
1926 gint real_requested_width = 0;
1929 column = list->data;
1930 old_width = column->width;
1932 if (!column->visible)
1935 /* We need to handle the dragged button specially.
1937 if (column == tree_view->priv->drag_column)
1939 GtkAllocation drag_allocation;
1940 drag_allocation.width = gdk_window_get_width (tree_view->priv->drag_window);
1941 drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window);
1942 drag_allocation.x = 0;
1943 drag_allocation.y = 0;
1944 pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column,
1946 width += drag_allocation.width;
1950 real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1952 col_allocation.x = width;
1953 column->width = real_requested_width;
1957 if (number_of_expand_columns == 1)
1959 /* We add the remander to the last column as
1961 column->width += extra;
1965 column->width += extra_per_column;
1966 extra -= extra_per_column;
1967 number_of_expand_columns --;
1971 if (column->width != old_width)
1972 g_object_notify (G_OBJECT (column), "width");
1974 col_allocation.width = column->width;
1975 width += column->width;
1977 if (column->width > old_width)
1978 column_changed = TRUE;
1980 pspp_sheet_view_column_size_allocate (column, &col_allocation);
1982 if (span_intersects (col_allocation.x, col_allocation.width,
1983 gtk_adjustment_get_value (tree_view->priv->hadjustment),
1985 && gtk_widget_get_realized (widget))
1986 pspp_sheet_view_column_set_need_button (column, TRUE);
1989 gdk_window_move_resize (column->window,
1990 col_allocation.x + (rtl ? 0 : col_allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
1992 TREE_VIEW_DRAG_WIDTH, col_allocation.height);
1995 /* We change the width here. The user might have been resizing columns,
1996 * so the total width of the tree view changes.
1998 tree_view->priv->width = width;
2000 *width_changed = TRUE;
2003 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2007 pspp_sheet_view_size_allocate (GtkWidget *widget,
2008 GtkAllocation *allocation)
2010 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2012 gboolean width_changed = FALSE;
2013 GtkAllocation old_allocation;
2014 gtk_widget_get_allocation (widget, &old_allocation);
2016 if (allocation->width != old_allocation.width)
2017 width_changed = TRUE;
2020 gtk_widget_set_allocation (widget, allocation);
2022 tmp_list = tree_view->priv->children;
2026 GtkAllocation allocation;
2028 PsppSheetViewChild *child = tmp_list->data;
2029 tmp_list = tmp_list->next;
2031 /* totally ignore our child's requisition */
2032 allocation.x = child->x;
2033 allocation.y = child->y;
2034 allocation.width = child->width;
2035 allocation.height = child->height;
2036 gtk_widget_size_allocate (child->widget, &allocation);
2039 /* We size-allocate the columns first because the width of the
2040 * tree view (used in updating the adjustments below) might change.
2042 pspp_sheet_view_size_allocate_columns (widget, &width_changed);
2044 gtk_adjustment_set_page_size (tree_view->priv->hadjustment, allocation->width);
2045 gtk_adjustment_set_page_increment (tree_view->priv->hadjustment, allocation->width * 0.9);
2046 gtk_adjustment_set_step_increment (tree_view->priv->hadjustment, allocation->width * 0.1);
2047 gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0);
2048 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->hadjustment), tree_view->priv->width));
2050 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2052 if (allocation->width < tree_view->priv->width)
2054 if (tree_view->priv->init_hadjust_value)
2056 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2057 tree_view->priv->init_hadjust_value = FALSE;
2059 else if (allocation->width != old_allocation.width)
2061 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));
2064 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));
2068 gtk_adjustment_set_value (tree_view->priv->hadjustment, 0);
2069 tree_view->priv->init_hadjust_value = TRUE;
2073 if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + allocation->width > tree_view->priv->width)
2074 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2076 gtk_adjustment_changed (tree_view->priv->hadjustment);
2078 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2079 gtk_adjustment_set_step_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.1);
2080 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.9);
2081 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
2082 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->vadjustment), tree_view->priv->height));
2084 gtk_adjustment_changed (tree_view->priv->vadjustment);
2086 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2087 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
2088 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2089 else if (gtk_adjustment_get_value (tree_view->priv->vadjustment) + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
2090 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2091 tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
2092 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2093 pspp_sheet_view_top_row_to_dy (tree_view);
2095 pspp_sheet_view_dy_to_top_row (tree_view);
2097 if (gtk_widget_get_realized (widget))
2099 gdk_window_move_resize (gtk_widget_get_window (widget),
2100 allocation->x, allocation->y,
2101 allocation->width, allocation->height);
2102 gdk_window_move_resize (tree_view->priv->header_window,
2103 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2105 MAX (tree_view->priv->width, allocation->width),
2106 tree_view->priv->header_height);
2107 gdk_window_move_resize (tree_view->priv->bin_window,
2108 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2109 TREE_VIEW_HEADER_HEIGHT (tree_view),
2110 MAX (tree_view->priv->width, allocation->width),
2111 allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2114 if (tree_view->priv->row_count == 0)
2115 invalidate_empty_focus (tree_view);
2117 if (gtk_widget_get_realized (widget))
2119 gboolean has_expand_column = FALSE;
2120 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2122 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2124 has_expand_column = TRUE;
2129 /* This little hack only works if we have an LTR locale, and no column has the */
2132 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2133 ! has_expand_column)
2134 invalidate_last_column (tree_view);
2136 gtk_widget_queue_draw (widget);
2141 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2143 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2145 GtkWidget *widget = GTK_WIDGET (tree_view);
2147 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2148 gtk_widget_grab_focus (widget);
2149 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2153 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2156 return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2160 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2163 range_tower_set1 (tree_view->priv->selected, node, 1);
2167 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2170 range_tower_set0 (tree_view->priv->selected, node, 1);
2174 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2177 return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2181 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2184 return node > 0 ? node - 1 : -1;
2188 all_columns_selected (PsppSheetView *tree_view)
2192 for (list = tree_view->priv->columns; list; list = list->next)
2194 PsppSheetViewColumn *column = list->data;
2195 if (column->selectable && !column->selected)
2203 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2205 PsppSheetViewColumn *column,
2206 GdkEventButton *event)
2208 PsppSheetSelection *selection;
2209 PsppSheetSelectionMode mode;
2211 gboolean update_anchor;
2215 g_return_val_if_fail (tree_view != NULL, FALSE);
2216 g_return_val_if_fail (column != NULL, FALSE);
2218 selection = tree_view->priv->selection;
2219 mode = pspp_sheet_selection_get_mode (selection);
2220 if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2223 if (!column->row_head)
2228 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2229 if (event->type != GDK_BUTTON_PRESS
2230 || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2236 path = gtk_tree_path_new_from_indices (node, -1);
2239 pspp_sheet_selection_unselect_all (selection);
2240 pspp_sheet_selection_select_path (selection, path);
2241 pspp_sheet_selection_select_all_columns (selection);
2242 update_anchor = TRUE;
2245 else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2247 if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2248 || !all_columns_selected (tree_view))
2250 pspp_sheet_selection_unselect_all (selection);
2251 pspp_sheet_selection_select_path (selection, path);
2252 pspp_sheet_selection_select_all_columns (selection);
2253 update_anchor = TRUE;
2257 update_anchor = handled = FALSE;
2259 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2260 && modifiers == GDK_CONTROL_MASK)
2262 if (!all_columns_selected (tree_view))
2264 pspp_sheet_selection_unselect_all (selection);
2265 pspp_sheet_selection_select_all_columns (selection);
2268 if (pspp_sheet_selection_path_is_selected (selection, path))
2269 pspp_sheet_selection_unselect_path (selection, path);
2271 pspp_sheet_selection_select_path (selection, path);
2272 update_anchor = TRUE;
2275 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2276 && modifiers == GDK_SHIFT_MASK)
2278 GtkTreeRowReference *anchor = tree_view->priv->anchor;
2279 GtkTreePath *anchor_path;
2281 if (all_columns_selected (tree_view)
2282 && gtk_tree_row_reference_valid (anchor))
2284 update_anchor = FALSE;
2285 anchor_path = gtk_tree_row_reference_get_path (anchor);
2289 update_anchor = TRUE;
2290 anchor_path = gtk_tree_path_copy (path);
2293 pspp_sheet_selection_unselect_all (selection);
2294 pspp_sheet_selection_select_range (selection, anchor_path, path);
2295 pspp_sheet_selection_select_all_columns (selection);
2297 gtk_tree_path_free (anchor_path);
2302 update_anchor = handled = FALSE;
2306 if (tree_view->priv->anchor)
2307 gtk_tree_row_reference_free (tree_view->priv->anchor);
2308 tree_view->priv->anchor =
2309 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2310 tree_view->priv->model,
2314 gtk_tree_path_free (path);
2319 find_click (PsppSheetView *tree_view,
2322 PsppSheetViewColumn **column,
2323 GdkRectangle *background_area,
2324 GdkRectangle *cell_area)
2331 /* find the node that was clicked */
2332 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2335 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2340 background_area->y = y_offset + y;
2341 background_area->height = ROW_HEIGHT (tree_view);
2342 background_area->x = 0;
2344 /* Let the column have a chance at selecting it. */
2345 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2346 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2347 list; list = (rtl ? list->prev : list->next))
2349 PsppSheetViewColumn *candidate = list->data;
2351 if (!candidate->visible)
2354 background_area->width = candidate->width;
2355 if ((background_area->x > x) ||
2356 (background_area->x + background_area->width <= x))
2358 background_area->x += background_area->width;
2362 /* we found the focus column */
2364 pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2366 *column = candidate;
2374 pspp_sheet_view_button_press (GtkWidget *widget,
2375 GdkEventButton *event)
2377 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2379 PsppSheetViewColumn *column = NULL;
2381 GdkRectangle background_area;
2382 GdkRectangle cell_area;
2385 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2386 pspp_sheet_view_stop_editing (tree_view, FALSE);
2389 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2390 * we're done handling the button press.
2393 if (event->window == tree_view->priv->bin_window)
2398 gint pre_val, aft_val;
2399 PsppSheetViewColumn *column = NULL;
2400 GtkCellRenderer *focus_cell = NULL;
2401 gboolean row_double_click = FALSE;
2404 if (tree_view->priv->row_count == 0)
2406 grab_focus_and_unset_draw_keyfocus (tree_view);
2410 if (!find_click (tree_view, event->x, event->y, &node, &column,
2411 &background_area, &cell_area))
2413 grab_focus_and_unset_draw_keyfocus (tree_view);
2417 tree_view->priv->focus_column = column;
2419 if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2423 pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2425 path = _pspp_sheet_view_find_path (tree_view, node);
2427 /* we only handle selection modifications on the first button press
2429 if (event->type == GDK_BUTTON_PRESS)
2431 PsppSheetSelectionMode mode = 0;
2433 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2434 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
2435 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2436 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
2438 focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2440 pspp_sheet_view_column_focus_cell (column, focus_cell);
2442 if (event->state & GDK_CONTROL_MASK)
2444 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, mode);
2445 pspp_sheet_view_real_toggle_cursor_row (tree_view);
2447 else if (event->state & GDK_SHIFT_MASK)
2449 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
2450 pspp_sheet_view_real_select_cursor_row (tree_view, FALSE, mode);
2454 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
2457 if (tree_view->priv->anchor_column == NULL ||
2458 !(event->state & GDK_SHIFT_MASK))
2459 tree_view->priv->anchor_column = column;
2460 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2461 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2462 tree_view->priv->anchor_column,
2466 /* the treeview may have been scrolled because of _set_cursor,
2470 aft_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2471 dval = pre_val - aft_val;
2473 cell_area.y += dval;
2474 background_area.y += dval;
2476 /* Save press to possibly begin a drag
2478 if (!tree_view->priv->in_grab &&
2479 tree_view->priv->pressed_button < 0)
2481 tree_view->priv->pressed_button = event->button;
2482 tree_view->priv->press_start_x = event->x;
2483 tree_view->priv->press_start_y = event->y;
2484 tree_view->priv->press_start_node = node;
2486 if (tree_view->priv->rubber_banding_enable
2487 && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2488 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2490 tree_view->priv->press_start_y += tree_view->priv->dy;
2491 tree_view->priv->rubber_band_x = event->x;
2492 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2493 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2495 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2496 tree_view->priv->rubber_band_ctrl = TRUE;
2497 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2498 tree_view->priv->rubber_band_shift = TRUE;
2503 /* Test if a double click happened on the same row. */
2504 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2506 int double_click_time, double_click_distance;
2508 g_object_get (gtk_settings_get_for_screen (
2509 gtk_widget_get_screen (widget)),
2510 "gtk-double-click-time", &double_click_time,
2511 "gtk-double-click-distance", &double_click_distance,
2514 /* Same conditions as _gdk_event_button_generate */
2515 if (tree_view->priv->last_button_x != -1 &&
2516 (event->time < tree_view->priv->last_button_time + double_click_time) &&
2517 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2518 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2520 /* We do no longer compare paths of this row and the
2521 * row clicked previously. We use the double click
2522 * distance to decide whether this is a valid click,
2523 * allowing the mouse to slightly move over another row.
2525 row_double_click = TRUE;
2527 tree_view->priv->last_button_time = 0;
2528 tree_view->priv->last_button_x = -1;
2529 tree_view->priv->last_button_y = -1;
2533 tree_view->priv->last_button_time = event->time;
2534 tree_view->priv->last_button_x = event->x;
2535 tree_view->priv->last_button_y = event->y;
2539 if (row_double_click)
2541 gtk_grab_remove (widget);
2542 pspp_sheet_view_row_activated (tree_view, path, column);
2544 if (tree_view->priv->pressed_button == event->button)
2545 tree_view->priv->pressed_button = -1;
2548 gtk_tree_path_free (path);
2550 /* If we activated the row through a double click we don't want to grab
2551 * focus back, as moving focus to another widget is pretty common.
2553 if (!row_double_click)
2554 grab_focus_and_unset_draw_keyfocus (tree_view);
2559 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2561 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2563 column = list->data;
2564 if (event->window == column->window &&
2565 column->resizable &&
2570 if (gdk_pointer_grab (column->window, FALSE,
2571 GDK_POINTER_MOTION_HINT_MASK |
2572 GDK_BUTTON1_MOTION_MASK |
2573 GDK_BUTTON_RELEASE_MASK,
2574 NULL, NULL, event->time))
2577 gtk_grab_add (widget);
2578 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2579 column->resized_width = column->width;
2581 /* block attached dnd signal handler */
2582 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2584 g_signal_handlers_block_matched (widget,
2585 G_SIGNAL_MATCH_DATA,
2589 tree_view->priv->drag_pos = i;
2590 tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2592 if (!gtk_widget_has_focus (widget))
2593 gtk_widget_grab_focus (widget);
2601 /* GtkWidget::button_release_event helper */
2603 pspp_sheet_view_button_release_drag_column (GtkWidget *widget,
2604 GdkEventButton *event)
2606 PsppSheetView *tree_view;
2610 tree_view = PSPP_SHEET_VIEW (widget);
2612 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2613 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2614 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2616 /* Move the button back */
2617 g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2619 g_object_ref (tree_view->priv->drag_column->button);
2620 gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2621 gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2622 gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2623 g_object_unref (tree_view->priv->drag_column->button);
2624 gtk_widget_queue_resize (widget);
2625 if (tree_view->priv->drag_column->resizable)
2627 gdk_window_raise (tree_view->priv->drag_column->window);
2628 gdk_window_show (tree_view->priv->drag_column->window);
2631 gdk_window_hide (tree_view->priv->drag_column->window);
2633 gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2637 if (tree_view->priv->cur_reorder &&
2638 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2639 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2640 tree_view->priv->cur_reorder->right_column);
2644 if (tree_view->priv->cur_reorder &&
2645 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2646 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2647 tree_view->priv->cur_reorder->left_column);
2649 tree_view->priv->drag_column = NULL;
2650 gdk_window_hide (tree_view->priv->drag_window);
2652 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2653 g_slice_free (PsppSheetViewColumnReorder, l->data);
2654 g_list_free (tree_view->priv->column_drag_info);
2655 tree_view->priv->column_drag_info = NULL;
2656 tree_view->priv->cur_reorder = NULL;
2658 if (tree_view->priv->drag_highlight_window)
2659 gdk_window_hide (tree_view->priv->drag_highlight_window);
2661 /* Reset our flags */
2662 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2663 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2668 /* GtkWidget::button_release_event helper */
2670 pspp_sheet_view_button_release_column_resize (GtkWidget *widget,
2671 GdkEventButton *event)
2673 PsppSheetView *tree_view;
2676 tree_view = PSPP_SHEET_VIEW (widget);
2678 tree_view->priv->drag_pos = -1;
2680 /* unblock attached dnd signal handler */
2681 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2683 g_signal_handlers_unblock_matched (widget,
2684 G_SIGNAL_MATCH_DATA,
2688 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2689 gtk_grab_remove (widget);
2690 gdk_display_pointer_ungrab (gdk_window_get_display (event->window),
2696 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2697 GdkEventButton *event)
2699 GtkCellEditable *cell_editable;
2704 PsppSheetViewColumn *column;
2705 GdkRectangle background_area;
2706 GdkRectangle cell_area;
2712 if (event->window != tree_view->priv->bin_window)
2715 /* Ignore a released button, if that button wasn't depressed */
2716 if (tree_view->priv->pressed_button != event->button)
2719 if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2723 /* decide if we edit */
2724 path = _pspp_sheet_view_find_path (tree_view, node);
2725 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2726 if (event->button != 1 || modifiers)
2729 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2730 pspp_sheet_view_column_cell_set_cell_data (column,
2731 tree_view->priv->model,
2734 if (!pspp_sheet_view_column_get_quick_edit (column)
2735 && _pspp_sheet_view_column_has_editable_cell (column))
2738 flags = 0; /* FIXME: get the right flags */
2739 path_string = gtk_tree_path_to_string (path);
2741 if (!_pspp_sheet_view_column_cell_event (column,
2749 if (cell_editable == NULL)
2752 pspp_sheet_view_real_set_cursor (tree_view, path,
2753 TRUE, TRUE, 0); /* XXX mode? */
2754 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2757 _pspp_sheet_view_column_get_neighbor_sizes (
2758 column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2761 area.width -= right + left;
2763 pspp_sheet_view_real_start_editing (tree_view,
2770 g_free (path_string);
2771 gtk_tree_path_free (path);
2776 pspp_sheet_view_button_release (GtkWidget *widget,
2777 GdkEventButton *event)
2779 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2781 pspp_sheet_view_stop_editing (tree_view, FALSE);
2782 if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2783 && pspp_sheet_view_button_release_edit (tree_view, event))
2785 if (tree_view->priv->pressed_button == event->button)
2786 tree_view->priv->pressed_button = -1;
2788 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2792 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2793 return pspp_sheet_view_button_release_drag_column (widget, event);
2795 if (tree_view->priv->rubber_band_status)
2796 pspp_sheet_view_stop_rubber_band (tree_view);
2798 if (tree_view->priv->pressed_button == event->button)
2799 tree_view->priv->pressed_button = -1;
2801 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2802 return pspp_sheet_view_button_release_column_resize (widget, event);
2808 pspp_sheet_view_grab_broken (GtkWidget *widget,
2809 GdkEventGrabBroken *event)
2811 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2813 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2814 pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2816 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2817 pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2822 /* GtkWidget::motion_event function set.
2826 do_prelight (PsppSheetView *tree_view,
2828 /* these are in bin_window coords */
2832 int prev_node = tree_view->priv->prelight_node;
2834 if (prev_node != node)
2836 tree_view->priv->prelight_node = node;
2839 _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2842 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2848 prelight_or_select (PsppSheetView *tree_view,
2850 /* these are in bin_window coords */
2854 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2856 if (tree_view->priv->hover_selection &&
2857 (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2858 !(tree_view->priv->edited_column &&
2859 tree_view->priv->edited_column->editable_widget))
2863 if (!pspp_sheet_view_node_is_selected (tree_view, node))
2867 path = _pspp_sheet_view_find_path (tree_view, node);
2868 pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2869 if (pspp_sheet_view_node_is_selected (tree_view, node))
2871 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2872 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE, 0); /* XXX mode? */
2874 gtk_tree_path_free (path);
2878 else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2879 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2882 do_prelight (tree_view, node, x, y);
2886 ensure_unprelighted (PsppSheetView *tree_view)
2888 do_prelight (tree_view,
2890 -1000, -1000); /* coords not possibly over an arrow */
2892 g_assert (tree_view->priv->prelight_node < 0);
2896 update_prelight (PsppSheetView *tree_view,
2903 if (tree_view->priv->row_count == 0)
2908 ensure_unprelighted (tree_view);
2912 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2916 pspp_sheet_view_find_offset (tree_view, new_y, &node);
2919 prelight_or_select (tree_view, node, x, y);
2925 /* Our motion arrow is either a box (in the case of the original spot)
2926 * or an arrow. It is expander_size wide.
2949 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2952 PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2953 GtkWidget *widget = GTK_WIDGET (tree_view);
2954 GdkBitmap *mask = NULL;
2959 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2960 GdkWindowAttr attributes;
2961 guint attributes_mask;
2964 reorder->left_column == tree_view->priv->drag_column ||
2965 reorder->right_column == tree_view->priv->drag_column)
2966 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2967 else if (reorder->left_column || reorder->right_column)
2969 GdkRectangle visible_rect;
2970 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2971 if (reorder->left_column)
2972 x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2974 x = reorder->right_column->allocation.x;
2976 if (x < visible_rect.x)
2977 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2978 else if (x > visible_rect.x + visible_rect.width)
2979 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2981 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2984 /* We want to draw the rectangle over the initial location. */
2985 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2990 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2992 if (tree_view->priv->drag_highlight_window)
2994 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2996 gdk_window_destroy (tree_view->priv->drag_highlight_window);
2999 attributes.window_type = GDK_WINDOW_CHILD;
3000 attributes.wclass = GDK_INPUT_OUTPUT;
3001 attributes.x = tree_view->priv->drag_column_x;
3003 width = attributes.width = tree_view->priv->drag_column->allocation.width;
3004 height = attributes.height = tree_view->priv->drag_column->allocation.height;
3005 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3006 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3007 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3008 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3009 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
3010 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3012 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3013 gc = gdk_gc_new (mask);
3015 gdk_gc_set_foreground (gc, &col);
3016 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3018 gdk_gc_set_foreground(gc, &col);
3019 gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
3020 g_object_unref (gc);
3022 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3024 if (mask) g_object_unref (mask);
3025 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3028 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
3034 width = tree_view->priv->expander_size;
3036 /* Get x, y, width, height of arrow */
3037 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
3038 if (reorder->left_column)
3040 x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
3041 height = reorder->left_column->allocation.height;
3045 x += reorder->right_column->allocation.x - width/2;
3046 height = reorder->right_column->allocation.height;
3048 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3049 height += tree_view->priv->expander_size;
3051 /* Create the new window */
3052 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3054 if (tree_view->priv->drag_highlight_window)
3056 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3058 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3061 attributes.window_type = GDK_WINDOW_TEMP;
3062 attributes.wclass = GDK_INPUT_OUTPUT;
3063 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3064 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3065 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3066 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3069 attributes.width = width;
3070 attributes.height = height;
3071 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3072 &attributes, attributes_mask);
3073 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3075 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3076 gc = gdk_gc_new (mask);
3078 gdk_gc_set_foreground (gc, &col);
3079 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3081 /* Draw the 2 arrows as per above */
3083 gdk_gc_set_foreground (gc, &col);
3084 for (i = 0; i < width; i ++)
3086 if (i == (width/2 - 1))
3088 gdk_draw_line (mask, gc, i, j, i, height - j);
3089 if (i < (width/2 - 1))
3094 g_object_unref (gc);
3095 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3097 if (mask) g_object_unref (mask);
3100 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3101 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3103 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3104 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3110 width = tree_view->priv->expander_size;
3112 /* Get x, y, width, height of arrow */
3113 width = width/2; /* remember, the arrow only takes half the available width */
3114 gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
3115 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3116 x += widget->allocation.width - width;
3118 if (reorder->left_column)
3119 height = reorder->left_column->allocation.height;
3121 height = reorder->right_column->allocation.height;
3123 y -= tree_view->priv->expander_size;
3124 height += 2*tree_view->priv->expander_size;
3126 /* Create the new window */
3127 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3128 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3130 if (tree_view->priv->drag_highlight_window)
3132 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3134 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3137 attributes.window_type = GDK_WINDOW_TEMP;
3138 attributes.wclass = GDK_INPUT_OUTPUT;
3139 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3140 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3141 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3142 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3145 attributes.width = width;
3146 attributes.height = height;
3147 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3148 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3150 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3151 gc = gdk_gc_new (mask);
3153 gdk_gc_set_foreground (gc, &col);
3154 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3156 /* Draw the 2 arrows as per above */
3158 gdk_gc_set_foreground (gc, &col);
3159 j = tree_view->priv->expander_size;
3160 for (i = 0; i < width; i ++)
3163 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3167 gdk_draw_line (mask, gc, k, j, k, height - j);
3168 gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3169 gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3172 g_object_unref (gc);
3173 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3175 if (mask) g_object_unref (mask);
3178 tree_view->priv->drag_column_window_state = arrow_type;
3179 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3183 g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3184 gdk_window_hide (tree_view->priv->drag_highlight_window);
3188 gdk_window_show (tree_view->priv->drag_highlight_window);
3189 gdk_window_raise (tree_view->priv->drag_highlight_window);
3194 pspp_sheet_view_motion_resize_column (GtkWidget *widget,
3195 GdkEventMotion *event)
3199 PsppSheetViewColumn *column;
3200 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3202 column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3204 if (event->is_hint || event->window != gtk_widget_get_window (widget))
3205 gtk_widget_get_pointer (widget, &x, NULL);
3209 if (tree_view->priv->hadjustment)
3210 x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
3212 new_width = pspp_sheet_view_new_column_width (tree_view,
3213 tree_view->priv->drag_pos, &x);
3214 if (x != tree_view->priv->x_drag &&
3215 (new_width != column->fixed_width))
3217 column->use_resized_width = TRUE;
3218 column->resized_width = new_width;
3221 column->resized_width -= tree_view->priv->last_extra_space_per_column;
3223 gtk_widget_queue_resize (widget);
3231 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3233 PsppSheetViewColumnReorder *reorder = NULL;
3237 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3238 for (list = tree_view->priv->column_drag_info; list; list = list->next)
3240 reorder = (PsppSheetViewColumnReorder *) list->data;
3241 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3246 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3249 tree_view->priv->cur_reorder = reorder;
3250 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3254 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3256 GdkRectangle visible_rect;
3261 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3262 y += tree_view->priv->dy;
3264 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3266 /* see if we are near the edge. */
3267 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3270 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3275 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->vadjustment) + offset, 0.0,
3276 gtk_adjustment_get_upper (tree_view->priv->vadjustment) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
3277 gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3281 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3283 GdkRectangle visible_rect;
3288 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3290 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3292 /* See if we are near the edge. */
3293 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3296 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3302 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) + offset,
3303 0.0, gtk_adjustment_get_upper (tree_view->priv->hadjustment) - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
3304 gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3311 pspp_sheet_view_motion_drag_column (GtkWidget *widget,
3312 GdkEventMotion *event)
3314 PsppSheetView *tree_view = (PsppSheetView *) widget;
3315 PsppSheetViewColumn *column = tree_view->priv->drag_column;
3317 GtkAllocation allocation;
3320 if ((column == NULL) ||
3321 (event->window != tree_view->priv->drag_window))
3324 /* Handle moving the header */
3325 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3326 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
3327 x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3328 MAX (tree_view->priv->width, allocation.width) - column->allocation.width);
3329 gdk_window_move (tree_view->priv->drag_window, x, y);
3331 /* autoscroll, if needed */
3332 pspp_sheet_view_horizontal_autoscroll (tree_view);
3333 /* Update the current reorder position and arrow; */
3334 pspp_sheet_view_update_current_reorder (tree_view);
3340 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3342 remove_scroll_timeout (tree_view);
3343 gtk_grab_remove (GTK_WIDGET (tree_view));
3345 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3347 GtkTreePath *tmp_path;
3349 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3351 /* The anchor path should be set to the start path */
3352 tmp_path = _pspp_sheet_view_find_path (tree_view,
3353 tree_view->priv->rubber_band_start_node);
3355 if (tree_view->priv->anchor)
3356 gtk_tree_row_reference_free (tree_view->priv->anchor);
3358 tree_view->priv->anchor =
3359 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3360 tree_view->priv->model,
3363 gtk_tree_path_free (tmp_path);
3365 /* ... and the cursor to the end path */
3366 tmp_path = _pspp_sheet_view_find_path (tree_view,
3367 tree_view->priv->rubber_band_end_node);
3368 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE, 0); /* XXX mode? */
3369 gtk_tree_path_free (tmp_path);
3371 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3374 /* Clear status variables */
3375 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3376 tree_view->priv->rubber_band_shift = 0;
3377 tree_view->priv->rubber_band_ctrl = 0;
3379 tree_view->priv->rubber_band_start_node = -1;
3380 tree_view->priv->rubber_band_end_node = -1;
3384 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3388 gboolean skip_start,
3391 if (start_node == end_node)
3394 /* We skip the first node and jump inside the loop */
3400 /* Small optimization by assuming insensitive nodes are never
3405 if (tree_view->priv->rubber_band_shift)
3406 pspp_sheet_view_node_select (tree_view, start_node);
3407 else if (tree_view->priv->rubber_band_ctrl)
3409 /* Toggle the selection state */
3410 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3411 pspp_sheet_view_node_unselect (tree_view, start_node);
3413 pspp_sheet_view_node_select (tree_view, start_node);
3416 pspp_sheet_view_node_select (tree_view, start_node);
3420 /* Mirror the above */
3421 if (tree_view->priv->rubber_band_shift)
3422 pspp_sheet_view_node_unselect (tree_view, start_node);
3423 else if (tree_view->priv->rubber_band_ctrl)
3425 /* Toggle the selection state */
3426 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3427 pspp_sheet_view_node_unselect (tree_view, start_node);
3429 pspp_sheet_view_node_select (tree_view, start_node);
3432 pspp_sheet_view_node_unselect (tree_view, start_node);
3435 _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3437 if (start_node == end_node)
3442 start_node = pspp_sheet_view_node_next (tree_view, start_node);
3445 /* Ran out of tree */
3448 if (skip_end && start_node == end_node)
3455 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3458 return node * tree_view->priv->fixed_height;
3462 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3466 int fixed_height = tree_view->priv->fixed_height;
3467 if (fixed_height <= 0
3469 || height >= tree_view->priv->row_count * fixed_height)
3476 *new_node = height / fixed_height;
3477 return height % fixed_height;
3482 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3487 pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3488 pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3490 /* Handle the start area first */
3491 if (tree_view->priv->rubber_band_start_node < 0)
3493 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3500 else if (start_node < tree_view->priv->rubber_band_start_node)
3502 /* New node is above the old one; selection became bigger */
3503 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3505 tree_view->priv->rubber_band_start_node,
3510 else if (start_node > tree_view->priv->rubber_band_start_node)
3512 /* New node is below the old one; selection became smaller */
3513 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3514 tree_view->priv->rubber_band_start_node,
3521 tree_view->priv->rubber_band_start_node = start_node;
3523 /* Next, handle the end area */
3524 if (tree_view->priv->rubber_band_end_node < 0)
3526 /* In the event this happens, start_node was also -1; this case is
3530 else if (end_node < 0)
3532 /* Find the last node in the tree */
3533 pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3536 /* Selection reached end of the tree */
3537 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3538 tree_view->priv->rubber_band_end_node,
3544 else if (end_node > tree_view->priv->rubber_band_end_node)
3546 /* New node is below the old one; selection became bigger */
3547 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3548 tree_view->priv->rubber_band_end_node,
3554 else if (end_node < tree_view->priv->rubber_band_end_node)
3556 /* New node is above the old one; selection became smaller */
3557 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3559 tree_view->priv->rubber_band_end_node,
3565 tree_view->priv->rubber_band_end_node = end_node;
3568 #define GDK_RECTANGLE_PTR(X) ((GdkRectangle *)(X))
3571 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3574 cairo_rectangle_int_t old_area;
3575 cairo_rectangle_int_t new_area;
3576 cairo_rectangle_int_t common;
3577 cairo_region_t *invalid_region;
3578 PsppSheetViewColumn *column;
3580 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3581 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3582 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3583 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3585 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3588 y = MAX (y, 0) + tree_view->priv->dy;
3590 new_area.x = MIN (tree_view->priv->press_start_x, x);
3591 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3592 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3593 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3595 invalid_region = cairo_region_create_rectangle (&old_area);
3596 cairo_region_union_rectangle (invalid_region, &new_area);
3598 gdk_rectangle_intersect (GDK_RECTANGLE_PTR (&old_area),
3599 GDK_RECTANGLE_PTR (&new_area), GDK_RECTANGLE_PTR (&common));
3600 if (common.width > 2 && common.height > 2)
3602 cairo_region_t *common_region;
3604 /* make sure the border is invalidated */
3610 common_region = cairo_region_create_rectangle (&common);
3612 cairo_region_subtract (invalid_region, common_region);
3613 cairo_region_destroy (common_region);
3616 #if GTK_MAJOR_VERSION == 3
3617 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
3620 cairo_rectangle_int_t extents;
3622 cairo_region_get_extents (invalid_region, &extents);
3623 ereg = gdk_region_rectangle (GDK_RECTANGLE_PTR (&extents));
3624 gdk_window_invalidate_region (tree_view->priv->bin_window, ereg, TRUE);
3625 gdk_region_destroy (ereg);
3629 cairo_region_destroy (invalid_region);
3631 tree_view->priv->rubber_band_x = x;
3632 tree_view->priv->rubber_band_y = y;
3633 pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3635 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3636 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3637 tree_view->priv->anchor_column,
3640 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3642 pspp_sheet_view_update_rubber_band_selection (tree_view);
3647 pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view,
3652 GdkRectangle rubber_rect;
3656 rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3657 rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3658 rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3659 rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3661 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3664 cr = gdk_cairo_create (tree_view->priv->bin_window);
3665 cairo_set_line_width (cr, 1.0);
3667 style = gtk_widget_get_style (GTK_WIDGET (tree_view));
3668 cairo_set_source_rgba (cr,
3669 style->fg[GTK_STATE_NORMAL].red / 65535.,
3670 style->fg[GTK_STATE_NORMAL].green / 65535.,
3671 style->fg[GTK_STATE_NORMAL].blue / 65535.,
3674 gdk_cairo_rectangle (cr, &rect);
3678 cairo_set_source_rgb (cr,
3679 style->fg[GTK_STATE_NORMAL].red / 65535.,
3680 style->fg[GTK_STATE_NORMAL].green / 65535.,
3681 style->fg[GTK_STATE_NORMAL].blue / 65535.);
3683 cairo_rectangle (cr,
3684 rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3685 rubber_rect.width - 1, rubber_rect.height - 1);
3694 pspp_sheet_view_motion_bin_window (GtkWidget *widget,
3695 GdkEventMotion *event)
3697 PsppSheetView *tree_view;
3701 tree_view = (PsppSheetView *) widget;
3703 if (tree_view->priv->row_count == 0)
3706 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3708 GdkRectangle background_area, cell_area;
3709 PsppSheetViewColumn *column;
3711 if (find_click (tree_view, event->x, event->y, &node, &column,
3712 &background_area, &cell_area)
3713 && tree_view->priv->focus_column == column
3714 && tree_view->priv->press_start_node == node)
3717 gtk_grab_add (GTK_WIDGET (tree_view));
3718 pspp_sheet_view_update_rubber_band (tree_view);
3720 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3722 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3724 pspp_sheet_view_update_rubber_band (tree_view);
3726 add_scroll_timeout (tree_view);
3729 /* only check for an initiated drag when a button is pressed */
3730 if (tree_view->priv->pressed_button >= 0
3731 && !tree_view->priv->rubber_band_status)
3732 pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3734 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3738 pspp_sheet_view_find_offset (tree_view, new_y, &node);
3740 tree_view->priv->event_last_x = event->x;
3741 tree_view->priv->event_last_y = event->y;
3743 prelight_or_select (tree_view, node, event->x, event->y);
3749 pspp_sheet_view_motion (GtkWidget *widget,
3750 GdkEventMotion *event)
3752 PsppSheetView *tree_view;
3754 tree_view = (PsppSheetView *) widget;
3756 /* Resizing a column */
3757 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3758 return pspp_sheet_view_motion_resize_column (widget, event);
3761 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3762 return pspp_sheet_view_motion_drag_column (widget, event);
3764 /* Sanity check it */
3765 if (event->window == tree_view->priv->bin_window)
3766 return pspp_sheet_view_motion_bin_window (widget, event);
3771 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3772 * the tree is empty.
3775 invalidate_empty_focus (PsppSheetView *tree_view)
3779 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3784 area.width = gdk_window_get_width (tree_view->priv->bin_window);
3785 area.height = gdk_window_get_height (tree_view->priv->bin_window);
3786 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3789 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3793 draw_empty_focus (PsppSheetView *tree_view)
3795 GtkWidget *widget = GTK_WIDGET (tree_view);
3797 cairo_t *cr = gdk_cairo_create (tree_view->priv->bin_window);
3799 if (!gtk_widget_has_focus (widget))
3802 w = gdk_window_get_width (tree_view->priv->bin_window);
3803 h = gdk_window_get_height (tree_view->priv->bin_window);
3809 gtk_paint_focus (gtk_widget_get_style (widget),
3811 gtk_widget_get_state (widget),
3819 pspp_sheet_view_draw_vertical_grid_lines (PsppSheetView *tree_view,
3821 gint n_visible_columns,
3825 GList *list = tree_view->priv->columns;
3829 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3830 && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3833 /* Only draw the lines for visible rows and columns */
3834 for (list = tree_view->priv->columns; list; list = list->next, i++)
3836 PsppSheetViewColumn *column = list->data;
3839 if (! column->visible)
3842 current_x += column->width;
3844 /* Generally the grid lines should fit within the column, but for the
3845 last visible column we put it just past the end of the column.
3846 (Otherwise horizontal grid lines sometimes stick out by one pixel.) */
3848 if (i != n_visible_columns - 1)
3851 cairo_set_line_width (cr, 1.0);
3852 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
3853 cairo_move_to (cr, x + 0.5, min_y);
3854 cairo_line_to (cr, x + 0.5, max_y - min_y);
3859 /* Warning: Very scary function.
3860 * Modify at your own risk
3862 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3863 * FIXME: It's not...
3866 pspp_sheet_view_bin_expose (GtkWidget *widget,
3869 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3874 int drag_highlight = -1;
3877 gint y_offset, cell_offset;
3879 GdkRectangle background_area;
3880 GdkRectangle cell_area;
3882 gint bin_window_width;
3883 gint bin_window_height;
3884 GtkTreePath *cursor_path;
3885 GtkTreePath *drag_dest_path;
3886 GList *first_column, *last_column;
3887 gint vertical_separator;
3888 gint horizontal_separator;
3889 gint focus_line_width;
3890 gboolean allow_rules;
3891 gboolean has_special_cell;
3893 gint n_visible_columns;
3894 gint grid_line_width;
3895 gboolean row_ending_details;
3896 gboolean draw_vgrid_lines, draw_hgrid_lines;
3900 GtkAllocation allocation;
3901 gtk_widget_get_allocation (widget, &allocation);
3905 Zarea.height = allocation.height;
3907 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3909 gtk_widget_style_get (widget,
3910 "horizontal-separator", &horizontal_separator,
3911 "vertical-separator", &vertical_separator,
3912 "allow-rules", &allow_rules,
3913 "focus-line-width", &focus_line_width,
3914 "row-ending-details", &row_ending_details,
3917 if (tree_view->priv->row_count == 0)
3919 draw_empty_focus (tree_view);
3924 /* clip event->area to the visible area */
3925 if (Zarea.height < 0.5)
3929 validate_visible_area (tree_view);
3931 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, Zarea.y);
3935 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3937 gdk_window_get_width (tree_view->priv->bin_window);
3940 gdk_window_get_height (tree_view->priv->bin_window);
3943 if (tree_view->priv->height < bin_window_height)
3945 gtk_paint_flat_box (gtk_widget_get_style (widget),
3947 gtk_widget_get_state (widget),
3951 0, tree_view->priv->height,
3953 bin_window_height - tree_view->priv->height);
3959 /* find the path for the node */
3960 path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3961 gtk_tree_model_get_iter (tree_view->priv->model,
3964 gtk_tree_path_free (path);
3967 drag_dest_path = NULL;
3969 if (tree_view->priv->cursor)
3970 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3973 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3975 if (tree_view->priv->drag_dest_row)
3976 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3979 _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3983 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3984 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3986 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
3987 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3989 if (draw_vgrid_lines || draw_hgrid_lines)
3990 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
3992 n_visible_columns = 0;
3993 for (list = tree_view->priv->columns; list; list = list->next)
3995 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
3997 n_visible_columns ++;
4000 /* Find the last column */
4001 for (last_column = g_list_last (tree_view->priv->columns);
4002 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
4003 last_column = last_column->prev)
4007 for (first_column = g_list_first (tree_view->priv->columns);
4008 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
4009 first_column = first_column->next)
4012 /* Actually process the expose event. To do this, we want to
4013 * start at the first node of the event, and walk the tree in
4014 * order, drawing each successive node.
4021 gboolean is_first = FALSE;
4022 gboolean is_last = FALSE;
4023 gboolean done = FALSE;
4026 max_height = ROW_HEIGHT (tree_view);
4030 background_area.y = y_offset + Zarea.y;
4031 background_area.height = max_height;
4032 max_y = background_area.y + max_height;
4036 if (node == tree_view->priv->prelight_node)
4037 flags |= GTK_CELL_RENDERER_PRELIT;
4039 selected = pspp_sheet_view_node_is_selected (tree_view, node);
4043 if (tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
4045 /* we *need* to set cell data on all cells before the call
4046 * to _has_special_cell, else _has_special_cell() does not
4047 * return a correct value.
4049 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4051 list = (rtl ? list->prev : list->next))
4053 PsppSheetViewColumn *column = list->data;
4054 pspp_sheet_view_column_cell_set_cell_data (column,
4055 tree_view->priv->model,
4059 has_special_cell = pspp_sheet_view_has_special_cell (tree_view);
4062 has_special_cell = tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
4064 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4066 list = (rtl ? list->prev : list->next))
4068 PsppSheetViewColumn *column = list->data;
4069 const gchar *detail = NULL;
4070 gboolean selected_column;
4073 if (!column->visible)
4076 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
4077 selected_column = column->selected && column->selectable;
4079 selected_column = TRUE;
4082 if (cell_offset > Zarea.x + Zarea.width ||
4083 cell_offset + column->width < Zarea.x)
4085 cell_offset += column->width;
4090 if (selected && selected_column)
4091 flags |= GTK_CELL_RENDERER_SELECTED;
4093 flags &= ~GTK_CELL_RENDERER_SELECTED;
4095 if (column->show_sort_indicator)
4096 flags |= GTK_CELL_RENDERER_SORTED;
4098 flags &= ~GTK_CELL_RENDERER_SORTED;
4101 flags |= GTK_CELL_RENDERER_FOCUSED;
4103 flags &= ~GTK_CELL_RENDERER_FOCUSED;
4105 background_area.x = cell_offset;
4106 background_area.width = column->width;
4108 cell_area = background_area;
4109 cell_area.y += vertical_separator / 2;
4110 cell_area.x += horizontal_separator / 2;
4111 cell_area.height -= vertical_separator;
4112 cell_area.width -= horizontal_separator;
4114 if (draw_vgrid_lines)
4116 if (list == first_column)
4118 cell_area.width -= grid_line_width / 2;
4120 else if (list == last_column)
4122 cell_area.x += grid_line_width / 2;
4123 cell_area.width -= grid_line_width / 2;
4127 cell_area.x += grid_line_width / 2;
4128 cell_area.width -= grid_line_width;
4132 if (draw_hgrid_lines)
4134 cell_area.y += grid_line_width / 2;
4135 cell_area.height -= grid_line_width;
4139 if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4141 cell_offset += column->width;
4146 pspp_sheet_view_column_cell_set_cell_data (column,
4147 tree_view->priv->model,
4150 /* Select the detail for drawing the cell. relevant
4151 * factors are parity, sortedness, and whether to
4154 if (allow_rules && tree_view->priv->has_rules)
4156 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4157 n_visible_columns >= 3)
4160 detail = "cell_odd_ruled_sorted";
4162 detail = "cell_even_ruled_sorted";
4167 detail = "cell_odd_ruled";
4169 detail = "cell_even_ruled";
4174 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4175 n_visible_columns >= 3)
4178 detail = "cell_odd_sorted";
4180 detail = "cell_even_sorted";
4185 detail = "cell_odd";
4187 detail = "cell_even";
4193 if (gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE)
4194 state = GTK_STATE_INSENSITIVE;
4195 else if (flags & GTK_CELL_RENDERER_SELECTED)
4196 state = GTK_STATE_SELECTED;
4198 state = GTK_STATE_NORMAL;
4200 /* Draw background */
4201 if (row_ending_details)
4203 char new_detail[128];
4205 is_first = (rtl ? !list->next : !list->prev);
4206 is_last = (rtl ? !list->prev : !list->next);
4208 /* (I don't like the snprintfs either, but couldn't find a
4211 if (is_first && is_last)
4212 g_snprintf (new_detail, 127, "%s", detail);
4214 g_snprintf (new_detail, 127, "%s_start", detail);
4216 g_snprintf (new_detail, 127, "%s_end", detail);
4218 g_snprintf (new_detail, 128, "%s_middle", detail);
4220 gtk_paint_flat_box (gtk_widget_get_style (widget),
4228 background_area.width,
4229 background_area.height);
4233 gtk_paint_flat_box (gtk_widget_get_style (widget),
4241 background_area.width,
4242 background_area.height);
4245 if (draw_hgrid_lines)
4247 cairo_set_line_width (cr, 1.0);
4248 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
4250 if (background_area.y >= 0)
4253 gdk_draw_line (event->window,
4254 tree_view->priv->grid_line_gc[widget->state],
4255 background_area.x, background_area.y,
4256 background_area.x + background_area.width,
4259 cairo_move_to (cr, background_area.x, background_area.y - 0.5);
4260 cairo_line_to (cr, background_area.x + background_area.width,
4261 background_area.y - 0.5);
4265 if (y_offset + max_height <= Zarea.height - 0.5)
4268 gdk_draw_line (event->window,
4269 tree_view->priv->grid_line_gc[widget->state],
4270 background_area.x, background_area.y + max_height,
4271 background_area.x + background_area.width,
4272 background_area.y + max_height);
4275 cairo_move_to (cr, background_area.x, background_area.y + max_height - 0.5);
4276 cairo_line_to (cr, background_area.x + background_area.width,
4277 background_area.y + max_height - 0.5);
4283 _pspp_sheet_view_column_cell_render (column,
4289 if (node == cursor && has_special_cell &&
4290 ((column == tree_view->priv->focus_column &&
4291 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4292 gtk_widget_has_focus (widget)) ||
4293 (column == tree_view->priv->edited_column)))
4295 _pspp_sheet_view_column_cell_draw_focus (column,
4302 cell_offset += column->width;
4305 if (cell_offset < Zarea.x)
4307 gtk_paint_flat_box (gtk_widget_get_style (widget),
4315 Zarea.x - cell_offset,
4316 background_area.height);
4319 if (node == drag_highlight)
4321 /* Draw indicator for the drop
4323 gint highlight_y = -1;
4327 switch (tree_view->priv->drag_dest_pos)
4329 case PSPP_SHEET_VIEW_DROP_BEFORE:
4330 highlight_y = background_area.y - 1;
4331 if (highlight_y < 0)
4335 case PSPP_SHEET_VIEW_DROP_AFTER:
4336 highlight_y = background_area.y + background_area.height - 1;
4339 case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE:
4340 case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER:
4341 _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node);
4345 width = gdk_window_get_width (tree_view->priv->bin_window);
4347 if (row_ending_details)
4348 gtk_paint_focus (gtk_widget_get_style (widget),
4350 gtk_widget_get_state (widget),
4353 ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4354 : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4355 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4356 - focus_line_width / 2,
4357 width, ROW_HEIGHT (tree_view)
4358 - focus_line_width + 1);
4360 gtk_paint_focus (gtk_widget_get_style (widget),
4362 gtk_widget_get_state (widget),
4364 "treeview-drop-indicator",
4365 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4366 - focus_line_width / 2,
4367 width, ROW_HEIGHT (tree_view)
4368 - focus_line_width + 1);
4373 if (highlight_y >= 0)
4375 gdk_draw_line (event->window,
4376 widget->style->fg_gc[gtk_widget_get_state (widget)],
4379 rtl ? 0 : bin_window_width,
4385 /* draw the big row-spanning focus rectangle, if needed */
4386 if (!has_special_cell && node == cursor &&
4387 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4388 gtk_widget_has_focus (widget))
4390 gint tmp_y, tmp_height;
4392 GtkStateType focus_rect_state;
4395 flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
4396 (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
4397 (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE :
4400 width = gdk_window_get_width (tree_view->priv->bin_window);
4402 if (draw_hgrid_lines)
4404 tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node) + grid_line_width / 2;
4405 tmp_height = ROW_HEIGHT (tree_view) - grid_line_width;
4409 tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node);
4410 tmp_height = ROW_HEIGHT (tree_view);
4413 if (row_ending_details)
4414 gtk_paint_focus (gtk_widget_get_style (widget),
4419 ? (is_last ? "treeview" : "treeview-left" )
4420 : (is_last ? "treeview-right" : "treeview-middle" )),
4424 gtk_paint_focus (gtk_widget_get_style (widget),
4433 y_offset += max_height;
4437 node = pspp_sheet_view_node_next (tree_view, node);
4440 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
4444 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
4451 while (y_offset < Zarea.height);
4454 pspp_sheet_view_draw_vertical_grid_lines (tree_view, cr, n_visible_columns,
4458 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4460 GdkRectangle *rectangles;
4463 gdk_region_get_rectangles (event->region,
4467 while (n_rectangles--)
4468 pspp_sheet_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
4470 g_free (rectangles);
4475 gtk_tree_path_free (cursor_path);
4478 gtk_tree_path_free (drag_dest_path);
4485 pspp_sheet_view_draw (GtkWidget *widget,
4488 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4490 if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window))
4496 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->bin_window);
4497 retval = pspp_sheet_view_bin_expose (widget, cr);
4500 /* We can't just chain up to Container::expose as it will try to send the
4501 * event to the headers, so we handle propagating it to our children
4502 * (eg. widgets being edited) ourselves.
4504 tmp_list = tree_view->priv->children;
4507 PsppSheetViewChild *child = tmp_list->data;
4508 tmp_list = tmp_list->next;
4510 gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
4515 else if (gtk_cairo_should_draw_window (cr, tree_view->priv->header_window))
4517 gint n_visible_columns;
4520 for (list = tree_view->priv->columns; list != NULL; list = list->next)
4522 PsppSheetViewColumn *column = list->data;
4524 if (column == tree_view->priv->drag_column || !column->visible)
4527 if (span_intersects (column->allocation.x, column->allocation.width,
4528 (int) gtk_adjustment_get_value (tree_view->priv->hadjustment),
4529 (int) gtk_widget_get_allocated_width (widget))
4530 && column->button != NULL)
4531 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4532 column->button, cr);
4535 n_visible_columns = 0;
4536 for (list = tree_view->priv->columns; list; list = list->next)
4538 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4540 n_visible_columns ++;
4543 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->header_window);
4544 pspp_sheet_view_draw_vertical_grid_lines (tree_view,
4548 TREE_VIEW_HEADER_HEIGHT (tree_view));
4553 else if (gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window))
4555 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4556 tree_view->priv->drag_column->button,
4573 /* returns 0x1 when no column has been found -- yes it's hackish */
4574 static PsppSheetViewColumn *
4575 pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
4576 PsppSheetViewColumn *column,
4579 PsppSheetViewColumn *left_column = NULL;
4580 PsppSheetViewColumn *cur_column = NULL;
4583 if (!column->reorderable)
4584 return (PsppSheetViewColumn *)0x1;
4586 switch (drop_position)
4589 /* find first column where we can drop */
4590 tmp_list = tree_view->priv->columns;
4591 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4592 return (PsppSheetViewColumn *)0x1;
4596 g_assert (tmp_list);
4598 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4599 tmp_list = tmp_list->next;
4601 if (left_column && left_column->visible == FALSE)
4604 if (!tree_view->priv->column_drop_func)
4607 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4609 left_column = cur_column;
4616 if (!tree_view->priv->column_drop_func)
4619 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4622 return (PsppSheetViewColumn *)0x1;
4626 /* find first column after column where we can drop */
4627 tmp_list = tree_view->priv->columns;
4629 for (; tmp_list; tmp_list = tmp_list->next)
4630 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4633 if (!tmp_list || !tmp_list->next)
4634 return (PsppSheetViewColumn *)0x1;
4636 tmp_list = tmp_list->next;
4637 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4638 tmp_list = tmp_list->next;
4642 g_assert (tmp_list);
4644 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4645 tmp_list = tmp_list->next;
4647 if (left_column && left_column->visible == FALSE)
4649 left_column = cur_column;
4651 tmp_list = tmp_list->next;
4655 if (!tree_view->priv->column_drop_func)
4658 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4660 left_column = cur_column;
4667 if (!tree_view->priv->column_drop_func)
4670 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4673 return (PsppSheetViewColumn *)0x1;
4677 /* find first column before column where we can drop */
4678 tmp_list = tree_view->priv->columns;
4680 for (; tmp_list; tmp_list = tmp_list->next)
4681 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4684 if (!tmp_list || !tmp_list->prev)
4685 return (PsppSheetViewColumn *)0x1;
4687 tmp_list = tmp_list->prev;
4688 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4689 tmp_list = tmp_list->prev;
4693 g_assert (tmp_list);
4695 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4697 if (left_column && !left_column->visible)
4699 /*if (!tmp_list->prev)
4700 return (PsppSheetViewColumn *)0x1;
4703 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4704 tmp_list = tmp_list->prev->prev;
4707 cur_column = left_column;
4709 tmp_list = tmp_list->prev;
4713 if (!tree_view->priv->column_drop_func)
4716 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4719 cur_column = left_column;
4720 tmp_list = tmp_list->prev;
4723 if (!tree_view->priv->column_drop_func)
4726 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4729 return (PsppSheetViewColumn *)0x1;
4733 /* same as DROP_HOME case, but doing it backwards */
4734 tmp_list = g_list_last (tree_view->priv->columns);
4737 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4738 return (PsppSheetViewColumn *)0x1;
4742 g_assert (tmp_list);
4744 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4746 if (left_column && !left_column->visible)
4748 cur_column = left_column;
4749 tmp_list = tmp_list->prev;
4752 if (!tree_view->priv->column_drop_func)
4755 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4758 cur_column = left_column;
4759 tmp_list = tmp_list->prev;
4762 if (!tree_view->priv->column_drop_func)
4765 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4768 return (PsppSheetViewColumn *)0x1;
4772 return (PsppSheetViewColumn *)0x1;
4776 pspp_sheet_view_key_press (GtkWidget *widget,
4779 PsppSheetView *tree_view = (PsppSheetView *) widget;
4781 if (tree_view->priv->rubber_band_status)
4783 if (event->keyval == GDK_Escape)
4784 pspp_sheet_view_stop_rubber_band (tree_view);
4789 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4791 if (event->keyval == GDK_Escape)
4793 tree_view->priv->cur_reorder = NULL;
4794 pspp_sheet_view_button_release_drag_column (widget, NULL);
4799 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4801 GList *focus_column;
4804 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4806 for (focus_column = tree_view->priv->columns;
4808 focus_column = focus_column->next)
4810 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4812 if (column->button && gtk_widget_has_focus (column->button))
4817 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4818 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4819 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4821 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4823 if (!column->resizable)
4825 gtk_widget_error_bell (widget);
4829 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4830 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4832 gint old_width = column->resized_width;
4834 column->resized_width = MAX (column->resized_width,
4836 column->resized_width -= 2;
4837 if (column->resized_width < 0)
4838 column->resized_width = 0;
4840 if (column->min_width == -1)
4841 column->resized_width = MAX (column->button_request,
4842 column->resized_width);
4844 column->resized_width = MAX (column->min_width,
4845 column->resized_width);
4847 if (column->max_width != -1)
4848 column->resized_width = MIN (column->resized_width,
4851 column->use_resized_width = TRUE;
4853 if (column->resized_width != old_width)
4854 gtk_widget_queue_resize (widget);
4856 gtk_widget_error_bell (widget);
4858 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4859 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4861 gint old_width = column->resized_width;
4863 column->resized_width = MAX (column->resized_width,
4865 column->resized_width += 2;
4867 if (column->max_width != -1)
4868 column->resized_width = MIN (column->resized_width,
4871 column->use_resized_width = TRUE;
4873 if (column->resized_width != old_width)
4874 gtk_widget_queue_resize (widget);
4876 gtk_widget_error_bell (widget);
4883 (event->state & GDK_MOD1_MASK) &&
4884 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4885 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4886 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4887 || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4889 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4891 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4892 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4894 PsppSheetViewColumn *col;
4895 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
4896 if (col != (PsppSheetViewColumn *)0x1)
4897 pspp_sheet_view_move_column_after (tree_view, column, col);
4899 gtk_widget_error_bell (widget);
4901 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4902 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4904 PsppSheetViewColumn *col;
4905 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4906 if (col != (PsppSheetViewColumn *)0x1)
4907 pspp_sheet_view_move_column_after (tree_view, column, col);
4909 gtk_widget_error_bell (widget);
4911 else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4913 PsppSheetViewColumn *col;
4914 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4915 if (col != (PsppSheetViewColumn *)0x1)
4916 pspp_sheet_view_move_column_after (tree_view, column, col);
4918 gtk_widget_error_bell (widget);
4920 else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4922 PsppSheetViewColumn *col;
4923 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4924 if (col != (PsppSheetViewColumn *)0x1)
4925 pspp_sheet_view_move_column_after (tree_view, column, col);
4927 gtk_widget_error_bell (widget);
4934 /* Chain up to the parent class. It handles the keybindings. */
4935 if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4938 if (tree_view->priv->search_entry_avoid_unhandled_binding)
4940 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4944 /* We pass the event to the search_entry. If its text changes, then we start
4945 * the typeahead find capabilities. */
4946 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4947 && tree_view->priv->enable_search
4948 && !tree_view->priv->search_custom_entry_set)
4950 GdkEvent *new_event;
4952 const char *new_text;
4955 gboolean text_modified;
4956 gulong popup_menu_id;
4958 pspp_sheet_view_ensure_interactive_directory (tree_view);
4960 /* Make a copy of the current text */
4961 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4962 new_event = gdk_event_copy ((GdkEvent *) event);
4963 g_object_unref (((GdkEventKey *) new_event)->window);
4964 ((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window));
4965 gtk_widget_realize (tree_view->priv->search_window);
4967 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
4968 "popup-menu", G_CALLBACK (gtk_true),
4971 /* Move the entry off screen */
4972 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4973 gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4974 gdk_screen_get_width (screen) + 1,
4975 gdk_screen_get_height (screen) + 1);
4976 gtk_widget_show (tree_view->priv->search_window);
4978 /* Send the event to the window. If the preedit_changed signal is emitted
4979 * during this event, we will set priv->imcontext_changed */
4980 tree_view->priv->imcontext_changed = FALSE;
4981 retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4982 gdk_event_free (new_event);
4983 gtk_widget_hide (tree_view->priv->search_window);
4985 g_signal_handler_disconnect (tree_view->priv->search_entry,
4988 /* We check to make sure that the entry tried to handle the text, and that
4989 * the text has changed.
4991 new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
4992 text_modified = strcmp (old_text, new_text) != 0;
4994 if (tree_view->priv->imcontext_changed || /* we're in a preedit */
4995 (retval && text_modified)) /* ...or the text was modified */
4997 if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
4999 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
5004 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
5014 pspp_sheet_view_key_release (GtkWidget *widget,
5017 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
5019 if (tree_view->priv->rubber_band_status)
5022 return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
5025 /* FIXME Is this function necessary? Can I get an enter_notify event
5026 * w/o either an expose event or a mouse motion event?
5029 pspp_sheet_view_enter_notify (GtkWidget *widget,
5030 GdkEventCrossing *event)
5032 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
5036 /* Sanity check it */
5037 if (event->window != tree_view->priv->bin_window)
5040 if (tree_view->priv->row_count == 0)
5043 if (event->mode == GDK_CROSSING_GRAB ||
5044 event->mode == GDK_CROSSING_GTK_GRAB ||
5045 event->mode == GDK_CROSSING_GTK_UNGRAB ||
5046 event->mode == GDK_CROSSING_STATE_CHANGED)
5049 /* find the node internally */
5050 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
5053 pspp_sheet_view_find_offset (tree_view, new_y, &node);
5055 tree_view->priv->event_last_x = event->x;
5056 tree_view->priv->event_last_y = event->y;
5058 prelight_or_select (tree_view, node, event->x, event->y);
5064 pspp_sheet_view_leave_notify (GtkWidget *widget,
5065 GdkEventCrossing *event)
5067 PsppSheetView *tree_view;
5069 if (event->mode == GDK_CROSSING_GRAB)
5072 tree_view = PSPP_SHEET_VIEW (widget);
5074 if (tree_view->priv->prelight_node >= 0)
5075 _pspp_sheet_view_queue_draw_node (tree_view,
5076 tree_view->priv->prelight_node,
5079 tree_view->priv->event_last_x = -10000;
5080 tree_view->priv->event_last_y = -10000;
5082 prelight_or_select (tree_view,
5084 -1000, -1000); /* coords not possibly over an arrow */
5091 pspp_sheet_view_focus_out (GtkWidget *widget,
5092 GdkEventFocus *event)
5094 PsppSheetView *tree_view;
5096 tree_view = PSPP_SHEET_VIEW (widget);
5098 gtk_widget_queue_draw (widget);
5100 /* destroy interactive search dialog */
5101 if (tree_view->priv->search_window)
5102 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
5108 /* Incremental Reflow
5112 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
5115 GtkAllocation allocation;
5116 gint y = pspp_sheet_view_node_find_offset (tree_view, node)
5117 - gtk_adjustment_get_value (tree_view->priv->vadjustment)
5118 + TREE_VIEW_HEADER_HEIGHT (tree_view);
5120 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5122 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
5125 tree_view->priv->fixed_height);
5129 node_is_visible (PsppSheetView *tree_view,
5135 y = pspp_sheet_view_node_find_offset (tree_view, node);
5136 height = ROW_HEIGHT (tree_view);
5138 if (y >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5139 y + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5140 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5146 /* Returns the row height. */
5148 validate_row (PsppSheetView *tree_view,
5153 PsppSheetViewColumn *column;
5154 GList *list, *first_column, *last_column;
5156 gint horizontal_separator;
5157 gint vertical_separator;
5158 gint focus_line_width;
5159 gboolean draw_vgrid_lines, draw_hgrid_lines;
5161 gint grid_line_width;
5162 gboolean wide_separators;
5163 gint separator_height;
5165 gtk_widget_style_get (GTK_WIDGET (tree_view),
5166 "focus-padding", &focus_pad,
5167 "focus-line-width", &focus_line_width,
5168 "horizontal-separator", &horizontal_separator,
5169 "vertical-separator", &vertical_separator,
5170 "grid-line-width", &grid_line_width,
5171 "wide-separators", &wide_separators,
5172 "separator-height", &separator_height,
5176 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5177 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5179 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5180 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5182 for (last_column = g_list_last (tree_view->priv->columns);
5183 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5184 last_column = last_column->prev)
5187 for (first_column = g_list_first (tree_view->priv->columns);
5188 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5189 first_column = first_column->next)
5192 for (list = tree_view->priv->columns; list; list = list->next)
5197 column = list->data;
5199 if (! column->visible)
5202 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5203 pspp_sheet_view_column_cell_get_size (column,
5205 &tmp_width, &tmp_height);
5207 tmp_height += vertical_separator;
5208 height = MAX (height, tmp_height);
5210 tmp_width = tmp_width + horizontal_separator;
5212 if (draw_vgrid_lines)
5214 if (list->data == first_column || list->data == last_column)
5215 tmp_width += grid_line_width / 2.0;
5217 tmp_width += grid_line_width;
5220 if (tmp_width > column->requested_width)
5221 column->requested_width = tmp_width;
5224 if (draw_hgrid_lines)
5225 height += grid_line_width;
5227 tree_view->priv->post_validation_flag = TRUE;
5233 validate_visible_area (PsppSheetView *tree_view)
5235 GtkTreePath *path = NULL;
5236 GtkTreePath *above_path = NULL;
5239 gboolean size_changed = FALSE;
5241 gint area_above = 0;
5242 gint area_below = 0;
5243 GtkAllocation allocation;
5245 if (tree_view->priv->row_count == 0)
5248 if (tree_view->priv->scroll_to_path == NULL)
5251 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5253 total_height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5255 if (total_height == 0)
5258 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5261 /* we are going to scroll, and will update dy */
5262 _pspp_sheet_view_find_node (tree_view, path, &node);
5263 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5265 if (tree_view->priv->scroll_to_use_align)
5267 gint height = ROW_HEIGHT (tree_view);
5268 area_above = (total_height - height) *
5269 tree_view->priv->scroll_to_row_align;
5270 area_below = total_height - area_above - height;
5271 area_above = MAX (area_above, 0);
5272 area_below = MAX (area_below, 0);
5277 * 1) row not visible
5281 gint height = ROW_HEIGHT (tree_view);
5283 dy = pspp_sheet_view_node_find_offset (tree_view, node);
5285 if (dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5286 dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5287 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5289 /* row visible: keep the row at the same position */
5290 area_above = dy - gtk_adjustment_get_value (tree_view->priv->vadjustment);
5291 area_below = (gtk_adjustment_get_value (tree_view->priv->vadjustment) +
5292 gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5297 /* row not visible */
5299 && dy + height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5301 /* row at the beginning -- fixed */
5303 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment)
5304 - area_above - height;
5306 else if (dy >= (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5307 gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5309 /* row at the end -- fixed */
5310 area_above = dy - (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5311 gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
5312 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) -
5313 area_above - height;
5317 area_above = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - height;
5323 /* row somewhere in the middle, bring it to the top
5327 area_below = total_height - height;
5333 /* the scroll to isn't valid; ignore it.
5336 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5337 tree_view->priv->scroll_to_path = NULL;
5341 above_path = gtk_tree_path_copy (path);
5343 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5344 * backwards is much slower then forward, as there is no iter_prev function.
5345 * We go forwards first in case we run out of tree. Then we go backwards to
5348 while (node >= 0 && area_below > 0)
5350 gboolean done = FALSE;
5353 node = pspp_sheet_view_node_next (tree_view, node);
5356 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5358 gtk_tree_path_next (path);
5361 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5371 area_below -= ROW_HEIGHT (tree_view);
5373 gtk_tree_path_free (path);
5375 /* If we ran out of tree, and have extra area_below left, we need to add it
5378 area_above += area_below;
5380 _pspp_sheet_view_find_node (tree_view, above_path, &node);
5382 /* We walk backwards */
5383 while (area_above > 0)
5385 node = pspp_sheet_view_node_prev (tree_view, node);
5387 /* Always find the new path in the tree. We cannot just assume
5388 * a gtk_tree_path_prev() is enough here, as there might be children
5389 * in between this node and the previous sibling node. If this
5390 * appears to be a performance hotspot in profiles, we can look into
5391 * intrigate logic for keeping path, node and iter in sync like
5392 * we do for forward walks. (Which will be hard because of the lacking
5399 gtk_tree_path_free (above_path);
5400 above_path = _pspp_sheet_view_find_path (tree_view, node);
5402 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5404 area_above -= ROW_HEIGHT (tree_view);
5407 /* set the dy here to scroll to the path,
5408 * and sync the top row accordingly
5410 pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5411 pspp_sheet_view_top_row_to_dy (tree_view);
5413 /* update width/height and queue a resize */
5416 GtkRequisition requisition;
5418 /* We temporarily guess a size, under the assumption that it will be the
5419 * same when we get our next size_allocate. If we don't do this, we'll be
5420 * in an inconsistent state if we call top_row_to_dy. */
5422 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5423 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5424 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5425 gtk_adjustment_changed (tree_view->priv->hadjustment);
5426 gtk_adjustment_changed (tree_view->priv->vadjustment);
5427 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5430 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5431 tree_view->priv->scroll_to_path = NULL;
5434 gtk_tree_path_free (above_path);
5436 if (tree_view->priv->scroll_to_column)
5438 tree_view->priv->scroll_to_column = NULL;
5440 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5444 initialize_fixed_height_mode (PsppSheetView *tree_view)
5446 if (!tree_view->priv->row_count)
5449 if (tree_view->priv->fixed_height_set)
5452 if (tree_view->priv->fixed_height < 0)
5459 path = _pspp_sheet_view_find_path (tree_view, node);
5460 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5462 tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5464 gtk_tree_path_free (path);
5466 g_object_notify (G_OBJECT (tree_view), "fixed-height");
5470 /* Our strategy for finding nodes to validate is a little convoluted. We find
5471 * the left-most uninvalidated node. We then try walking right, validating
5472 * nodes. Once we find a valid node, we repeat the previous process of finding
5473 * the first invalid node.
5477 validate_rows_handler (PsppSheetView *tree_view)
5479 initialize_fixed_height_mode (tree_view);
5480 if (tree_view->priv->validate_rows_timer)
5482 g_source_remove (tree_view->priv->validate_rows_timer);
5483 tree_view->priv->validate_rows_timer = 0;
5490 do_presize_handler (PsppSheetView *tree_view)
5492 GtkRequisition requisition;
5494 validate_visible_area (tree_view);
5495 tree_view->priv->presize_handler_timer = 0;
5497 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5500 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5502 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5503 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5504 gtk_adjustment_changed (tree_view->priv->hadjustment);
5505 gtk_adjustment_changed (tree_view->priv->vadjustment);
5506 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5512 presize_handler_callback (gpointer data)
5514 do_presize_handler (PSPP_SHEET_VIEW (data));
5520 install_presize_handler (PsppSheetView *tree_view)
5522 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5525 if (! tree_view->priv->presize_handler_timer)
5527 tree_view->priv->presize_handler_timer =
5528 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5530 if (! tree_view->priv->validate_rows_timer)
5532 tree_view->priv->validate_rows_timer =
5533 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5538 scroll_sync_handler (PsppSheetView *tree_view)
5540 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5541 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5542 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5543 pspp_sheet_view_top_row_to_dy (tree_view);
5545 pspp_sheet_view_dy_to_top_row (tree_view);
5547 tree_view->priv->scroll_sync_timer = 0;
5553 install_scroll_sync_handler (PsppSheetView *tree_view)
5555 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5558 if (!tree_view->priv->scroll_sync_timer)
5560 tree_view->priv->scroll_sync_timer =
5561 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5566 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5570 gtk_tree_row_reference_free (tree_view->priv->top_row);
5574 tree_view->priv->top_row = NULL;
5575 tree_view->priv->top_row_dy = 0;
5579 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5580 tree_view->priv->top_row_dy = offset;
5584 /* Always call this iff dy is in the visible range. If the tree is empty, then
5585 * it's set to be NULL, and top_row_dy is 0;
5588 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5594 if (tree_view->priv->row_count == 0)
5596 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5600 offset = pspp_sheet_view_find_offset (tree_view,
5601 tree_view->priv->dy,
5606 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5610 path = _pspp_sheet_view_find_path (tree_view, node);
5611 pspp_sheet_view_set_top_row (tree_view, path, offset);
5612 gtk_tree_path_free (path);
5618 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5624 /* Avoid recursive calls */
5625 if (tree_view->priv->in_top_row_to_dy)
5628 if (tree_view->priv->top_row)
5629 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5636 _pspp_sheet_view_find_node (tree_view, path, &node);
5639 gtk_tree_path_free (path);
5643 /* keep dy and set new toprow */
5644 gtk_tree_row_reference_free (tree_view->priv->top_row);
5645 tree_view->priv->top_row = NULL;
5646 tree_view->priv->top_row_dy = 0;
5647 /* DO NOT install the idle handler */
5648 pspp_sheet_view_dy_to_top_row (tree_view);
5652 if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5654 /* new top row -- do NOT install the idle handler */
5655 pspp_sheet_view_dy_to_top_row (tree_view);
5659 new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5660 new_dy += tree_view->priv->top_row_dy;
5662 if (new_dy + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
5663 new_dy = tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
5665 new_dy = MAX (0, new_dy);
5667 tree_view->priv->in_top_row_to_dy = TRUE;
5668 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5669 tree_view->priv->in_top_row_to_dy = FALSE;
5674 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5676 install_presize_handler (tree_view);
5682 set_source_row (GdkDragContext *context,
5683 GtkTreeModel *model,
5684 GtkTreePath *source_row)
5686 g_object_set_data_full (G_OBJECT (context),
5687 "gtk-tree-view-source-row",
5688 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5689 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5693 get_source_row (GdkDragContext *context)
5695 GtkTreeRowReference *ref =
5696 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5699 return gtk_tree_row_reference_get_path (ref);
5706 GtkTreeRowReference *dest_row;
5707 guint path_down_mode : 1;
5708 guint empty_view_drop : 1;
5709 guint drop_append_mode : 1;
5714 dest_row_free (gpointer data)
5716 DestRow *dr = (DestRow *)data;
5718 gtk_tree_row_reference_free (dr->dest_row);
5719 g_slice_free (DestRow, dr);
5723 set_dest_row (GdkDragContext *context,
5724 GtkTreeModel *model,
5725 GtkTreePath *dest_row,
5726 gboolean path_down_mode,
5727 gboolean empty_view_drop,
5728 gboolean drop_append_mode)
5734 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5739 dr = g_slice_new (DestRow);
5741 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5742 dr->path_down_mode = path_down_mode != FALSE;
5743 dr->empty_view_drop = empty_view_drop != FALSE;
5744 dr->drop_append_mode = drop_append_mode != FALSE;
5746 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5747 dr, (GDestroyNotify) dest_row_free);
5751 get_dest_row (GdkDragContext *context,
5752 gboolean *path_down_mode)
5755 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5759 GtkTreePath *path = NULL;
5762 *path_down_mode = dr->path_down_mode;
5765 path = gtk_tree_row_reference_get_path (dr->dest_row);
5766 else if (dr->empty_view_drop)
5767 path = gtk_tree_path_new_from_indices (0, -1);
5771 if (path && dr->drop_append_mode)
5772 gtk_tree_path_next (path);
5780 /* Get/set whether drag_motion requested the drag data and
5781 * drag_data_received should thus not actually insert the data,
5782 * since the data doesn't result from a drop.
5785 set_status_pending (GdkDragContext *context,
5786 GdkDragAction suggested_action)
5788 g_object_set_data (G_OBJECT (context),
5789 "gtk-tree-view-status-pending",
5790 GINT_TO_POINTER (suggested_action));
5793 static GdkDragAction
5794 get_status_pending (GdkDragContext *context)
5796 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5797 "gtk-tree-view-status-pending"));
5800 static TreeViewDragInfo*
5801 get_info (PsppSheetView *tree_view)
5803 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5807 destroy_info (TreeViewDragInfo *di)
5809 g_slice_free (TreeViewDragInfo, di);
5812 static TreeViewDragInfo*
5813 ensure_info (PsppSheetView *tree_view)
5815 TreeViewDragInfo *di;
5817 di = get_info (tree_view);
5821 di = g_slice_new0 (TreeViewDragInfo);
5823 g_object_set_data_full (G_OBJECT (tree_view),
5824 "gtk-tree-view-drag-info",
5826 (GDestroyNotify) destroy_info);
5833 remove_info (PsppSheetView *tree_view)
5835 g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5840 drag_scan_timeout (gpointer data)
5842 PsppSheetView *tree_view;
5844 GdkModifierType state;
5845 GtkTreePath *path = NULL;
5846 PsppSheetViewColumn *column = NULL;
5847 GdkRectangle visible_rect;
5849 GDK_THREADS_ENTER ();
5851 tree_view = PSPP_SHEET_VIEW (data);
5853 gdk_window_get_pointer (tree_view->priv->bin_window,
5856 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5858 /* See if we are near the edge. */
5859 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5860 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5861 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5862 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5864 pspp_sheet_view_get_path_at_pos (tree_view,
5865 tree_view->priv->bin_window,
5874 pspp_sheet_view_scroll_to_cell (tree_view,
5880 gtk_tree_path_free (path);
5884 GDK_THREADS_LEAVE ();
5891 add_scroll_timeout (PsppSheetView *tree_view)
5893 if (tree_view->priv->scroll_timeout == 0)
5895 tree_view->priv->scroll_timeout =
5896 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5901 remove_scroll_timeout (PsppSheetView *tree_view)
5903 if (tree_view->priv->scroll_timeout != 0)
5905 g_source_remove (tree_view->priv->scroll_timeout);
5906 tree_view->priv->scroll_timeout = 0;
5911 check_model_dnd (GtkTreeModel *model,
5912 GType required_iface,
5913 const gchar *signal)
5915 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5917 g_warning ("You must override the default '%s' handler "
5918 "on PsppSheetView when using models that don't support "
5919 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5920 "is to connect to '%s' and call "
5921 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5922 "the default handler from running. Look at the source code "
5923 "for the default handler in gtktreeview.c to get an idea what "
5924 "your handler should do. (gtktreeview.c is in the GTK source "
5925 "code.) If you're using GTK from a language other than C, "
5926 "there may be a more natural way to override default handlers, e.g. via derivation.",
5927 signal, g_type_name (required_iface), signal);
5935 scroll_row_timeout (gpointer data)
5937 PsppSheetView *tree_view = data;
5939 pspp_sheet_view_horizontal_autoscroll (tree_view);
5940 pspp_sheet_view_vertical_autoscroll (tree_view);
5942 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5943 pspp_sheet_view_update_rubber_band (tree_view);
5948 /* Returns TRUE if event should not be propagated to parent widgets */
5950 set_destination_row (PsppSheetView *tree_view,
5951 GdkDragContext *context,
5952 /* coordinates relative to the widget */
5955 GdkDragAction *suggested_action,
5958 GtkTreePath *path = NULL;
5959 PsppSheetViewDropPosition pos;
5960 PsppSheetViewDropPosition old_pos;
5961 TreeViewDragInfo *di;
5963 GtkTreePath *old_dest_path = NULL;
5964 gboolean can_drop = FALSE;
5966 *suggested_action = 0;
5969 widget = GTK_WIDGET (tree_view);
5971 di = get_info (tree_view);
5973 if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5975 /* someone unset us as a drag dest, note that if
5976 * we return FALSE drag_leave isn't called
5979 pspp_sheet_view_set_drag_dest_row (tree_view,
5981 PSPP_SHEET_VIEW_DROP_BEFORE);
5983 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5985 return FALSE; /* no longer a drop site */
5988 *target = gtk_drag_dest_find_target (widget, context,
5989 gtk_drag_dest_get_target_list (widget));
5990 if (*target == GDK_NONE)
5995 if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
6001 GtkTreeModel *model;
6003 /* the row got dropped on empty space, let's setup a special case
6007 gtk_tree_path_free (path);
6009 model = pspp_sheet_view_get_model (tree_view);
6011 n_children = gtk_tree_model_iter_n_children (model, NULL);
6014 pos = PSPP_SHEET_VIEW_DROP_AFTER;
6015 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
6019 pos = PSPP_SHEET_VIEW_DROP_BEFORE;
6020 path = gtk_tree_path_new_from_indices (0, -1);
6030 /* If we left the current row's "open" zone, unset the timeout for
6033 pspp_sheet_view_get_drag_dest_row (tree_view,
6038 gtk_tree_path_free (old_dest_path);
6040 if (TRUE /* FIXME if the location droppable predicate */)
6048 GtkWidget *source_widget;
6050 *suggested_action = gdk_drag_context_get_suggested_action (context);
6051 source_widget = gtk_drag_get_source_widget (context);
6053 if (source_widget == widget)
6055 /* Default to MOVE, unless the user has
6056 * pressed ctrl or shift to affect available actions
6058 if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
6059 *suggested_action = GDK_ACTION_MOVE;
6062 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6067 /* can't drop here */
6068 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6070 PSPP_SHEET_VIEW_DROP_BEFORE);
6074 gtk_tree_path_free (path);
6080 get_logical_dest_row (PsppSheetView *tree_view,
6081 gboolean *path_down_mode,
6082 gboolean *drop_append_mode)
6084 /* adjust path to point to the row the drop goes in front of */
6085 GtkTreePath *path = NULL;
6086 PsppSheetViewDropPosition pos;
6088 g_return_val_if_fail (path_down_mode != NULL, NULL);
6089 g_return_val_if_fail (drop_append_mode != NULL, NULL);
6091 *path_down_mode = FALSE;
6092 *drop_append_mode = 0;
6094 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6099 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
6101 else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
6102 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
6103 *path_down_mode = TRUE;
6107 GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
6109 g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
6111 if (!gtk_tree_model_get_iter (model, &iter, path) ||
6112 !gtk_tree_model_iter_next (model, &iter))
6113 *drop_append_mode = 1;
6116 *drop_append_mode = 0;
6117 gtk_tree_path_next (path);
6125 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
6126 GdkEventMotion *event)
6128 GtkWidget *widget = GTK_WIDGET (tree_view);
6129 GdkDragContext *context;
6130 TreeViewDragInfo *di;
6131 GtkTreePath *path = NULL;
6133 gint cell_x, cell_y;
6134 GtkTreeModel *model;
6135 gboolean retval = FALSE;
6137 di = get_info (tree_view);
6139 if (di == NULL || !di->source_set)
6142 if (tree_view->priv->pressed_button < 0)
6145 if (!gtk_drag_check_threshold (widget,
6146 tree_view->priv->press_start_x,
6147 tree_view->priv->press_start_y,
6148 event->x, event->y))
6151 model = pspp_sheet_view_get_model (tree_view);
6156 button = tree_view->priv->pressed_button;
6157 tree_view->priv->pressed_button = -1;
6159 pspp_sheet_view_get_path_at_pos (tree_view,
6160 tree_view->priv->press_start_x,
6161 tree_view->priv->press_start_y,
6170 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6171 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6175 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6178 /* Now we can begin the drag */
6182 context = gtk_drag_begin (widget,
6183 gtk_drag_source_get_target_list (widget),
6188 set_source_row (context, model, path);
6192 gtk_tree_path_free (path);
6200 pspp_sheet_view_drag_begin (GtkWidget *widget,
6201 GdkDragContext *context)
6204 PsppSheetView *tree_view;
6205 GtkTreePath *path = NULL;
6206 gint cell_x, cell_y;
6208 TreeViewDragInfo *di;
6210 tree_view = PSPP_SHEET_VIEW (widget);
6212 /* if the user uses a custom DND source impl, we don't set the icon here */
6213 di = get_info (tree_view);
6215 if (di == NULL || !di->source_set)
6218 pspp_sheet_view_get_path_at_pos (tree_view,
6219 tree_view->priv->press_start_x,
6220 tree_view->priv->press_start_y,
6226 g_return_if_fail (path != NULL);
6228 row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6231 gtk_drag_set_icon_pixmap (context,
6232 gdk_drawable_get_colormap (row_pix),
6235 /* the + 1 is for the black border in the icon */
6236 tree_view->priv->press_start_x + 1,
6239 g_object_unref (row_pix);
6240 gtk_tree_path_free (path);
6246 pspp_sheet_view_drag_end (GtkWidget *widget,
6247 GdkDragContext *context)
6252 /* Default signal implementations for the drag signals */
6254 pspp_sheet_view_drag_data_get (GtkWidget *widget,
6255 GdkDragContext *context,
6256 GtkSelectionData *selection_data,
6260 PsppSheetView *tree_view;
6261 GtkTreeModel *model;
6262 TreeViewDragInfo *di;
6263 GtkTreePath *source_row;
6265 tree_view = PSPP_SHEET_VIEW (widget);
6267 model = pspp_sheet_view_get_model (tree_view);
6272 di = get_info (PSPP_SHEET_VIEW (widget));
6277 source_row = get_source_row (context);
6279 if (source_row == NULL)
6282 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6283 * any model; for DragSource models there are some other targets
6287 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6288 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6293 /* If drag_data_get does nothing, try providing row data. */
6294 if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6296 gtk_tree_set_row_drag_data (selection_data,
6302 gtk_tree_path_free (source_row);
6307 pspp_sheet_view_drag_data_delete (GtkWidget *widget,
6308 GdkDragContext *context)
6310 TreeViewDragInfo *di;
6311 GtkTreeModel *model;
6312 PsppSheetView *tree_view;
6313 GtkTreePath *source_row;
6315 tree_view = PSPP_SHEET_VIEW (widget);
6316 model = pspp_sheet_view_get_model (tree_view);
6318 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6321 di = get_info (tree_view);
6326 source_row = get_source_row (context);
6328 if (source_row == NULL)
6331 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6334 gtk_tree_path_free (source_row);
6336 set_source_row (context, NULL, NULL);
6340 pspp_sheet_view_drag_leave (GtkWidget *widget,
6341 GdkDragContext *context,
6344 /* unset any highlight row */
6345 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6347 PSPP_SHEET_VIEW_DROP_BEFORE);
6349 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6354 pspp_sheet_view_drag_motion (GtkWidget *widget,
6355 GdkDragContext *context,
6356 /* coordinates relative to the widget */
6362 GtkTreePath *path = NULL;
6363 PsppSheetViewDropPosition pos;
6364 PsppSheetView *tree_view;
6365 GdkDragAction suggested_action = 0;
6368 tree_view = PSPP_SHEET_VIEW (widget);
6370 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6373 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6375 /* we only know this *after* set_desination_row */
6376 empty = tree_view->priv->empty_view_drop;
6378 if (path == NULL && !empty)
6380 /* Can't drop here. */
6381 gdk_drag_status (context, 0, time);
6385 if (tree_view->priv->open_dest_timeout == 0 &&
6386 (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6387 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6393 add_scroll_timeout (tree_view);
6396 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6398 /* Request data so we can use the source row when
6399 * determining whether to accept the drop
6401 set_status_pending (context, suggested_action);
6402 gtk_drag_get_data (widget, context, target, time);
6406 set_status_pending (context, 0);
6407 gdk_drag_status (context, suggested_action, time);
6412 gtk_tree_path_free (path);
6419 pspp_sheet_view_drag_drop (GtkWidget *widget,
6420 GdkDragContext *context,
6421 /* coordinates relative to the widget */
6426 PsppSheetView *tree_view;
6428 GdkDragAction suggested_action = 0;
6429 GdkAtom target = GDK_NONE;
6430 TreeViewDragInfo *di;
6431 GtkTreeModel *model;
6432 gboolean path_down_mode;
6433 gboolean drop_append_mode;
6435 tree_view = PSPP_SHEET_VIEW (widget);
6437 model = pspp_sheet_view_get_model (tree_view);
6439 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6441 di = get_info (tree_view);
6446 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6449 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6452 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6454 if (target != GDK_NONE && path != NULL)
6456 /* in case a motion had requested drag data, change things so we
6457 * treat drag data receives as a drop.
6459 set_status_pending (context, 0);
6460 set_dest_row (context, model, path,
6461 path_down_mode, tree_view->priv->empty_view_drop,
6466 gtk_tree_path_free (path);
6468 /* Unset this thing */
6469 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6471 PSPP_SHEET_VIEW_DROP_BEFORE);
6473 if (target != GDK_NONE)
6475 gtk_drag_get_data (widget, context, target, time);
6483 pspp_sheet_view_drag_data_received (GtkWidget *widget,
6484 GdkDragContext *context,
6485 /* coordinates relative to the widget */
6488 GtkSelectionData *selection_data,
6493 TreeViewDragInfo *di;
6494 gboolean accepted = FALSE;
6495 GtkTreeModel *model;
6496 PsppSheetView *tree_view;
6497 GtkTreePath *dest_row;
6498 GdkDragAction suggested_action;
6499 gboolean path_down_mode;
6500 gboolean drop_append_mode;
6502 tree_view = PSPP_SHEET_VIEW (widget);
6504 model = pspp_sheet_view_get_model (tree_view);
6506 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6509 di = get_info (tree_view);
6514 suggested_action = get_status_pending (context);
6516 if (suggested_action)
6518 /* We are getting this data due to a request in drag_motion,
6519 * rather than due to a request in drag_drop, so we are just
6520 * supposed to call drag_status, not actually paste in the
6523 path = get_logical_dest_row (tree_view, &path_down_mode,
6527 suggested_action = 0;
6528 else if (path_down_mode)
6529 gtk_tree_path_down (path);
6531 if (suggested_action)
6533 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6539 path_down_mode = FALSE;
6540 gtk_tree_path_up (path);
6542 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6545 suggested_action = 0;
6548 suggested_action = 0;
6552 gdk_drag_status (context, suggested_action, time);
6555 gtk_tree_path_free (path);
6557 /* If you can't drop, remove user drop indicator until the next motion */
6558 if (suggested_action == 0)
6559 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6561 PSPP_SHEET_VIEW_DROP_BEFORE);
6566 dest_row = get_dest_row (context, &path_down_mode);
6568 if (dest_row == NULL)
6571 if (gtk_selection_data_get_length (selection_data) >= 0)
6575 gtk_tree_path_down (dest_row);
6576 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6577 dest_row, selection_data))
6578 gtk_tree_path_up (dest_row);
6582 if (gtk_selection_data_get_length (selection_data) >= 0)
6584 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6590 gtk_drag_finish (context,
6592 (gdk_drag_context_get_actions (context) == GDK_ACTION_MOVE),
6595 if (gtk_tree_path_get_depth (dest_row) == 1
6596 && gtk_tree_path_get_indices (dest_row)[0] == 0)
6598 /* special special case drag to "0", scroll to first item */
6599 if (!tree_view->priv->scroll_to_path)
6600 pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6603 gtk_tree_path_free (dest_row);
6606 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6611 /* GtkContainer Methods
6616 pspp_sheet_view_remove (GtkContainer *container,
6619 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6620 PsppSheetViewChild *child = NULL;
6623 tmp_list = tree_view->priv->children;
6626 child = tmp_list->data;
6627 if (child->widget == widget)
6629 gtk_widget_unparent (widget);
6631 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6632 g_list_free_1 (tmp_list);
6633 g_slice_free (PsppSheetViewChild, child);
6637 tmp_list = tmp_list->next;
6640 tmp_list = tree_view->priv->columns;
6644 PsppSheetViewColumn *column;
6645 column = tmp_list->data;
6646 if (column->button == widget)
6648 gtk_widget_unparent (widget);
6651 tmp_list = tmp_list->next;
6656 pspp_sheet_view_forall (GtkContainer *container,
6657 gboolean include_internals,
6658 GtkCallback callback,
6659 gpointer callback_data)
6661 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6662 PsppSheetViewChild *child = NULL;
6663 PsppSheetViewColumn *column;
6666 tmp_list = tree_view->priv->children;
6669 child = tmp_list->data;
6670 tmp_list = tmp_list->next;
6672 (* callback) (child->widget, callback_data);
6674 if (include_internals == FALSE)
6677 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6679 column = tmp_list->data;
6682 (* callback) (column->button, callback_data);
6686 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6687 * cells. If so we draw one big row-spanning focus rectangle.
6690 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6694 if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6695 return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6697 for (list = tree_view->priv->columns; list; list = list->next)
6699 if (!((PsppSheetViewColumn *)list->data)->visible)
6701 if (_pspp_sheet_view_column_count_special_cells (list->data))
6709 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6710 PsppSheetViewColumn *focus_column,
6711 gboolean clamp_column_visible)
6713 g_return_if_fail (focus_column != NULL);
6715 tree_view->priv->focus_column = focus_column;
6716 if (!focus_column->button)
6718 pspp_sheet_view_column_set_need_button (focus_column, TRUE);
6719 // g_return_if_fail (focus_column->button != NULL);
6720 if (focus_column->button == NULL)
6724 if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view)) != focus_column->button)
6725 gtk_widget_grab_focus (focus_column->button);
6727 if (clamp_column_visible)
6728 pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6731 /* Returns TRUE if the focus is within the headers, after the focus operation is
6735 pspp_sheet_view_header_focus (PsppSheetView *tree_view,
6736 GtkDirectionType dir,
6737 gboolean clamp_column_visible)
6739 GtkWidget *focus_child;
6740 PsppSheetViewColumn *focus_column;
6741 GList *last_column, *first_column;
6745 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6748 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
6750 first_column = tree_view->priv->columns;
6751 while (first_column)
6753 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6755 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6757 first_column = first_column->next;
6760 /* No headers are visible, or are focusable. We can't focus in or out.
6762 if (first_column == NULL)
6765 last_column = g_list_last (tree_view->priv->columns);
6768 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6770 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6772 last_column = last_column->prev;
6776 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6780 case GTK_DIR_TAB_BACKWARD:
6781 case GTK_DIR_TAB_FORWARD:
6784 if (focus_child == NULL)
6786 if (tree_view->priv->focus_column != NULL &&
6787 pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6788 focus_column = tree_view->priv->focus_column;
6790 focus_column = first_column->data;
6791 pspp_sheet_view_focus_column (tree_view, focus_column,
6792 clamp_column_visible);
6799 if (focus_child == NULL)
6801 if (tree_view->priv->focus_column != NULL)
6802 focus_column = tree_view->priv->focus_column;
6803 else if (dir == GTK_DIR_LEFT)
6804 focus_column = last_column->data;
6806 focus_column = first_column->data;
6807 pspp_sheet_view_focus_column (tree_view, focus_column,
6808 clamp_column_visible);
6812 if (gtk_widget_child_focus (focus_child, dir))
6814 /* The focus moves inside the button. */
6815 /* This is probably a great example of bad UI */
6816 if (clamp_column_visible)
6817 pspp_sheet_view_clamp_column_visible (tree_view,
6818 tree_view->priv->focus_column,
6823 /* We need to move the focus among the row of buttons. */
6824 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6825 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6828 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6829 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6831 gtk_widget_error_bell (GTK_WIDGET (tree_view));
6837 PsppSheetViewColumn *column;
6839 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6840 tmp_list = tmp_list->next;
6842 tmp_list = tmp_list->prev;
6844 if (tmp_list == NULL)
6846 g_warning ("Internal button not found");
6849 column = tmp_list->data;
6850 if (column->visible &&
6851 pspp_sheet_view_column_can_focus (column))
6853 pspp_sheet_view_column_set_need_button (column, TRUE);
6856 pspp_sheet_view_focus_column (tree_view, column,
6857 clamp_column_visible);
6865 g_assert_not_reached ();
6872 /* This function returns in 'path' the first focusable path, if the given path
6873 * is already focusable, it's the returned one.
6877 search_first_focusable_path (PsppSheetView *tree_view,
6879 gboolean search_forward,
6882 /* XXX this function is trivial given that the sheetview doesn't support
6886 if (!path || !*path)
6889 _pspp_sheet_view_find_node (tree_view, *path, &node);
6897 return (*path != NULL);
6901 pspp_sheet_view_focus (GtkWidget *widget,
6902 GtkDirectionType direction)
6904 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6905 GtkContainer *container = GTK_CONTAINER (widget);
6906 GtkWidget *focus_child;
6908 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6911 focus_child = gtk_container_get_focus_child (container);
6913 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6914 /* Case 1. Headers currently have focus. */
6921 pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6923 case GTK_DIR_TAB_BACKWARD:
6926 case GTK_DIR_TAB_FORWARD:
6928 gtk_widget_grab_focus (widget);
6931 g_assert_not_reached ();
6936 /* Case 2. We don't have focus at all. */
6937 if (!gtk_widget_has_focus (widget))
6939 if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6940 gtk_widget_grab_focus (widget);
6944 /* Case 3. We have focus already. */
6945 if (direction == GTK_DIR_TAB_BACKWARD)
6946 return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6947 else if (direction == GTK_DIR_TAB_FORWARD)
6950 /* Other directions caught by the keybindings */
6951 gtk_widget_grab_focus (widget);
6956 pspp_sheet_view_grab_focus (GtkWidget *widget)
6958 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6960 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6964 pspp_sheet_view_style_set (GtkWidget *widget,
6965 GtkStyle *previous_style)
6967 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6969 PsppSheetViewColumn *column;
6971 if (gtk_widget_get_realized (widget))
6973 gdk_window_set_background (tree_view->priv->bin_window, >k_widget_get_style (widget)->base[gtk_widget_get_state (widget)]);
6974 gtk_style_set_background (gtk_widget_get_style (widget), tree_view->priv->header_window, GTK_STATE_NORMAL);
6975 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6978 gtk_widget_style_get (widget,
6979 "expander-size", &tree_view->priv->expander_size,
6981 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6983 for (list = tree_view->priv->columns; list; list = list->next)
6985 column = list->data;
6986 _pspp_sheet_view_column_cell_set_dirty (column);
6989 tree_view->priv->fixed_height = -1;
6991 /* Invalidate cached button style. */
6992 if (tree_view->priv->button_style)
6994 g_object_unref (tree_view->priv->button_style);
6995 tree_view->priv->button_style = NULL;
6998 gtk_widget_queue_resize (widget);
7003 pspp_sheet_view_set_focus_child (GtkContainer *container,
7006 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
7009 for (list = tree_view->priv->columns; list; list = list->next)
7011 if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
7013 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7018 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
7022 pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
7023 GtkAdjustment *hadj,
7024 GtkAdjustment *vadj)
7026 gboolean need_adjust = FALSE;
7028 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7031 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
7033 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7035 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
7037 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7039 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
7041 g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
7042 pspp_sheet_view_adjustment_changed,
7044 g_object_unref (tree_view->priv->hadjustment);
7047 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
7049 g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
7050 pspp_sheet_view_adjustment_changed,
7052 g_object_unref (tree_view->priv->vadjustment);
7055 if (tree_view->priv->hadjustment != hadj)
7057 tree_view->priv->hadjustment = hadj;
7058 g_object_ref_sink (tree_view->priv->hadjustment);
7060 g_signal_connect (tree_view->priv->hadjustment, "value-changed",
7061 G_CALLBACK (pspp_sheet_view_adjustment_changed),
7066 if (tree_view->priv->vadjustment != vadj)
7068 tree_view->priv->vadjustment = vadj;
7069 g_object_ref_sink (tree_view->priv->vadjustment);
7071 g_signal_connect (tree_view->priv->vadjustment, "value-changed",
7072 G_CALLBACK (pspp_sheet_view_adjustment_changed),
7078 pspp_sheet_view_adjustment_changed (NULL, tree_view);
7083 pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
7084 GtkMovementStep step,
7087 PsppSheetSelectMode mode;
7088 GdkModifierType state;
7090 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
7091 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
7092 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
7093 step == GTK_MOVEMENT_DISPLAY_LINES ||
7094 step == GTK_MOVEMENT_PAGES ||
7095 step == GTK_MOVEMENT_BUFFER_ENDS ||
7096 step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
7098 if (tree_view->priv->row_count == 0)
7100 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7103 pspp_sheet_view_stop_editing (tree_view, FALSE);
7104 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7105 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7108 if (gtk_get_current_event_state (&state))
7110 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7111 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
7112 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
7113 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
7115 /* else we assume not pressed */
7119 case GTK_MOVEMENT_LOGICAL_POSITIONS:
7120 pspp_sheet_view_move_cursor_tab (tree_view, count);
7122 case GTK_MOVEMENT_VISUAL_POSITIONS:
7123 pspp_sheet_view_move_cursor_left_right (tree_view, count, mode);
7125 case GTK_MOVEMENT_DISPLAY_LINES:
7126 pspp_sheet_view_move_cursor_up_down (tree_view, count, mode);
7128 case GTK_MOVEMENT_PAGES:
7129 pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode);
7131 case GTK_MOVEMENT_BUFFER_ENDS:
7132 pspp_sheet_view_move_cursor_start_end (tree_view, count, mode);
7134 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7135 pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode);
7138 g_assert_not_reached ();
7145 pspp_sheet_view_put (PsppSheetView *tree_view,
7146 GtkWidget *child_widget,
7147 /* in bin_window coordinates */
7153 PsppSheetViewChild *child;
7155 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7156 g_return_if_fail (GTK_IS_WIDGET (child_widget));
7158 child = g_slice_new (PsppSheetViewChild);
7160 child->widget = child_widget;
7163 child->width = width;
7164 child->height = height;
7166 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7168 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7169 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7171 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7175 _pspp_sheet_view_child_move_resize (PsppSheetView *tree_view,
7177 /* in tree coordinates */
7183 PsppSheetViewChild *child = NULL;
7185 GdkRectangle allocation;
7187 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7188 g_return_if_fail (GTK_IS_WIDGET (widget));
7190 for (list = tree_view->priv->children; list; list = list->next)
7192 if (((PsppSheetViewChild *)list->data)->widget == widget)
7201 allocation.x = child->x = x;
7202 allocation.y = child->y = y;
7203 allocation.width = child->width = width;
7204 allocation.height = child->height = height;
7206 if (gtk_widget_get_realized (widget))
7207 gtk_widget_size_allocate (widget, &allocation);
7211 /* TreeModel Callbacks
7215 pspp_sheet_view_row_changed (GtkTreeModel *model,
7220 PsppSheetView *tree_view = (PsppSheetView *)data;
7222 gboolean free_path = FALSE;
7223 GtkTreePath *cursor_path;
7225 g_return_if_fail (path != NULL || iter != NULL);
7227 if (tree_view->priv->cursor != NULL)
7228 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7232 if (tree_view->priv->edited_column &&
7233 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7234 pspp_sheet_view_stop_editing (tree_view, TRUE);
7236 if (cursor_path != NULL)
7237 gtk_tree_path_free (cursor_path);
7241 path = gtk_tree_model_get_path (model, iter);
7244 else if (iter == NULL)
7245 gtk_tree_model_get_iter (model, iter, path);
7247 _pspp_sheet_view_find_node (tree_view,
7253 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7254 pspp_sheet_view_node_queue_redraw (tree_view, node);
7258 gtk_tree_path_free (path);
7262 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7267 PsppSheetView *tree_view = (PsppSheetView *) data;
7270 gint height = tree_view->priv->fixed_height;
7271 gboolean free_path = FALSE;
7272 gboolean node_visible = TRUE;
7274 g_return_if_fail (path != NULL || iter != NULL);
7278 path = gtk_tree_model_get_path (model, iter);
7281 else if (iter == NULL)
7282 gtk_tree_model_get_iter (model, iter, path);
7284 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7286 /* Update all row-references */
7287 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7288 indices = gtk_tree_path_get_indices (path);
7289 tmpnode = indices[0];
7291 range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7295 if (node_visible && node_is_visible (tree_view, tmpnode))
7296 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7298 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7301 install_presize_handler (tree_view);
7303 gtk_tree_path_free (path);
7307 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7311 PsppSheetView *tree_view = (PsppSheetView *)data;
7314 g_return_if_fail (path != NULL);
7316 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7318 _pspp_sheet_view_find_node (tree_view, path, &node);
7323 range_tower_delete (tree_view->priv->selected, node, 1);
7325 /* Ensure we don't have a dangling pointer to a dead node */
7326 ensure_unprelighted (tree_view);
7328 /* Cancel editting if we've started */
7329 pspp_sheet_view_stop_editing (tree_view, TRUE);
7331 if (tree_view->priv->destroy_count_func)
7333 gint child_count = 0;
7334 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7337 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7339 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7341 gtk_tree_row_reference_free (tree_view->priv->top_row);
7342 tree_view->priv->top_row = NULL;
7345 install_scroll_sync_handler (tree_view);
7347 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7350 if (helper_data.changed)
7351 g_signal_emit_by_name (tree_view->priv->selection, "changed");
7356 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7357 GtkTreePath *parent,
7362 PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7365 /* XXX need to adjust selection */
7366 len = gtk_tree_model_iter_n_children (model, iter);
7371 gtk_tree_row_reference_reordered (G_OBJECT (data),
7376 if (gtk_tree_path_get_depth (parent) != 0)
7379 if (tree_view->priv->edited_column)
7380 pspp_sheet_view_stop_editing (tree_view, TRUE);
7382 /* we need to be unprelighted */
7383 ensure_unprelighted (tree_view);
7385 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7387 pspp_sheet_view_dy_to_top_row (tree_view);
7391 /* Internal tree functions
7396 pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view,
7397 PsppSheetViewColumn *column,
7401 PsppSheetViewColumn *tmp_column = NULL;
7412 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7415 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7417 list = (rtl ? list->prev : list->next))
7419 tmp_column = list->data;
7421 if (tmp_column == column)
7424 if (tmp_column->visible)
7425 total_width += tmp_column->width;
7428 if (tmp_column != column)
7430 g_warning (G_STRLOC": passed-in column isn't in the tree");
7439 if (column->visible)
7440 *x2 = total_width + column->width;
7442 *x2 = total_width; /* width of 0 */
7446 /* Make sure the node is visible vertically */
7448 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7451 gint node_dy, height;
7452 GtkTreePath *path = NULL;
7454 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7457 /* just return if the node is visible, avoiding a costly expose */
7458 node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7459 height = ROW_HEIGHT (tree_view);
7460 if (node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment)
7461 && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
7462 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
7465 path = _pspp_sheet_view_find_path (tree_view, node);
7468 /* We process updates because we want to clear old selected items when we scroll.
7469 * if this is removed, we get a "selection streak" at the bottom. */
7470 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7471 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7472 gtk_tree_path_free (path);
7477 pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
7478 PsppSheetViewColumn *column,
7479 gboolean focus_to_cell)
7486 x = column->allocation.x;
7487 width = column->allocation.width;
7489 if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7491 /* The column is larger than the horizontal page size. If the
7492 * column has cells which can be focussed individually, then we make
7493 * sure the cell which gets focus is fully visible (if even the
7494 * focus cell is bigger than the page size, we make sure the
7495 * left-hand side of the cell is visible).
7497 * If the column does not have those so-called special cells, we
7498 * make sure the left-hand side of the column is visible.
7501 if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7503 GtkTreePath *cursor_path;
7504 GdkRectangle background_area, cell_area, focus_area;
7506 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7508 pspp_sheet_view_get_cell_area (tree_view,
7509 cursor_path, column, &cell_area);
7510 pspp_sheet_view_get_background_area (tree_view,
7511 cursor_path, column,
7514 gtk_tree_path_free (cursor_path);
7516 _pspp_sheet_view_column_get_focus_area (column,
7522 width = focus_area.width;
7524 if (width < gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7526 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7527 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7528 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7529 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7530 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7534 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7536 gtk_adjustment_get_lower (tree_view->priv->hadjustment),
7537 gtk_adjustment_get_upper (tree_view->priv->hadjustment)
7538 - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)));
7542 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7543 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7544 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7545 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7546 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7551 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7556 path = gtk_tree_path_new ();
7558 gtk_tree_path_append_index (path, node);
7563 _pspp_sheet_view_find_node (PsppSheetView *tree_view,
7567 gint *indices = gtk_tree_path_get_indices (path);
7568 gint depth = gtk_tree_path_get_depth (path);
7571 if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7577 pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
7580 gboolean add_shifted_binding,
7581 GtkMovementStep step,
7585 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7590 if (add_shifted_binding)
7591 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7596 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7599 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7604 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7611 pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view,
7612 PsppSheetViewColumn *column)
7614 PsppSheetViewColumn *left_column;
7615 PsppSheetViewColumn *cur_column = NULL;
7616 PsppSheetViewColumnReorder *reorder;
7621 /* We want to precalculate the motion list such that we know what column slots
7625 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7627 /* First, identify all possible drop spots */
7629 tmp_list = g_list_last (tree_view->priv->columns);
7631 tmp_list = g_list_first (tree_view->priv->columns);
7635 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7636 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7638 if (cur_column->visible == FALSE)
7641 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7642 if (left_column != column && cur_column != column &&
7643 tree_view->priv->column_drop_func &&
7644 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7646 left_column = cur_column;
7649 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7650 reorder->left_column = left_column;
7651 left_column = reorder->right_column = cur_column;
7653 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7656 /* Add the last one */
7657 if (tree_view->priv->column_drop_func == NULL ||
7658 ((left_column != column) &&
7659 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7661 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7662 reorder->left_column = left_column;
7663 reorder->right_column = NULL;
7664 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7667 /* We quickly check to see if it even makes sense to reorder columns. */
7668 /* If there is nothing that can be moved, then we return */
7670 if (tree_view->priv->column_drag_info == NULL)
7673 /* We know there are always 2 slots possbile, as you can always return column. */
7674 /* If that's all there is, return */
7675 if (tree_view->priv->column_drag_info->next == NULL ||
7676 (tree_view->priv->column_drag_info->next->next == NULL &&
7677 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7678 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7680 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7681 g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7682 g_list_free (tree_view->priv->column_drag_info);
7683 tree_view->priv->column_drag_info = NULL;
7686 /* We fill in the ranges for the columns, now that we've isolated them */
7687 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7689 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7691 reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7693 reorder->left_align = left;
7694 if (tmp_list->next != NULL)
7696 g_assert (tmp_list->next->data);
7697 left = reorder->right_align = (reorder->right_column->allocation.x +
7698 reorder->right_column->allocation.width +
7699 ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7703 gint width = gdk_window_get_width (tree_view->priv->header_window);
7704 reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7710 _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view,
7711 PsppSheetViewColumn *column)
7713 GdkEvent *send_event;
7714 GtkAllocation allocation;
7716 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7717 GdkDisplay *display = gdk_screen_get_display (screen);
7719 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7720 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7721 g_return_if_fail (column->button);
7723 pspp_sheet_view_set_column_drag_info (tree_view, column);
7725 if (tree_view->priv->column_drag_info == NULL)
7728 if (tree_view->priv->drag_window == NULL)
7730 GdkWindowAttr attributes;
7731 guint attributes_mask;
7733 attributes.window_type = GDK_WINDOW_CHILD;
7734 attributes.wclass = GDK_INPUT_OUTPUT;
7735 attributes.x = column->allocation.x;
7737 attributes.width = column->allocation.width;
7738 attributes.height = column->allocation.height;
7739 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7740 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7741 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL ;
7743 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7746 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7749 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7750 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7752 gtk_grab_remove (column->button);
7754 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7755 send_event->crossing.send_event = TRUE;
7756 send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button)));
7757 send_event->crossing.subwindow = NULL;
7758 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7759 send_event->crossing.time = GDK_CURRENT_TIME;
7761 gtk_propagate_event (column->button, send_event);
7762 gdk_event_free (send_event);
7764 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7765 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7766 send_event->button.send_event = TRUE;
7767 send_event->button.time = GDK_CURRENT_TIME;
7768 send_event->button.x = -1;
7769 send_event->button.y = -1;
7770 send_event->button.axes = NULL;
7771 send_event->button.state = 0;
7772 send_event->button.button = 1;
7773 send_event->button.device =
7774 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
7776 send_event->button.x_root = 0;
7777 send_event->button.y_root = 0;
7779 gtk_propagate_event (column->button, send_event);
7780 gdk_event_free (send_event);
7782 /* Kids, don't try this at home */
7783 g_object_ref (column->button);
7784 gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7785 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7786 gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7787 g_object_unref (column->button);
7789 tree_view->priv->drag_column_x = column->allocation.x;
7790 allocation = column->allocation;
7792 gtk_widget_size_allocate (column->button, &allocation);
7793 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7795 tree_view->priv->drag_column = column;
7796 gdk_window_show (tree_view->priv->drag_window);
7798 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7800 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7801 while (gtk_events_pending ())
7802 gtk_main_iteration ();
7804 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7805 gdk_pointer_grab (tree_view->priv->drag_window,
7807 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7808 NULL, NULL, GDK_CURRENT_TIME);
7809 gdk_keyboard_grab (tree_view->priv->drag_window,
7815 _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view,
7817 const GdkRectangle *clip_rect)
7820 GtkAllocation allocation;
7822 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7825 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
7827 rect.width = MAX (tree_view->priv->width, allocation.width);
7829 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7830 rect.height = ROW_HEIGHT (tree_view);
7834 GdkRectangle new_rect;
7836 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7838 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7842 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7847 pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
7849 const GdkRectangle *clip_rect)
7853 _pspp_sheet_view_find_node (tree_view, path, &node);
7856 _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7860 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7863 GtkTreePath *cursor_path;
7865 if ((tree_view->priv->row_count == 0) ||
7866 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7870 if (tree_view->priv->cursor)
7871 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7873 if (cursor_path == NULL)
7875 /* There's no cursor. Move the cursor to the first selected row, if any
7876 * are selected, otherwise to the first row in the sheetview.
7878 GList *selected_rows;
7879 GtkTreeModel *model;
7880 PsppSheetSelection *selection;
7882 selection = pspp_sheet_view_get_selection (tree_view);
7883 selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7887 /* XXX we could avoid doing O(n) work to get this result */
7888 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7889 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7890 g_list_free (selected_rows);
7894 cursor_path = gtk_tree_path_new_first ();
7895 search_first_focusable_path (tree_view, &cursor_path,
7899 gtk_tree_row_reference_free (tree_view->priv->cursor);
7900 tree_view->priv->cursor = NULL;
7904 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7905 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7906 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0);
7908 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0);
7914 /* Now find a column for the cursor. */
7915 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7917 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7918 gtk_tree_path_free (cursor_path);
7920 if (tree_view->priv->focus_column == NULL)
7923 for (list = tree_view->priv->columns; list; list = list->next)
7925 if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7927 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7928 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7929 pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7939 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7941 PsppSheetSelectMode mode)
7943 gint selection_count;
7944 int cursor_node = -1;
7945 int new_cursor_node = -1;
7946 GtkTreePath *cursor_path = NULL;
7947 gboolean grab_focus = TRUE;
7949 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7953 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7954 /* FIXME: we lost the cursor; should we get the first? */
7957 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7958 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7960 if (cursor_node < 0)
7961 /* FIXME: we lost the cursor; should we get the first? */
7964 selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7966 if (selection_count == 0
7967 && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7968 && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
7970 /* Don't move the cursor, but just select the current node */
7971 new_cursor_node = cursor_node;
7976 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7978 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7981 gtk_tree_path_free (cursor_path);
7983 if (new_cursor_node)
7985 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7987 search_first_focusable_path (tree_view, &cursor_path,
7992 gtk_tree_path_free (cursor_path);
7996 * If the list has only one item and multi-selection is set then select
7997 * the row (if not yet selected).
7999 if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
8000 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
8001 new_cursor_node < 0)
8004 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
8006 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
8008 if (new_cursor_node < 0
8009 && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
8011 new_cursor_node = cursor_node;
8015 new_cursor_node = -1;
8019 if (new_cursor_node >= 0)
8021 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
8022 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode);
8023 gtk_tree_path_free (cursor_path);
8027 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8029 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8031 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
8033 GTK_DIR_UP : GTK_DIR_DOWN))
8035 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8038 gtk_widget_child_focus (toplevel,
8040 GTK_DIR_TAB_BACKWARD :
8041 GTK_DIR_TAB_FORWARD);
8048 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8053 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8055 return new_cursor_node >= 0;
8059 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
8061 PsppSheetSelectMode mode)
8063 int cursor_node = -1;
8064 GtkTreePath *old_cursor_path = NULL;
8065 GtkTreePath *cursor_path = NULL;
8066 int start_cursor_node = -1;
8069 gint vertical_separator;
8071 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8074 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8075 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8077 /* This is sorta weird. Focus in should give us a cursor */
8080 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
8081 _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
8083 if (cursor_node < 0)
8085 /* FIXME: we lost the cursor. Should we try to get one? */
8086 gtk_tree_path_free (old_cursor_path);
8090 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8091 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
8092 y += tree_view->priv->cursor_offset;
8093 y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment);
8094 y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment), (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator);
8096 if (y >= tree_view->priv->height)
8097 y = tree_view->priv->height - 1;
8099 tree_view->priv->cursor_offset =
8100 pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
8102 if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
8104 cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
8105 tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
8108 y -= tree_view->priv->cursor_offset;
8109 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8111 start_cursor_node = cursor_node;
8113 if (! search_first_focusable_path (tree_view, &cursor_path,
8117 /* It looks like we reached the end of the view without finding
8118 * a focusable row. We will step backwards to find the last
8121 cursor_node = start_cursor_node;
8122 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8124 search_first_focusable_path (tree_view, &cursor_path,
8133 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8135 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode);
8138 pspp_sheet_view_scroll_to_point (tree_view, -1, y);
8139 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8140 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8142 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
8143 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8145 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8148 gtk_tree_path_free (old_cursor_path);
8149 gtk_tree_path_free (cursor_path);
8153 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8155 PsppSheetSelectMode mode)
8157 int cursor_node = -1;
8158 GtkTreePath *cursor_path = NULL;
8159 PsppSheetViewColumn *column;
8162 gboolean found_column = FALSE;
8165 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8167 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8170 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8171 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8175 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8176 if (cursor_node < 0)
8178 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8180 gtk_tree_path_free (cursor_path);
8183 gtk_tree_path_free (cursor_path);
8185 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8186 if (tree_view->priv->focus_column)
8188 for (; list; list = (rtl ? list->prev : list->next))
8190 if (list->data == tree_view->priv->focus_column)
8197 gboolean left, right;
8199 column = list->data;
8200 if (column->visible == FALSE || column->row_head)
8203 pspp_sheet_view_column_cell_set_cell_data (column,
8204 tree_view->priv->model,
8209 right = list->prev ? TRUE : FALSE;
8210 left = list->next ? TRUE : FALSE;
8214 left = list->prev ? TRUE : FALSE;
8215 right = list->next ? TRUE : FALSE;
8218 if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8220 tree_view->priv->focus_column = column;
8221 found_column = TRUE;
8226 list = rtl ? list->prev : list->next;
8228 list = rtl ? list->next : list->prev;
8233 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8234 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8235 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8239 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8242 pspp_sheet_view_clamp_column_visible (tree_view,
8243 tree_view->priv->focus_column, TRUE);
8247 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8249 PsppSheetSelectMode mode)
8251 int cursor_node = -1;
8252 GtkTreePath *cursor_path = NULL;
8253 PsppSheetViewColumn *column;
8254 PsppSheetViewColumn *found_column;
8259 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8261 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8264 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8265 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8269 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8270 if (cursor_node < 0)
8272 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8274 gtk_tree_path_free (cursor_path);
8277 gtk_tree_path_free (cursor_path);
8279 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8280 if (tree_view->priv->focus_column)
8282 for (; list; list = (rtl ? list->prev : list->next))
8284 if (list->data == tree_view->priv->focus_column)
8289 found_column = NULL;
8292 gboolean left, right;
8294 column = list->data;
8295 if (column->visible == FALSE || column->row_head)
8298 pspp_sheet_view_column_cell_set_cell_data (column,
8299 tree_view->priv->model,
8304 right = list->prev ? TRUE : FALSE;
8305 left = list->next ? TRUE : FALSE;
8309 left = list->prev ? TRUE : FALSE;
8310 right = list->next ? TRUE : FALSE;
8313 if (column->tabbable
8314 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8315 found_column = column;
8319 list = rtl ? list->prev : list->next;
8321 list = rtl ? list->next : list->prev;
8326 tree_view->priv->focus_column = found_column;
8327 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8328 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8329 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8332 pspp_sheet_view_clamp_column_visible (tree_view,
8333 tree_view->priv->focus_column, TRUE);
8337 try_move_cursor_tab (PsppSheetView *tree_view,
8338 gboolean start_at_focus_column,
8341 PsppSheetViewColumn *column;
8343 int cursor_node = -1;
8344 GtkTreePath *cursor_path = NULL;
8348 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8349 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8353 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8354 if (cursor_node < 0)
8356 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8358 gtk_tree_path_free (cursor_path);
8361 gtk_tree_path_free (cursor_path);
8363 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8364 if (start_at_focus_column)
8367 ? g_list_last (tree_view->priv->columns)
8368 : g_list_first (tree_view->priv->columns));
8369 if (tree_view->priv->focus_column)
8371 for (; list; list = (rtl ? list->prev : list->next))
8373 if (list->data == tree_view->priv->focus_column)
8380 list = (rtl ^ (count == 1)
8381 ? g_list_first (tree_view->priv->columns)
8382 : g_list_last (tree_view->priv->columns));
8387 gboolean left, right;
8389 column = list->data;
8390 if (column->visible == FALSE || !column->tabbable)
8393 pspp_sheet_view_column_cell_set_cell_data (column,
8394 tree_view->priv->model,
8399 right = list->prev ? TRUE : FALSE;
8400 left = list->next ? TRUE : FALSE;
8404 left = list->prev ? TRUE : FALSE;
8405 right = list->next ? TRUE : FALSE;
8408 if (column->tabbable
8409 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8411 tree_view->priv->focus_column = column;
8412 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8413 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8414 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8419 list = rtl ? list->prev : list->next;
8421 list = rtl ? list->next : list->prev;
8428 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8431 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8434 if (!try_move_cursor_tab (tree_view, TRUE, count))
8436 if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0)
8437 && !try_move_cursor_tab (tree_view, FALSE, count))
8438 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8441 pspp_sheet_view_clamp_column_visible (tree_view,
8442 tree_view->priv->focus_column, TRUE);
8446 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8448 PsppSheetSelectMode mode)
8452 GtkTreePath *old_path;
8454 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8457 g_return_if_fail (tree_view->priv->row_count > 0);
8459 pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8463 /* Now go forward to find the first focusable row. */
8464 path = _pspp_sheet_view_find_path (tree_view, 0);
8465 search_first_focusable_path (tree_view, &path,
8466 TRUE, &cursor_node);
8470 /* Now go backwards to find last focusable row. */
8471 path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8472 search_first_focusable_path (tree_view, &path,
8473 FALSE, &cursor_node);
8479 if (gtk_tree_path_compare (old_path, path))
8481 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
8482 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8486 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8490 gtk_tree_path_free (old_path);
8491 gtk_tree_path_free (path);
8495 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8497 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8500 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8501 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8504 pspp_sheet_selection_select_all (tree_view->priv->selection);
8510 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8512 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8515 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8516 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8519 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8525 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8526 gboolean start_editing,
8527 PsppSheetSelectMode mode)
8530 int cursor_node = -1;
8531 GtkTreePath *cursor_path = NULL;
8533 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8536 if (tree_view->priv->cursor)
8537 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8539 if (cursor_path == NULL)
8542 _pspp_sheet_view_find_node (tree_view, cursor_path,
8545 if (cursor_node < 0)
8547 gtk_tree_path_free (cursor_path);
8551 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing &&
8552 tree_view->priv->focus_column)
8554 if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8556 gtk_tree_path_free (cursor_path);
8561 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8567 /* We bail out if the original (tree, node) don't exist anymore after
8568 * handling the selection-changed callback. We do return TRUE because
8569 * the key press has been handled at this point.
8571 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8573 if (cursor_node != new_node)
8576 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8578 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8579 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8581 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8582 pspp_sheet_view_row_activated (tree_view, cursor_path,
8583 tree_view->priv->focus_column);
8585 gtk_tree_path_free (cursor_path);
8591 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8594 int cursor_node = -1;
8595 GtkTreePath *cursor_path = NULL;
8597 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8601 if (tree_view->priv->cursor)
8602 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8604 if (cursor_path == NULL)
8607 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8608 if (cursor_node < 0)
8610 gtk_tree_path_free (cursor_path);
8614 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8617 PSPP_SHEET_SELECT_MODE_TOGGLE,
8620 /* We bail out if the original (tree, node) don't exist anymore after
8621 * handling the selection-changed callback. We do return TRUE because
8622 * the key press has been handled at this point.
8624 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8626 if (cursor_node != new_node)
8629 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8631 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8632 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8633 gtk_tree_path_free (cursor_path);
8639 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8641 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8642 tree_view->priv->typeselect_flush_timeout = 0;
8647 /* Cut and paste from gtkwindow.c */
8649 send_focus_change (GtkWidget *widget,
8652 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8654 fevent->focus_change.type = GDK_FOCUS_CHANGE;
8655 fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
8656 fevent->focus_change.in = in;
8658 gtk_widget_send_focus_change (widget, fevent);
8659 gdk_event_free (fevent);
8663 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8665 GtkWidget *frame, *vbox, *toplevel;
8668 if (tree_view->priv->search_custom_entry_set)
8671 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8672 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8674 if (tree_view->priv->search_window != NULL)
8676 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8677 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8678 GTK_WINDOW (tree_view->priv->search_window));
8679 else if (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)))
8680 gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
8681 GTK_WINDOW (tree_view->priv->search_window));
8682 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8686 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8687 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8689 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8690 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8691 GTK_WINDOW (tree_view->priv->search_window));
8693 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8694 GDK_WINDOW_TYPE_HINT_UTILITY);
8695 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8696 g_signal_connect (tree_view->priv->search_window, "delete-event",
8697 G_CALLBACK (pspp_sheet_view_search_delete_event),
8699 g_signal_connect (tree_view->priv->search_window, "key-press-event",
8700 G_CALLBACK (pspp_sheet_view_search_key_press_event),
8702 g_signal_connect (tree_view->priv->search_window, "button-press-event",
8703 G_CALLBACK (pspp_sheet_view_search_button_press_event),
8705 g_signal_connect (tree_view->priv->search_window, "scroll-event",
8706 G_CALLBACK (pspp_sheet_view_search_scroll_event),
8709 frame = gtk_frame_new (NULL);
8710 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8711 gtk_widget_show (frame);
8712 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8714 vbox = gtk_vbox_new (FALSE, 0);
8715 gtk_widget_show (vbox);
8716 gtk_container_add (GTK_CONTAINER (frame), vbox);
8717 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8720 tree_view->priv->search_entry = gtk_entry_new ();
8721 gtk_widget_show (tree_view->priv->search_entry);
8722 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8723 G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8725 g_signal_connect (tree_view->priv->search_entry,
8726 "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8730 g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8732 G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8736 gtk_container_add (GTK_CONTAINER (vbox),
8737 tree_view->priv->search_entry);
8739 gtk_widget_realize (tree_view->priv->search_entry);
8742 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8743 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8746 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8747 gboolean keybinding)
8749 /* We only start interactive search if we have focus or the columns
8750 * have focus. If one of our children have focus, we don't want to
8754 gboolean found_focus = FALSE;
8755 GtkWidgetClass *entry_parent_class;
8757 if (!tree_view->priv->enable_search && !keybinding)
8760 if (tree_view->priv->search_custom_entry_set)
8763 if (tree_view->priv->search_window != NULL &&
8764 gtk_widget_get_visible (tree_view->priv->search_window))
8767 for (list = tree_view->priv->columns; list; list = list->next)
8769 PsppSheetViewColumn *column;
8771 column = list->data;
8772 if (! column->visible)
8775 if (column->button && gtk_widget_has_focus (column->button))
8782 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8788 if (tree_view->priv->search_column < 0)
8791 pspp_sheet_view_ensure_interactive_directory (tree_view);
8794 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8797 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8798 gtk_widget_show (tree_view->priv->search_window);
8799 if (tree_view->priv->search_entry_changed_id == 0)
8801 tree_view->priv->search_entry_changed_id =
8802 g_signal_connect (tree_view->priv->search_entry, "changed",
8803 G_CALLBACK (pspp_sheet_view_search_init),
8807 tree_view->priv->typeselect_flush_timeout =
8808 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8809 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8812 /* Grab focus will select all the text. We don't want that to happen, so we
8813 * call the parent instance and bypass the selection change. This is probably
8814 * really non-kosher. */
8815 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8816 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8818 /* send focus-in event */
8819 send_focus_change (tree_view->priv->search_entry, TRUE);
8821 /* search first matching iter */
8822 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8828 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8830 return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8833 /* this function returns the new width of the column being resized given
8834 * the column and x position of the cursor; the x cursor position is passed
8835 * in as a pointer and automagicly corrected if it's beyond min/max limits
8838 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8842 PsppSheetViewColumn *column;
8846 /* first translate the x position from gtk_widget_get_window (widget)
8847 * to clist->clist_window
8849 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8850 column = g_list_nth (tree_view->priv->columns, i)->data;
8851 width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8853 /* Clamp down the value */
8854 if (column->min_width == -1)
8855 width = MAX (column->button_request, width);
8857 width = MAX (column->min_width, width);
8858 if (column->max_width != -1)
8859 width = MIN (width, column->max_width);
8861 *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8867 /* FIXME this adjust_allocation is a big cut-and-paste from
8868 * GtkCList, needs to be some "official" way to do this
8878 /* The window to which gtk_widget_get_window (widget) is relative */
8879 #define ALLOCATION_WINDOW(widget) \
8880 (!gtk_widget_get_has_window (widget) ? \
8881 gtk_widget_get_window (widget) : \
8882 gdk_window_get_parent (gtk_widget_get_window (widget)))
8885 adjust_allocation_recurse (GtkWidget *widget,
8888 ScrollData *scroll_data = data;
8889 GtkAllocation allocation;
8890 gtk_widget_get_allocation (widget, &allocation);
8891 /* Need to really size allocate instead of just poking
8892 * into widget->allocation if the widget is not realized.
8893 * FIXME someone figure out why this was.
8895 if (!gtk_widget_get_realized (widget))
8897 if (gtk_widget_get_visible (widget))
8899 GdkRectangle tmp_rectangle = allocation;
8900 tmp_rectangle.x += scroll_data->dx;
8901 tmp_rectangle.y += scroll_data->dy;
8903 gtk_widget_size_allocate (widget, &tmp_rectangle);
8908 if (ALLOCATION_WINDOW (widget) == scroll_data->window)
8910 allocation.x += scroll_data->dx;
8911 allocation.y += scroll_data->dy;
8913 if (GTK_IS_CONTAINER (widget))
8914 gtk_container_forall (GTK_CONTAINER (widget),
8915 adjust_allocation_recurse,
8922 adjust_allocation (GtkWidget *widget,
8926 ScrollData scroll_data;
8928 if (gtk_widget_get_realized (widget))
8929 scroll_data.window = ALLOCATION_WINDOW (widget);
8931 scroll_data.window = NULL;
8933 scroll_data.dx = dx;
8934 scroll_data.dy = dy;
8936 adjust_allocation_recurse (widget, &scroll_data);
8940 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8944 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8945 PsppSheetView *tree_view)
8947 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8952 gdk_window_move (tree_view->priv->bin_window,
8953 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8954 TREE_VIEW_HEADER_HEIGHT (tree_view));
8955 gdk_window_move (tree_view->priv->header_window,
8956 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8958 dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8961 update_prelight (tree_view,
8962 tree_view->priv->event_last_x,
8963 tree_view->priv->event_last_y - dy);
8965 if (tree_view->priv->edited_column &&
8966 GTK_IS_WIDGET (tree_view->priv->edited_column->editable_widget))
8970 PsppSheetViewChild *child = NULL;
8972 widget = GTK_WIDGET (tree_view->priv->edited_column->editable_widget);
8973 adjust_allocation (widget, 0, dy);
8975 for (list = tree_view->priv->children; list; list = list->next)
8977 child = (PsppSheetViewChild *)list->data;
8978 if (child->widget == widget)
8986 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8988 if (tree_view->priv->dy != (int) gtk_adjustment_get_value (tree_view->priv->vadjustment))
8990 /* update our dy and top_row */
8991 tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8993 if (!tree_view->priv->in_top_row_to_dy)
8994 pspp_sheet_view_dy_to_top_row (tree_view);
8997 for (list = tree_view->priv->columns; list; list = list->next)
8999 PsppSheetViewColumn *column = list->data;
9000 GtkAllocation *col_allocation = &column->allocation;
9001 GtkAllocation widget_allocation;
9002 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &widget_allocation);
9004 if (span_intersects (col_allocation->x, col_allocation->width,
9005 gtk_adjustment_get_value (tree_view->priv->hadjustment),
9006 widget_allocation.width))
9008 pspp_sheet_view_column_set_need_button (column, TRUE);
9009 if (!column->button)
9010 pspp_sheet_view_column_update_button (column);
9022 * pspp_sheet_view_new:
9024 * Creates a new #PsppSheetView widget.
9026 * Return value: A newly created #PsppSheetView widget.
9029 pspp_sheet_view_new (void)
9031 return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
9035 * pspp_sheet_view_new_with_model:
9036 * @model: the model.
9038 * Creates a new #PsppSheetView widget with the model initialized to @model.
9040 * Return value: A newly created #PsppSheetView widget.
9043 pspp_sheet_view_new_with_model (GtkTreeModel *model)
9045 return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
9052 * pspp_sheet_view_get_model:
9053 * @tree_view: a #PsppSheetView
9055 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
9058 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
9061 pspp_sheet_view_get_model (PsppSheetView *tree_view)
9063 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9065 return tree_view->priv->model;
9069 * pspp_sheet_view_set_model:
9070 * @tree_view: A #GtkTreeNode.
9071 * @model: (allow-none): The model.
9073 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
9074 * set, it will remove it before setting the new model. If @model is %NULL,
9075 * then it will unset the old model.
9078 pspp_sheet_view_set_model (PsppSheetView *tree_view,
9079 GtkTreeModel *model)
9081 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9082 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
9084 if (model == tree_view->priv->model)
9087 if (tree_view->priv->scroll_to_path)
9089 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9090 tree_view->priv->scroll_to_path = NULL;
9093 if (tree_view->priv->model)
9095 GList *tmplist = tree_view->priv->columns;
9097 if (tree_view->priv->selected)
9098 range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
9099 pspp_sheet_view_stop_editing (tree_view, TRUE);
9101 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9102 pspp_sheet_view_row_changed,
9104 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9105 pspp_sheet_view_row_inserted,
9107 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9108 pspp_sheet_view_row_deleted,
9110 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9111 pspp_sheet_view_rows_reordered,
9114 for (; tmplist; tmplist = tmplist->next)
9115 _pspp_sheet_view_column_unset_model (tmplist->data,
9116 tree_view->priv->model);
9118 tree_view->priv->prelight_node = -1;
9120 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
9121 tree_view->priv->drag_dest_row = NULL;
9122 gtk_tree_row_reference_free (tree_view->priv->cursor);
9123 tree_view->priv->cursor = NULL;
9124 gtk_tree_row_reference_free (tree_view->priv->anchor);
9125 tree_view->priv->anchor = NULL;
9126 gtk_tree_row_reference_free (tree_view->priv->top_row);
9127 tree_view->priv->top_row = NULL;
9128 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9129 tree_view->priv->scroll_to_path = NULL;
9131 tree_view->priv->scroll_to_column = NULL;
9133 g_object_unref (tree_view->priv->model);
9135 tree_view->priv->search_column = -1;
9136 tree_view->priv->fixed_height = -1;
9137 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
9138 tree_view->priv->last_button_x = -1;
9139 tree_view->priv->last_button_y = -1;
9142 tree_view->priv->model = model;
9144 if (tree_view->priv->model)
9148 if (tree_view->priv->search_column == -1)
9150 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
9152 GType type = gtk_tree_model_get_column_type (model, i);
9154 if (g_value_type_transformable (type, G_TYPE_STRING))
9156 tree_view->priv->search_column = i;
9162 g_object_ref (tree_view->priv->model);
9163 g_signal_connect (tree_view->priv->model,
9165 G_CALLBACK (pspp_sheet_view_row_changed),
9167 g_signal_connect (tree_view->priv->model,
9169 G_CALLBACK (pspp_sheet_view_row_inserted),
9171 g_signal_connect (tree_view->priv->model,
9173 G_CALLBACK (pspp_sheet_view_row_deleted),
9175 g_signal_connect (tree_view->priv->model,
9177 G_CALLBACK (pspp_sheet_view_rows_reordered),
9180 tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
9182 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
9183 install_presize_handler (tree_view);
9186 g_object_notify (G_OBJECT (tree_view), "model");
9188 if (tree_view->priv->selection)
9189 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
9191 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9192 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9196 * pspp_sheet_view_get_selection:
9197 * @tree_view: A #PsppSheetView.
9199 * Gets the #PsppSheetSelection associated with @tree_view.
9201 * Return value: A #PsppSheetSelection object.
9203 PsppSheetSelection *
9204 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
9206 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9208 return tree_view->priv->selection;
9212 * pspp_sheet_view_get_hadjustment:
9213 * @tree_view: A #PsppSheetView
9215 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
9217 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9221 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
9223 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9225 return pspp_sheet_view_do_get_hadjustment (tree_view);
9228 static GtkAdjustment *
9229 pspp_sheet_view_do_get_hadjustment (PsppSheetView *tree_view)
9231 return tree_view->priv->hadjustment;
9235 * pspp_sheet_view_set_hadjustment:
9236 * @tree_view: A #PsppSheetView
9237 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9239 * Sets the #GtkAdjustment for the current horizontal aspect.
9242 pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view,
9243 GtkAdjustment *adjustment)
9245 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9247 pspp_sheet_view_set_adjustments (tree_view,
9249 tree_view->priv->vadjustment);
9251 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9255 pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
9256 GtkAdjustment *adjustment)
9258 PsppSheetViewPrivate *priv = tree_view->priv;
9260 if (adjustment && priv->hadjustment == adjustment)
9263 if (priv->hadjustment != NULL)
9265 g_signal_handlers_disconnect_by_func (priv->hadjustment,
9266 pspp_sheet_view_adjustment_changed,
9268 g_object_unref (priv->hadjustment);
9271 if (adjustment == NULL)
9272 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9275 g_signal_connect (adjustment, "value-changed",
9276 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9277 priv->hadjustment = g_object_ref_sink (adjustment);
9278 /* FIXME: Adjustment should probably be populated here with fresh values, but
9279 * internal details are too complicated for me to decipher right now.
9281 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9283 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9287 * pspp_sheet_view_get_vadjustment:
9288 * @tree_view: A #PsppSheetView
9290 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9292 * Return value: (transfer none): A #GtkAdjustment object, or %NULL
9293 * if none is currently being used.
9295 * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
9298 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9300 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9302 return pspp_sheet_view_do_get_vadjustment (tree_view);
9305 static GtkAdjustment *
9306 pspp_sheet_view_do_get_vadjustment (PsppSheetView *tree_view)
9308 return tree_view->priv->vadjustment;
9312 * pspp_sheet_view_set_vadjustment:
9313 * @tree_view: A #PsppSheetView
9314 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9316 * Sets the #GtkAdjustment for the current vertical aspect.
9318 * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
9321 pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view,
9322 GtkAdjustment *adjustment)
9324 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9325 g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
9327 pspp_sheet_view_do_set_vadjustment (tree_view, adjustment);
9331 pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
9332 GtkAdjustment *adjustment)
9334 PsppSheetViewPrivate *priv = tree_view->priv;
9336 if (adjustment && priv->vadjustment == adjustment)
9339 if (priv->vadjustment != NULL)
9341 g_signal_handlers_disconnect_by_func (priv->vadjustment,
9342 pspp_sheet_view_adjustment_changed,
9344 g_object_unref (priv->vadjustment);
9347 if (adjustment == NULL)
9348 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9351 g_signal_connect (adjustment, "value-changed",
9352 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9353 priv->vadjustment = g_object_ref_sink (adjustment);
9354 /* FIXME: Adjustment should probably be populated here with fresh values, but
9355 * internal details are too complicated for me to decipher right now.
9357 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9358 g_object_notify (G_OBJECT (tree_view), "vadjustment");
9361 /* Column and header operations */
9364 * pspp_sheet_view_get_headers_visible:
9365 * @tree_view: A #PsppSheetView.
9367 * Returns %TRUE if the headers on the @tree_view are visible.
9369 * Return value: Whether the headers are visible or not.
9372 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9374 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9376 return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9380 * pspp_sheet_view_set_headers_visible:
9381 * @tree_view: A #PsppSheetView.
9382 * @headers_visible: %TRUE if the headers are visible
9384 * Sets the visibility state of the headers.
9387 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9388 gboolean headers_visible)
9392 PsppSheetViewColumn *column;
9393 GtkAllocation allocation;
9395 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9397 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9399 headers_visible = !! headers_visible;
9401 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9404 if (headers_visible)
9405 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9407 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9409 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9411 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9412 if (headers_visible)
9414 gdk_window_move_resize (tree_view->priv->bin_window, x, y + TREE_VIEW_HEADER_HEIGHT (tree_view),
9415 tree_view->priv->width, allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view));
9417 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9418 pspp_sheet_view_map_buttons (tree_view);
9422 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9424 for (list = tree_view->priv->columns; list; list = list->next)
9426 column = list->data;
9428 gtk_widget_unmap (column->button);
9430 gdk_window_hide (tree_view->priv->header_window);
9434 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view));
9435 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2);
9436 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
9437 gtk_adjustment_set_upper (tree_view->priv->vadjustment, tree_view->priv->height);
9438 gtk_adjustment_changed (tree_view->priv->vadjustment);
9440 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9442 g_object_notify (G_OBJECT (tree_view), "headers-visible");
9446 * pspp_sheet_view_columns_autosize:
9447 * @tree_view: A #PsppSheetView.
9449 * Resizes all columns to their optimal width. Only works after the
9450 * treeview has been realized.
9453 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9455 gboolean dirty = FALSE;
9457 PsppSheetViewColumn *column;
9459 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9461 for (list = tree_view->priv->columns; list; list = list->next)
9463 column = list->data;
9464 _pspp_sheet_view_column_cell_set_dirty (column);
9469 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9473 * pspp_sheet_view_set_headers_clickable:
9474 * @tree_view: A #PsppSheetView.
9475 * @setting: %TRUE if the columns are clickable.
9477 * Allow the column title buttons to be clicked.
9480 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9485 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9487 for (list = tree_view->priv->columns; list; list = list->next)
9488 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9490 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9495 * pspp_sheet_view_get_headers_clickable:
9496 * @tree_view: A #PsppSheetView.
9498 * Returns whether all header columns are clickable.
9500 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9505 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9509 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9511 for (list = tree_view->priv->columns; list; list = list->next)
9512 if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9519 * pspp_sheet_view_set_rules_hint
9520 * @tree_view: a #PsppSheetView
9521 * @setting: %TRUE if the tree requires reading across rows
9523 * This function tells GTK+ that the user interface for your
9524 * application requires users to read across tree rows and associate
9525 * cells with one another. By default, GTK+ will then render the tree
9526 * with alternating row colors. Do <emphasis>not</emphasis> use it
9527 * just because you prefer the appearance of the ruled tree; that's a
9528 * question for the theme. Some themes will draw tree rows in
9529 * alternating colors even when rules are turned off, and users who
9530 * prefer that appearance all the time can choose those themes. You
9531 * should call this function only as a <emphasis>semantic</emphasis>
9532 * hint to the theme engine that your tree makes alternating colors
9533 * useful from a functional standpoint (since it has lots of columns,
9538 pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view,
9541 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9543 setting = setting != FALSE;
9545 if (tree_view->priv->has_rules != setting)
9547 tree_view->priv->has_rules = setting;
9548 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9551 g_object_notify (G_OBJECT (tree_view), "rules-hint");
9555 * pspp_sheet_view_get_rules_hint
9556 * @tree_view: a #PsppSheetView
9558 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9560 * Return value: %TRUE if rules are useful for the user of this tree
9563 pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view)
9565 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9567 return tree_view->priv->has_rules;
9570 /* Public Column functions
9574 * pspp_sheet_view_append_column:
9575 * @tree_view: A #PsppSheetView.
9576 * @column: The #PsppSheetViewColumn to add.
9578 * Appends @column to the list of columns.
9580 * Return value: The number of columns in @tree_view after appending.
9583 pspp_sheet_view_append_column (PsppSheetView *tree_view,
9584 PsppSheetViewColumn *column)
9586 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9587 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9588 g_return_val_if_fail (column->tree_view == NULL, -1);
9590 return pspp_sheet_view_insert_column (tree_view, column, -1);
9595 * pspp_sheet_view_remove_column:
9596 * @tree_view: A #PsppSheetView.
9597 * @column: The #PsppSheetViewColumn to remove.
9599 * Removes @column from @tree_view.
9601 * Return value: The number of columns in @tree_view after removing.
9604 pspp_sheet_view_remove_column (PsppSheetView *tree_view,
9605 PsppSheetViewColumn *column)
9607 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9608 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9609 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9611 if (tree_view->priv->focus_column == column)
9612 tree_view->priv->focus_column = NULL;
9614 if (tree_view->priv->edited_column == column)
9616 pspp_sheet_view_stop_editing (tree_view, TRUE);
9618 /* no need to, but just to be sure ... */
9619 tree_view->priv->edited_column = NULL;
9622 _pspp_sheet_view_column_unset_tree_view (column);
9624 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9625 tree_view->priv->n_columns--;
9627 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9631 _pspp_sheet_view_column_unrealize_button (column);
9632 for (list = tree_view->priv->columns; list; list = list->next)
9634 PsppSheetViewColumn *tmp_column;
9636 tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9637 if (tmp_column->visible)
9638 _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9641 if (tree_view->priv->n_columns == 0 &&
9642 pspp_sheet_view_get_headers_visible (tree_view) &&
9643 tree_view->priv->header_window)
9644 gdk_window_hide (tree_view->priv->header_window);
9646 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9649 g_object_unref (column);
9650 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9652 return tree_view->priv->n_columns;
9656 * pspp_sheet_view_insert_column:
9657 * @tree_view: A #PsppSheetView.
9658 * @column: The #PsppSheetViewColumn to be inserted.
9659 * @position: The position to insert @column in.
9661 * This inserts the @column into the @tree_view at @position. If @position is
9662 * -1, then the column is inserted at the end.
9664 * Return value: The number of columns in @tree_view after insertion.
9667 pspp_sheet_view_insert_column (PsppSheetView *tree_view,
9668 PsppSheetViewColumn *column,
9671 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9672 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9673 g_return_val_if_fail (column->tree_view == NULL, -1);
9675 g_object_ref_sink (column);
9677 if (tree_view->priv->n_columns == 0 &&
9678 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9679 pspp_sheet_view_get_headers_visible (tree_view))
9681 gdk_window_show (tree_view->priv->header_window);
9684 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9686 tree_view->priv->n_columns++;
9688 _pspp_sheet_view_column_set_tree_view (column, tree_view);
9690 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9694 _pspp_sheet_view_column_realize_button (column);
9696 for (list = tree_view->priv->columns; list; list = list->next)
9698 column = PSPP_SHEET_VIEW_COLUMN (list->data);
9699 if (column->visible)
9700 _pspp_sheet_view_column_cell_set_dirty (column);
9702 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9705 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9707 return tree_view->priv->n_columns;
9711 * pspp_sheet_view_insert_column_with_attributes:
9712 * @tree_view: A #PsppSheetView
9713 * @position: The position to insert the new column in.
9714 * @title: The title to set the header to.
9715 * @cell: The #GtkCellRenderer.
9716 * @Varargs: A %NULL-terminated list of attributes.
9718 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9719 * @position. If @position is -1, then the newly created column is inserted at
9720 * the end. The column is initialized with the attributes given.
9722 * Return value: The number of columns in @tree_view after insertion.
9725 pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view,
9728 GtkCellRenderer *cell,
9731 PsppSheetViewColumn *column;
9736 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9738 column = pspp_sheet_view_column_new ();
9739 pspp_sheet_view_column_set_title (column, title);
9740 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9742 va_start (args, cell);
9744 attribute = va_arg (args, gchar *);
9746 while (attribute != NULL)
9748 column_id = va_arg (args, gint);
9749 pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9750 attribute = va_arg (args, gchar *);
9755 pspp_sheet_view_insert_column (tree_view, column, position);
9757 return tree_view->priv->n_columns;
9761 * pspp_sheet_view_insert_column_with_data_func:
9762 * @tree_view: a #PsppSheetView
9763 * @position: Position to insert, -1 for append
9764 * @title: column title
9765 * @cell: cell renderer for column
9766 * @func: function to set attributes of cell renderer
9767 * @data: data for @func
9768 * @dnotify: destroy notifier for @data
9770 * Convenience function that inserts a new column into the #PsppSheetView
9771 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9772 * attributes (normally using data from the model). See also
9773 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9775 * Return value: number of columns in the tree view post-insert
9778 pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view,
9781 GtkCellRenderer *cell,
9782 PsppSheetCellDataFunc func,
9784 GDestroyNotify dnotify)
9786 PsppSheetViewColumn *column;
9788 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9790 column = pspp_sheet_view_column_new ();
9791 pspp_sheet_view_column_set_title (column, title);
9792 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9793 pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9795 pspp_sheet_view_insert_column (tree_view, column, position);
9797 return tree_view->priv->n_columns;
9801 * pspp_sheet_view_get_column:
9802 * @tree_view: A #PsppSheetView.
9803 * @n: The position of the column, counting from 0.
9805 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9807 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9810 PsppSheetViewColumn *
9811 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9814 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9816 if (n < 0 || n >= tree_view->priv->n_columns)
9819 if (tree_view->priv->columns == NULL)
9822 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9826 * pspp_sheet_view_get_columns:
9827 * @tree_view: A #PsppSheetView
9829 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9830 * The returned list must be freed with g_list_free ().
9832 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9835 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9837 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9839 return g_list_copy (tree_view->priv->columns);
9843 * pspp_sheet_view_move_column_after:
9844 * @tree_view: A #PsppSheetView
9845 * @column: The #PsppSheetViewColumn to be moved.
9846 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9848 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9849 * @column is placed in the first position.
9852 pspp_sheet_view_move_column_after (PsppSheetView *tree_view,
9853 PsppSheetViewColumn *column,
9854 PsppSheetViewColumn *base_column)
9856 GList *column_list_el, *base_el = NULL;
9858 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9860 column_list_el = g_list_find (tree_view->priv->columns, column);
9861 g_return_if_fail (column_list_el != NULL);
9865 base_el = g_list_find (tree_view->priv->columns, base_column);
9866 g_return_if_fail (base_el != NULL);
9869 if (column_list_el->prev == base_el)
9872 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9873 if (base_el == NULL)
9875 column_list_el->prev = NULL;
9876 column_list_el->next = tree_view->priv->columns;
9877 if (column_list_el->next)
9878 column_list_el->next->prev = column_list_el;
9879 tree_view->priv->columns = column_list_el;
9883 column_list_el->prev = base_el;
9884 column_list_el->next = base_el->next;
9885 if (column_list_el->next)
9886 column_list_el->next->prev = column_list_el;
9887 base_el->next = column_list_el;
9890 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9892 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9893 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9896 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9900 * pspp_sheet_view_set_column_drag_function:
9901 * @tree_view: A #PsppSheetView.
9902 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9903 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9904 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9906 * Sets a user function for determining where a column may be dropped when
9907 * dragged. This function is called on every column pair in turn at the
9908 * beginning of a column drag to determine where a drop can take place. The
9909 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9910 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9911 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9912 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9913 * @tree_view reverts to the default behavior of allowing all columns to be
9914 * dropped everywhere.
9917 pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view,
9918 PsppSheetViewColumnDropFunc func,
9920 GDestroyNotify destroy)
9922 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9924 if (tree_view->priv->column_drop_func_data_destroy)
9925 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9927 tree_view->priv->column_drop_func = func;
9928 tree_view->priv->column_drop_func_data = user_data;
9929 tree_view->priv->column_drop_func_data_destroy = destroy;
9933 * pspp_sheet_view_scroll_to_point:
9934 * @tree_view: a #PsppSheetView
9935 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9936 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9938 * Scrolls the tree view such that the top-left corner of the visible
9939 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9940 * in tree coordinates. The @tree_view must be realized before
9941 * this function is called. If it isn't, you probably want to be
9942 * using pspp_sheet_view_scroll_to_cell().
9944 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9947 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9951 GtkAdjustment *hadj;
9952 GtkAdjustment *vadj;
9954 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9955 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9957 hadj = tree_view->priv->hadjustment;
9958 vadj = tree_view->priv->vadjustment;
9961 gtk_adjustment_set_value (hadj, CLAMP (tree_x, gtk_adjustment_get_lower (hadj), gtk_adjustment_get_upper (hadj) - gtk_adjustment_get_page_size (hadj)));
9963 gtk_adjustment_set_value (vadj, CLAMP (tree_y, gtk_adjustment_get_lower (vadj), gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj)));
9967 * pspp_sheet_view_scroll_to_cell:
9968 * @tree_view: A #PsppSheetView.
9969 * @path: (allow-none): The path of the row to move to, or %NULL.
9970 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9971 * @use_align: whether to use alignment arguments, or %FALSE.
9972 * @row_align: The vertical alignment of the row specified by @path.
9973 * @col_align: The horizontal alignment of the column specified by @column.
9975 * Moves the alignments of @tree_view to the position specified by @column and
9976 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9977 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9978 * or @path need to be non-%NULL. @row_align determines where the row is
9979 * placed, and @col_align determines where @column is placed. Both are expected
9980 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9981 * right/bottom alignment, 0.5 means center.
9983 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9984 * tree does the minimum amount of work to scroll the cell onto the screen.
9985 * This means that the cell will be scrolled to the edge closest to its current
9986 * position. If the cell is currently visible on the screen, nothing is done.
9988 * This function only works if the model is set, and @path is a valid row on the
9989 * model. If the model changes before the @tree_view is realized, the centered
9990 * path will be modified to reflect this change.
9993 pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view,
9995 PsppSheetViewColumn *column,
10000 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10001 g_return_if_fail (tree_view->priv->model != NULL);
10002 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
10003 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
10004 g_return_if_fail (path != NULL || column != NULL);
10007 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
10008 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
10010 row_align = CLAMP (row_align, 0.0, 1.0);
10011 col_align = CLAMP (col_align, 0.0, 1.0);
10014 /* Note: Despite the benefits that come from having one code path for the
10015 * scrolling code, we short-circuit validate_visible_area's immplementation as
10016 * it is much slower than just going to the point.
10018 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
10019 !gtk_widget_get_realized (GTK_WIDGET (tree_view))
10020 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
10022 if (tree_view->priv->scroll_to_path)
10023 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
10025 tree_view->priv->scroll_to_path = NULL;
10026 tree_view->priv->scroll_to_column = NULL;
10029 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
10031 tree_view->priv->scroll_to_column = column;
10032 tree_view->priv->scroll_to_use_align = use_align;
10033 tree_view->priv->scroll_to_row_align = row_align;
10034 tree_view->priv->scroll_to_col_align = col_align;
10036 install_presize_handler (tree_view);
10040 GdkRectangle cell_rect;
10041 GdkRectangle vis_rect;
10042 gint dest_x, dest_y;
10044 pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
10045 pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
10047 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
10049 dest_x = vis_rect.x;
10050 dest_y = vis_rect.y;
10056 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
10060 if (cell_rect.x < vis_rect.x)
10061 dest_x = cell_rect.x;
10062 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
10063 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
10071 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
10072 dest_y = MAX (dest_y, 0);
10076 if (cell_rect.y < vis_rect.y)
10077 dest_y = cell_rect.y;
10078 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
10079 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
10083 pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
10088 * pspp_sheet_view_row_activated:
10089 * @tree_view: A #PsppSheetView
10090 * @path: The #GtkTreePath to be activated.
10091 * @column: The #PsppSheetViewColumn to be activated.
10093 * Activates the cell determined by @path and @column.
10096 pspp_sheet_view_row_activated (PsppSheetView *tree_view,
10098 PsppSheetViewColumn *column)
10100 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10102 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
10107 * pspp_sheet_view_get_reorderable:
10108 * @tree_view: a #PsppSheetView
10110 * Retrieves whether the user can reorder the tree via drag-and-drop. See
10111 * pspp_sheet_view_set_reorderable().
10113 * Return value: %TRUE if the tree can be reordered.
10116 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
10118 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10120 return tree_view->priv->reorderable;
10124 * pspp_sheet_view_set_reorderable:
10125 * @tree_view: A #PsppSheetView.
10126 * @reorderable: %TRUE, if the tree can be reordered.
10128 * This function is a convenience function to allow you to reorder
10129 * models that support the #GtkDragSourceIface and the
10130 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
10131 * these. If @reorderable is %TRUE, then the user can reorder the
10132 * model by dragging and dropping rows. The developer can listen to
10133 * these changes by connecting to the model's row_inserted and
10134 * row_deleted signals. The reordering is implemented by setting up
10135 * the tree view as a drag source and destination. Therefore, drag and
10136 * drop can not be used in a reorderable view for any other purpose.
10138 * This function does not give you any degree of control over the order -- any
10139 * reordering is allowed. If more control is needed, you should probably
10140 * handle drag and drop manually.
10143 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
10144 gboolean reorderable)
10146 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10148 reorderable = reorderable != FALSE;
10150 if (tree_view->priv->reorderable == reorderable)
10155 const GtkTargetEntry row_targets[] = {
10156 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
10159 pspp_sheet_view_enable_model_drag_source (tree_view,
10162 G_N_ELEMENTS (row_targets),
10164 pspp_sheet_view_enable_model_drag_dest (tree_view,
10166 G_N_ELEMENTS (row_targets),
10171 pspp_sheet_view_unset_rows_drag_source (tree_view);
10172 pspp_sheet_view_unset_rows_drag_dest (tree_view);
10175 tree_view->priv->reorderable = reorderable;
10177 g_object_notify (G_OBJECT (tree_view), "reorderable");
10180 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
10181 is pressed, other rows will be unselected.
10183 If CLAMP_NODE is true, then the sheetview will scroll to make the row
10186 pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
10188 gboolean clear_and_select,
10189 gboolean clamp_node,
10190 PsppSheetSelectMode mode)
10194 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10196 GtkTreePath *cursor_path;
10197 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10198 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
10199 gtk_tree_path_free (cursor_path);
10202 gtk_tree_row_reference_free (tree_view->priv->cursor);
10203 tree_view->priv->cursor = NULL;
10205 _pspp_sheet_view_find_node (tree_view, path, &node);
10206 tree_view->priv->cursor =
10207 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
10208 tree_view->priv->model,
10211 if (tree_view->priv->row_count > 0)
10215 if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
10216 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
10220 /* We have to re-find tree and node here again, somebody might have
10221 * cleared the node or the whole tree in the PsppSheetSelection::changed
10222 * callback. If the nodes differ we bail out here.
10224 _pspp_sheet_view_find_node (tree_view, path, &new_node);
10226 if (node != new_node)
10231 pspp_sheet_view_clamp_node_visible (tree_view, node);
10232 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
10236 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
10240 * pspp_sheet_view_get_cursor:
10241 * @tree_view: A #PsppSheetView
10242 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
10243 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
10245 * Fills in @path and @focus_column with the current path and focus column. If
10246 * the cursor isn't currently set, then *@path will be %NULL. If no column
10247 * currently has focus, then *@focus_column will be %NULL.
10249 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
10250 * you are done with it.
10253 pspp_sheet_view_get_cursor (PsppSheetView *tree_view,
10254 GtkTreePath **path,
10255 PsppSheetViewColumn **focus_column)
10257 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10261 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10262 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10269 *focus_column = tree_view->priv->focus_column;
10274 * pspp_sheet_view_set_cursor:
10275 * @tree_view: A #PsppSheetView
10276 * @path: A #GtkTreePath
10277 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10278 * @start_editing: %TRUE if the specified cell should start being edited.
10280 * Sets the current keyboard focus to be at @path, and selects it. This is
10281 * useful when you want to focus the user's attention on a particular row. If
10282 * @focus_column is not %NULL, then focus is given to the column specified by
10283 * it. Additionally, if @focus_column is specified, and @start_editing is
10284 * %TRUE, then editing should be started in the specified cell.
10285 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10286 * in order to give keyboard focus to the widget. Please note that editing
10287 * can only happen when the widget is realized.
10289 * If @path is invalid for @model, the current cursor (if any) will be unset
10290 * and the function will return without failing.
10293 pspp_sheet_view_set_cursor (PsppSheetView *tree_view,
10295 PsppSheetViewColumn *focus_column,
10296 gboolean start_editing)
10298 pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10299 NULL, start_editing);
10303 * pspp_sheet_view_set_cursor_on_cell:
10304 * @tree_view: A #PsppSheetView
10305 * @path: A #GtkTreePath
10306 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10307 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10308 * @start_editing: %TRUE if the specified cell should start being edited.
10310 * Sets the current keyboard focus to be at @path, and selects it. This is
10311 * useful when you want to focus the user's attention on a particular row. If
10312 * @focus_column is not %NULL, then focus is given to the column specified by
10313 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10314 * contains 2 or more editable or activatable cells, then focus is given to
10315 * the cell specified by @focus_cell. Additionally, if @focus_column is
10316 * specified, and @start_editing is %TRUE, then editing should be started in
10317 * the specified cell. This function is often followed by
10318 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10319 * widget. Please note that editing can only happen when the widget is
10322 * If @path is invalid for @model, the current cursor (if any) will be unset
10323 * and the function will return without failing.
10328 pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view,
10330 PsppSheetViewColumn *focus_column,
10331 GtkCellRenderer *focus_cell,
10332 gboolean start_editing)
10334 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10335 g_return_if_fail (path != NULL);
10336 g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10338 if (!tree_view->priv->model)
10343 g_return_if_fail (focus_column);
10344 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10347 /* cancel the current editing, if it exists */
10348 if (tree_view->priv->edited_column &&
10349 tree_view->priv->edited_column->editable_widget)
10350 pspp_sheet_view_stop_editing (tree_view, TRUE);
10352 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
10354 if (focus_column && focus_column->visible)
10357 gboolean column_in_tree = FALSE;
10359 for (list = tree_view->priv->columns; list; list = list->next)
10360 if (list->data == focus_column)
10362 column_in_tree = TRUE;
10365 g_return_if_fail (column_in_tree);
10366 tree_view->priv->focus_column = focus_column;
10368 pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10370 pspp_sheet_view_start_editing (tree_view, path);
10372 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10373 pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10379 * pspp_sheet_view_get_bin_window:
10380 * @tree_view: A #PsppSheetView
10382 * Returns the window that @tree_view renders to. This is used primarily to
10383 * compare to <literal>event->window</literal> to confirm that the event on
10384 * @tree_view is on the right window.
10386 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10389 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10391 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10393 return tree_view->priv->bin_window;
10397 * pspp_sheet_view_get_path_at_pos:
10398 * @tree_view: A #PsppSheetView.
10399 * @x: The x position to be identified (relative to bin_window).
10400 * @y: The y position to be identified (relative to bin_window).
10401 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10402 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10403 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10404 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10406 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10407 * (please see pspp_sheet_view_get_bin_window()).
10408 * That is, @x and @y are relative to an events coordinates. @x and @y must
10409 * come from an event on the @tree_view only where <literal>event->window ==
10410 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10411 * things like popup menus. If @path is non-%NULL, then it will be filled
10412 * with the #GtkTreePath at that point. This path should be freed with
10413 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10414 * with the column at that point. @cell_x and @cell_y return the coordinates
10415 * relative to the cell background (i.e. the @background_area passed to
10416 * gtk_cell_renderer_render()). This function is only meaningful if
10417 * @tree_view is realized. Therefore this function will always return %FALSE
10418 * if @tree_view is not realized or does not have a model.
10420 * For converting widget coordinates (eg. the ones you get from
10421 * GtkWidget::query-tooltip), please see
10422 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10424 * Return value: %TRUE if a row exists at that coordinate.
10427 pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view,
10430 GtkTreePath **path,
10431 PsppSheetViewColumn **column,
10438 g_return_val_if_fail (tree_view != NULL, FALSE);
10445 if (tree_view->priv->bin_window == NULL)
10448 if (tree_view->priv->row_count == 0)
10451 if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment))
10454 if (x < 0 || y < 0)
10457 if (column || cell_x)
10459 PsppSheetViewColumn *tmp_column;
10460 PsppSheetViewColumn *last_column = NULL;
10462 gint remaining_x = x;
10463 gboolean found = FALSE;
10466 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10467 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10469 list = (rtl ? list->prev : list->next))
10471 tmp_column = list->data;
10473 if (tmp_column->visible == FALSE)
10476 last_column = tmp_column;
10477 if (remaining_x <= tmp_column->width)
10482 *column = tmp_column;
10485 *cell_x = remaining_x;
10489 remaining_x -= tmp_column->width;
10492 /* If found is FALSE and there is a last_column, then it the remainder
10493 * space is in that area
10500 *column = last_column;
10503 *cell_x = last_column->width + remaining_x;
10512 y_offset = pspp_sheet_view_find_offset (tree_view,
10513 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10520 *cell_y = y_offset;
10523 *path = _pspp_sheet_view_find_path (tree_view, node);
10528 /* Computes 'cell_area' from 'background_area', which must be the background
10529 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10530 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10531 the cell area as passed to _pspp_sheet_view_column_cell_render().
10533 'column' is required to properly adjust 'cell_area->x' and
10534 'cell_area->width'. It may be set to NULL if these values are not of
10535 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10538 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
10539 PsppSheetViewColumn *column,
10540 const GdkRectangle *background_area,
10541 gboolean subtract_focus_rect,
10542 GdkRectangle *cell_area)
10544 gint vertical_separator;
10545 gint horizontal_separator;
10547 *cell_area = *background_area;
10549 gtk_widget_style_get (GTK_WIDGET (tree_view),
10550 "vertical-separator", &vertical_separator,
10551 "horizontal-separator", &horizontal_separator,
10553 cell_area->x += horizontal_separator / 2;
10554 cell_area->y += vertical_separator / 2;
10555 cell_area->width -= horizontal_separator;
10556 cell_area->height -= vertical_separator;
10558 if (subtract_focus_rect)
10560 int focus_line_width;
10562 gtk_widget_style_get (GTK_WIDGET (tree_view),
10563 "focus-line-width", &focus_line_width,
10565 cell_area->x += focus_line_width;
10566 cell_area->y += focus_line_width;
10567 cell_area->width -= 2 * focus_line_width;
10568 cell_area->height -= 2 * focus_line_width;
10571 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10573 gint grid_line_width;
10574 gtk_widget_style_get (GTK_WIDGET (tree_view),
10575 "grid-line-width", &grid_line_width,
10578 if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10579 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10582 PsppSheetViewColumn *first_column, *last_column;
10585 /* Find the last visible column. */
10586 last_column = NULL;
10587 for (list = g_list_last (tree_view->priv->columns);
10591 PsppSheetViewColumn *c = list->data;
10599 /* Find the first visible column. */
10600 first_column = NULL;
10601 for (list = g_list_first (tree_view->priv->columns);
10605 PsppSheetViewColumn *c = list->data;
10613 if (column == first_column)
10615 cell_area->width -= grid_line_width / 2;
10617 else if (column == last_column)
10619 cell_area->x += grid_line_width / 2;
10620 cell_area->width -= grid_line_width / 2;
10624 cell_area->x += grid_line_width / 2;
10625 cell_area->width -= grid_line_width;
10629 if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10630 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10632 cell_area->y += grid_line_width / 2;
10633 cell_area->height -= grid_line_width;
10637 if (column == NULL)
10640 cell_area->width = 0;
10645 * pspp_sheet_view_get_cell_area:
10646 * @tree_view: a #PsppSheetView
10647 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10648 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10649 * @rect: rectangle to fill with cell rect
10651 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10652 * row specified by @path and the column specified by @column. If @path is
10653 * %NULL, or points to a path not currently displayed, the @y and @height fields
10654 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10655 * fields will be filled with 0. The sum of all cell rects does not cover the
10656 * entire tree; there are extra pixels in between rows, for example. The
10657 * returned rectangle is equivalent to the @cell_area passed to
10658 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10662 pspp_sheet_view_get_cell_area (PsppSheetView *tree_view,
10664 PsppSheetViewColumn *column,
10665 GdkRectangle *rect)
10667 GdkRectangle background_area;
10669 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10670 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10671 g_return_if_fail (rect != NULL);
10672 g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10673 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10675 pspp_sheet_view_get_background_area (tree_view, path, column,
10677 pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10682 * pspp_sheet_view_get_background_area:
10683 * @tree_view: a #PsppSheetView
10684 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10685 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10686 * @rect: rectangle to fill with cell background rect
10688 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10689 * row specified by @path and the column specified by @column. If @path is
10690 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10691 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10692 * fields will be filled with 0. The returned rectangle is equivalent to the
10693 * @background_area passed to gtk_cell_renderer_render(). These background
10694 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10695 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10696 * itself, excluding surrounding borders.
10700 pspp_sheet_view_get_background_area (PsppSheetView *tree_view,
10702 PsppSheetViewColumn *column,
10703 GdkRectangle *rect)
10707 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10708 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10709 g_return_if_fail (rect != NULL);
10718 /* Get vertical coords */
10720 _pspp_sheet_view_find_node (tree_view, path, &node);
10724 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10726 rect->height = ROW_HEIGHT (tree_view);
10733 pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10734 rect->width = x2 - rect->x;
10739 * pspp_sheet_view_get_visible_rect:
10740 * @tree_view: a #PsppSheetView
10741 * @visible_rect: rectangle to fill
10743 * Fills @visible_rect with the currently-visible region of the
10744 * buffer, in tree coordinates. Convert to bin_window coordinates with
10745 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10746 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10747 * scrollable area of the tree.
10750 pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view,
10751 GdkRectangle *visible_rect)
10755 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10757 widget = GTK_WIDGET (tree_view);
10761 GtkAllocation allocation;
10762 gtk_widget_get_allocation (widget, &allocation);
10763 visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
10764 visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
10765 visible_rect->width = allocation.width;
10766 visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10771 * pspp_sheet_view_widget_to_tree_coords:
10772 * @tree_view: a #PsppSheetView
10773 * @wx: X coordinate relative to bin_window
10774 * @wy: Y coordinate relative to bin_window
10775 * @tx: return location for tree X coordinate
10776 * @ty: return location for tree Y coordinate
10778 * Converts bin_window coordinates to coordinates for the
10779 * tree (the full scrollable area of the tree).
10781 * Deprecated: 2.12: Due to historial reasons the name of this function is
10782 * incorrect. For converting coordinates relative to the widget to
10783 * bin_window coordinates, please see
10784 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10788 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10794 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10797 *tx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10799 *ty = wy + tree_view->priv->dy;
10803 * pspp_sheet_view_tree_to_widget_coords:
10804 * @tree_view: a #PsppSheetView
10805 * @tx: tree X coordinate
10806 * @ty: tree Y coordinate
10807 * @wx: return location for X coordinate relative to bin_window
10808 * @wy: return location for Y coordinate relative to bin_window
10810 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10811 * to bin_window coordinates.
10813 * Deprecated: 2.12: Due to historial reasons the name of this function is
10814 * incorrect. For converting bin_window coordinates to coordinates relative
10815 * to bin_window, please see
10816 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10820 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10826 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10829 *wx = tx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10831 *wy = ty - tree_view->priv->dy;
10836 * pspp_sheet_view_convert_widget_to_tree_coords:
10837 * @tree_view: a #PsppSheetView
10838 * @wx: X coordinate relative to the widget
10839 * @wy: Y coordinate relative to the widget
10840 * @tx: return location for tree X coordinate
10841 * @ty: return location for tree Y coordinate
10843 * Converts widget coordinates to coordinates for the
10844 * tree (the full scrollable area of the tree).
10849 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10857 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10859 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10862 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10868 * pspp_sheet_view_convert_tree_to_widget_coords:
10869 * @tree_view: a #PsppSheetView
10870 * @tx: X coordinate relative to the tree
10871 * @ty: Y coordinate relative to the tree
10872 * @wx: return location for widget X coordinate
10873 * @wy: return location for widget Y coordinate
10875 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10876 * to widget coordinates.
10881 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10889 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10891 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10894 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10900 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10901 * @tree_view: a #PsppSheetView
10902 * @wx: X coordinate relative to the widget
10903 * @wy: Y coordinate relative to the widget
10904 * @bx: return location for bin_window X coordinate
10905 * @by: return location for bin_window Y coordinate
10907 * Converts widget coordinates to coordinates for the bin_window
10908 * (see pspp_sheet_view_get_bin_window()).
10913 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10919 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10922 *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10924 *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10928 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10929 * @tree_view: a #PsppSheetView
10930 * @bx: bin_window X coordinate
10931 * @by: bin_window Y coordinate
10932 * @wx: return location for widget X coordinate
10933 * @wy: return location for widget Y coordinate
10935 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10936 * to widget relative coordinates.
10941 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10947 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10950 *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10952 *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10956 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10957 * @tree_view: a #PsppSheetView
10958 * @tx: tree X coordinate
10959 * @ty: tree Y coordinate
10960 * @bx: return location for X coordinate relative to bin_window
10961 * @by: return location for Y coordinate relative to bin_window
10963 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10964 * to bin_window coordinates.
10969 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10975 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10980 *by = ty - tree_view->priv->dy;
10984 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10985 * @tree_view: a #PsppSheetView
10986 * @bx: X coordinate relative to bin_window
10987 * @by: Y coordinate relative to bin_window
10988 * @tx: return location for tree X coordinate
10989 * @ty: return location for tree Y coordinate
10991 * Converts bin_window coordinates to coordinates for the
10992 * tree (the full scrollable area of the tree).
10997 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
11003 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11008 *ty = by + tree_view->priv->dy;
11014 * pspp_sheet_view_get_visible_range:
11015 * @tree_view: A #PsppSheetView
11016 * @start_path: (allow-none): Return location for start of region, or %NULL.
11017 * @end_path: (allow-none): Return location for end of region, or %NULL.
11019 * Sets @start_path and @end_path to be the first and last visible path.
11020 * Note that there may be invisible paths in between.
11022 * The paths should be freed with gtk_tree_path_free() after use.
11024 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
11029 pspp_sheet_view_get_visible_range (PsppSheetView *tree_view,
11030 GtkTreePath **start_path,
11031 GtkTreePath **end_path)
11036 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11038 if (!tree_view->priv->row_count)
11045 pspp_sheet_view_find_offset (tree_view,
11046 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
11049 *start_path = _pspp_sheet_view_find_path (tree_view, node);
11058 if (tree_view->priv->height < gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
11059 y = tree_view->priv->height - 1;
11061 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1;
11063 pspp_sheet_view_find_offset (tree_view, y, &node);
11065 *end_path = _pspp_sheet_view_find_path (tree_view, node);
11074 unset_reorderable (PsppSheetView *tree_view)
11076 if (tree_view->priv->reorderable)
11078 tree_view->priv->reorderable = FALSE;
11079 g_object_notify (G_OBJECT (tree_view), "reorderable");
11084 * pspp_sheet_view_enable_model_drag_source:
11085 * @tree_view: a #PsppSheetView
11086 * @start_button_mask: Mask of allowed buttons to start drag
11087 * @targets: the table of targets that the drag will support
11088 * @n_targets: the number of items in @targets
11089 * @actions: the bitmask of possible actions for a drag from this
11092 * Turns @tree_view into a drag source for automatic DND. Calling this
11093 * method sets #PsppSheetView:reorderable to %FALSE.
11096 pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view,
11097 GdkModifierType start_button_mask,
11098 const GtkTargetEntry *targets,
11100 GdkDragAction actions)
11102 TreeViewDragInfo *di;
11104 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11106 gtk_drag_source_set (GTK_WIDGET (tree_view),
11112 di = ensure_info (tree_view);
11114 di->start_button_mask = start_button_mask;
11115 di->source_actions = actions;
11116 di->source_set = TRUE;
11118 unset_reorderable (tree_view);
11122 * pspp_sheet_view_enable_model_drag_dest:
11123 * @tree_view: a #PsppSheetView
11124 * @targets: the table of targets that the drag will support
11125 * @n_targets: the number of items in @targets
11126 * @actions: the bitmask of possible actions for a drag from this
11129 * Turns @tree_view into a drop destination for automatic DND. Calling
11130 * this method sets #PsppSheetView:reorderable to %FALSE.
11133 pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view,
11134 const GtkTargetEntry *targets,
11136 GdkDragAction actions)
11138 TreeViewDragInfo *di;
11140 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11142 gtk_drag_dest_set (GTK_WIDGET (tree_view),
11148 di = ensure_info (tree_view);
11149 di->dest_set = TRUE;
11151 unset_reorderable (tree_view);
11155 * pspp_sheet_view_unset_rows_drag_source:
11156 * @tree_view: a #PsppSheetView
11158 * Undoes the effect of
11159 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
11160 * #PsppSheetView:reorderable to %FALSE.
11163 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
11165 TreeViewDragInfo *di;
11167 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11169 di = get_info (tree_view);
11173 if (di->source_set)
11175 gtk_drag_source_unset (GTK_WIDGET (tree_view));
11176 di->source_set = FALSE;
11179 if (!di->dest_set && !di->source_set)
11180 remove_info (tree_view);
11183 unset_reorderable (tree_view);
11187 * pspp_sheet_view_unset_rows_drag_dest:
11188 * @tree_view: a #PsppSheetView
11190 * Undoes the effect of
11191 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
11192 * #PsppSheetView:reorderable to %FALSE.
11195 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
11197 TreeViewDragInfo *di;
11199 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11201 di = get_info (tree_view);
11207 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
11208 di->dest_set = FALSE;
11211 if (!di->dest_set && !di->source_set)
11212 remove_info (tree_view);
11215 unset_reorderable (tree_view);
11219 * pspp_sheet_view_set_drag_dest_row:
11220 * @tree_view: a #PsppSheetView
11221 * @path: (allow-none): The path of the row to highlight, or %NULL.
11222 * @pos: Specifies whether to drop before, after or into the row
11224 * Sets the row that is highlighted for feedback.
11227 pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view,
11229 PsppSheetViewDropPosition pos)
11231 GtkTreePath *current_dest;
11233 /* Note; this function is exported to allow a custom DND
11234 * implementation, so it can't touch TreeViewDragInfo
11237 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11239 current_dest = NULL;
11241 if (tree_view->priv->drag_dest_row)
11243 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11244 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
11247 /* special case a drop on an empty model */
11248 tree_view->priv->empty_view_drop = 0;
11250 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
11251 && gtk_tree_path_get_depth (path) == 1
11252 && gtk_tree_path_get_indices (path)[0] == 0)
11256 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
11260 tree_view->priv->empty_view_drop = 1;
11263 tree_view->priv->drag_dest_pos = pos;
11267 tree_view->priv->drag_dest_row =
11268 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
11269 pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
11272 tree_view->priv->drag_dest_row = NULL;
11276 int node, new_node;
11278 _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11279 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11283 new_node = pspp_sheet_view_node_next (tree_view, node);
11285 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11287 new_node = pspp_sheet_view_node_prev (tree_view, node);
11289 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11291 gtk_tree_path_free (current_dest);
11296 * pspp_sheet_view_get_drag_dest_row:
11297 * @tree_view: a #PsppSheetView
11298 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11299 * @pos: (allow-none): Return location for the drop position, or %NULL
11301 * Gets information about the row that is highlighted for feedback.
11304 pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view,
11305 GtkTreePath **path,
11306 PsppSheetViewDropPosition *pos)
11308 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11312 if (tree_view->priv->drag_dest_row)
11313 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11316 if (tree_view->priv->empty_view_drop)
11317 *path = gtk_tree_path_new_from_indices (0, -1);
11324 *pos = tree_view->priv->drag_dest_pos;
11328 * pspp_sheet_view_get_dest_row_at_pos:
11329 * @tree_view: a #PsppSheetView
11330 * @drag_x: the position to determine the destination row for
11331 * @drag_y: the position to determine the destination row for
11332 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11333 * @pos: (allow-none): Return location for the drop position, or %NULL
11335 * Determines the destination row for a given position. @drag_x and
11336 * @drag_y are expected to be in widget coordinates. This function is only
11337 * meaningful if @tree_view is realized. Therefore this function will always
11338 * return %FALSE if @tree_view is not realized or does not have a model.
11340 * Return value: whether there is a row at the given position, %TRUE if this
11341 * is indeed the case.
11344 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view,
11347 GtkTreePath **path,
11348 PsppSheetViewDropPosition *pos)
11352 gdouble offset_into_row;
11355 PsppSheetViewColumn *column = NULL;
11356 GtkTreePath *tmp_path = NULL;
11358 /* Note; this function is exported to allow a custom DND
11359 * implementation, so it can't touch TreeViewDragInfo
11362 g_return_val_if_fail (tree_view != NULL, FALSE);
11363 g_return_val_if_fail (drag_x >= 0, FALSE);
11364 g_return_val_if_fail (drag_y >= 0, FALSE);
11369 if (tree_view->priv->bin_window == NULL)
11372 if (tree_view->priv->row_count == 0)
11375 /* If in the top third of a row, we drop before that row; if
11376 * in the bottom third, drop after that row; if in the middle,
11377 * and the row has children, drop into the row.
11379 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11382 if (!pspp_sheet_view_get_path_at_pos (tree_view,
11391 pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11394 offset_into_row = cell_y;
11399 gtk_tree_path_free (tmp_path);
11403 third = cell.height / 3.0;
11407 if (offset_into_row < third)
11409 *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11411 else if (offset_into_row < (cell.height / 2.0))
11413 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11415 else if (offset_into_row < third * 2.0)
11417 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11421 *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11429 #if GTK3_TRANSITION
11430 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11432 * pspp_sheet_view_create_row_drag_icon:
11433 * @tree_view: a #PsppSheetView
11434 * @path: a #GtkTreePath in @tree_view
11436 * Creates a #GdkPixmap representation of the row at @path.
11437 * This image is used for a drag icon.
11439 * Return value: a newly-allocated pixmap of the drag icon.
11442 pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view,
11449 GdkRectangle background_area;
11450 GdkRectangle expose_area;
11452 /* start drawing inside the black outline */
11454 GdkDrawable *drawable;
11455 gint bin_window_width;
11458 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11459 g_return_val_if_fail (path != NULL, NULL);
11461 widget = GTK_WIDGET (tree_view);
11463 if (!gtk_widget_get_realized (widget))
11466 _pspp_sheet_view_find_node (tree_view,
11473 if (!gtk_tree_model_get_iter (tree_view->priv->model,
11480 background_area.y = y;
11481 background_area.height = ROW_HEIGHT (tree_view);
11483 bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
11485 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11486 bin_window_width + 2,
11487 background_area.height + 2,
11492 expose_area.width = bin_window_width + 2;
11493 expose_area.height = background_area.height + 2;
11495 #if GTK3_TRANSITION
11496 gdk_draw_rectangle (drawable,
11497 widget->style->base_gc [gtk_widget_get_state (widget)],
11500 bin_window_width + 2,
11501 background_area.height + 2);
11504 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11506 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11508 list = (rtl ? list->prev : list->next))
11510 PsppSheetViewColumn *column = list->data;
11511 GdkRectangle cell_area;
11512 gint vertical_separator;
11514 if (!column->visible)
11517 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11519 background_area.x = cell_offset;
11520 background_area.width = column->width;
11522 gtk_widget_style_get (widget,
11523 "vertical-separator", &vertical_separator,
11526 cell_area = background_area;
11528 cell_area.y += vertical_separator / 2;
11529 cell_area.height -= vertical_separator;
11531 if (pspp_sheet_view_column_cell_is_visible (column))
11532 _pspp_sheet_view_column_cell_render (column,
11538 cell_offset += column->width;
11541 #if GTK3_TRANSITION
11542 gdk_draw_rectangle (drawable,
11543 widget->style->black_gc,
11546 bin_window_width + 1,
11547 background_area.height + 1);
11555 * pspp_sheet_view_set_destroy_count_func:
11556 * @tree_view: A #PsppSheetView
11557 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11558 * @data: (allow-none): User data to be passed to @func, or %NULL
11559 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11561 * This function should almost never be used. It is meant for private use by
11562 * ATK for determining the number of visible children that are removed when a row is deleted.
11565 pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view,
11566 PsppSheetDestroyCountFunc func,
11568 GDestroyNotify destroy)
11570 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11572 if (tree_view->priv->destroy_count_destroy)
11573 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11575 tree_view->priv->destroy_count_func = func;
11576 tree_view->priv->destroy_count_data = data;
11577 tree_view->priv->destroy_count_destroy = destroy;
11582 * Interactive search
11586 * pspp_sheet_view_set_enable_search:
11587 * @tree_view: A #PsppSheetView
11588 * @enable_search: %TRUE, if the user can search interactively
11590 * If @enable_search is set, then the user can type in text to search through
11591 * the tree interactively (this is sometimes called "typeahead find").
11593 * Note that even if this is %FALSE, the user can still initiate a search
11594 * using the "start-interactive-search" key binding.
11597 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11598 gboolean enable_search)
11600 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11602 enable_search = !!enable_search;
11604 if (tree_view->priv->enable_search != enable_search)
11606 tree_view->priv->enable_search = enable_search;
11607 g_object_notify (G_OBJECT (tree_view), "enable-search");
11612 * pspp_sheet_view_get_enable_search:
11613 * @tree_view: A #PsppSheetView
11615 * Returns whether or not the tree allows to start interactive searching
11616 * by typing in text.
11618 * Return value: whether or not to let the user search interactively
11621 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11623 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11625 return tree_view->priv->enable_search;
11630 * pspp_sheet_view_get_search_column:
11631 * @tree_view: A #PsppSheetView
11633 * Gets the column searched on by the interactive search code.
11635 * Return value: the column the interactive search code searches in.
11638 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11640 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11642 return (tree_view->priv->search_column);
11646 * pspp_sheet_view_set_search_column:
11647 * @tree_view: A #PsppSheetView
11648 * @column: the column of the model to search in, or -1 to disable searching
11650 * Sets @column as the column where the interactive search code should
11651 * search in for the current model.
11653 * If the search column is set, users can use the "start-interactive-search"
11654 * key binding to bring up search popup. The enable-search property controls
11655 * whether simply typing text will also start an interactive search.
11657 * Note that @column refers to a column of the current model. The search
11658 * column is reset to -1 when the model is changed.
11661 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11664 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11665 g_return_if_fail (column >= -1);
11667 if (tree_view->priv->search_column == column)
11670 tree_view->priv->search_column = column;
11671 g_object_notify (G_OBJECT (tree_view), "search-column");
11675 * pspp_sheet_view_get_search_equal_func:
11676 * @tree_view: A #PsppSheetView
11678 * Returns the compare function currently in use.
11680 * Return value: the currently used compare function for the search code.
11683 PsppSheetViewSearchEqualFunc
11684 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11686 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11688 return tree_view->priv->search_equal_func;
11692 * pspp_sheet_view_set_search_equal_func:
11693 * @tree_view: A #PsppSheetView
11694 * @search_equal_func: the compare function to use during the search
11695 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11696 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11698 * Sets the compare function for the interactive search capabilities; note
11699 * that somewhat like strcmp() returning 0 for equality
11700 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11703 pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view,
11704 PsppSheetViewSearchEqualFunc search_equal_func,
11705 gpointer search_user_data,
11706 GDestroyNotify search_destroy)
11708 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11709 g_return_if_fail (search_equal_func != NULL);
11711 if (tree_view->priv->search_destroy)
11712 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11714 tree_view->priv->search_equal_func = search_equal_func;
11715 tree_view->priv->search_user_data = search_user_data;
11716 tree_view->priv->search_destroy = search_destroy;
11717 if (tree_view->priv->search_equal_func == NULL)
11718 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11722 * pspp_sheet_view_get_search_entry:
11723 * @tree_view: A #PsppSheetView
11725 * Returns the #GtkEntry which is currently in use as interactive search
11726 * entry for @tree_view. In case the built-in entry is being used, %NULL
11727 * will be returned.
11729 * Return value: the entry currently in use as search entry.
11734 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11736 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11738 if (tree_view->priv->search_custom_entry_set)
11739 return GTK_ENTRY (tree_view->priv->search_entry);
11745 * pspp_sheet_view_set_search_entry:
11746 * @tree_view: A #PsppSheetView
11747 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11749 * Sets the entry which the interactive search code will use for this
11750 * @tree_view. This is useful when you want to provide a search entry
11751 * in our interface at all time at a fixed position. Passing %NULL for
11752 * @entry will make the interactive search code use the built-in popup
11758 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11761 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11762 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11764 if (tree_view->priv->search_custom_entry_set)
11766 if (tree_view->priv->search_entry_changed_id)
11768 g_signal_handler_disconnect (tree_view->priv->search_entry,
11769 tree_view->priv->search_entry_changed_id);
11770 tree_view->priv->search_entry_changed_id = 0;
11772 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11773 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11776 g_object_unref (tree_view->priv->search_entry);
11778 else if (tree_view->priv->search_window)
11780 gtk_widget_destroy (tree_view->priv->search_window);
11782 tree_view->priv->search_window = NULL;
11787 tree_view->priv->search_entry = g_object_ref (entry);
11788 tree_view->priv->search_custom_entry_set = TRUE;
11790 if (tree_view->priv->search_entry_changed_id == 0)
11792 tree_view->priv->search_entry_changed_id =
11793 g_signal_connect (tree_view->priv->search_entry, "changed",
11794 G_CALLBACK (pspp_sheet_view_search_init),
11798 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11799 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11802 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11806 tree_view->priv->search_entry = NULL;
11807 tree_view->priv->search_custom_entry_set = FALSE;
11812 * pspp_sheet_view_set_search_position_func:
11813 * @tree_view: A #PsppSheetView
11814 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11815 * to use the default search position function
11816 * @data: (allow-none): user data to pass to @func, or %NULL
11817 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11819 * Sets the function to use when positioning the search dialog.
11824 pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view,
11825 PsppSheetViewSearchPositionFunc func,
11826 gpointer user_data,
11827 GDestroyNotify destroy)
11829 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11831 if (tree_view->priv->search_position_destroy)
11832 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11834 tree_view->priv->search_position_func = func;
11835 tree_view->priv->search_position_user_data = user_data;
11836 tree_view->priv->search_position_destroy = destroy;
11837 if (tree_view->priv->search_position_func == NULL)
11838 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11842 * pspp_sheet_view_get_search_position_func:
11843 * @tree_view: A #PsppSheetView
11845 * Returns the positioning function currently in use.
11847 * Return value: the currently used function for positioning the search dialog.
11851 PsppSheetViewSearchPositionFunc
11852 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11854 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11856 return tree_view->priv->search_position_func;
11861 pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
11862 PsppSheetView *tree_view)
11864 if (tree_view->priv->disable_popdown)
11867 if (tree_view->priv->search_entry_changed_id)
11869 g_signal_handler_disconnect (tree_view->priv->search_entry,
11870 tree_view->priv->search_entry_changed_id);
11871 tree_view->priv->search_entry_changed_id = 0;
11873 if (tree_view->priv->typeselect_flush_timeout)
11875 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11876 tree_view->priv->typeselect_flush_timeout = 0;
11879 if (gtk_widget_get_visible (search_dialog))
11881 /* send focus-in event */
11882 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11883 gtk_widget_hide (search_dialog);
11884 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11885 send_focus_change (GTK_WIDGET (tree_view), TRUE);
11890 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11891 GtkWidget *search_dialog,
11892 gpointer user_data)
11895 gint tree_x, tree_y;
11896 gint tree_width, tree_height;
11897 GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
11898 GdkScreen *screen = gdk_window_get_screen (tree_window);
11899 GtkRequisition requisition;
11901 GdkRectangle monitor;
11903 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11904 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11906 gtk_widget_realize (search_dialog);
11908 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11909 tree_width = gdk_window_get_width (tree_window);
11910 tree_height = gdk_window_get_height (tree_window);
11912 gtk_widget_size_request (search_dialog, &requisition);
11914 if (tree_x + tree_width > gdk_screen_get_width (screen))
11915 x = gdk_screen_get_width (screen) - requisition.width;
11916 else if (tree_x + tree_width - requisition.width < 0)
11919 x = tree_x + tree_width - requisition.width;
11921 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11922 y = gdk_screen_get_height (screen) - requisition.height;
11923 else if (tree_y + tree_height < 0) /* isn't really possible ... */
11926 y = tree_y + tree_height;
11928 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11932 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11936 PsppSheetView *tree_view = (PsppSheetView *)data;
11938 tree_view->priv->disable_popdown = 1;
11939 g_signal_connect (menu, "hide",
11940 G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11943 #if GTK3_TRANSITION
11944 /* Because we're visible but offscreen, we just set a flag in the preedit
11948 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11949 PsppSheetView *tree_view)
11951 tree_view->priv->imcontext_changed = 1;
11952 if (tree_view->priv->typeselect_flush_timeout)
11954 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11955 tree_view->priv->typeselect_flush_timeout =
11956 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11957 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11965 pspp_sheet_view_search_activate (GtkEntry *entry,
11966 PsppSheetView *tree_view)
11971 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11974 /* If we have a row selected and it's the cursor row, we activate
11976 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11978 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11980 _pspp_sheet_view_find_node (tree_view, path, &node);
11982 if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11983 pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11985 gtk_tree_path_free (path);
11990 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11992 PsppSheetView *tree_view = (PsppSheetView *)data;
11994 tree_view->priv->disable_popdown = 0;
12000 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
12003 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
12007 pspp_sheet_view_search_delete_event (GtkWidget *widget,
12008 GdkEventAny *event,
12009 PsppSheetView *tree_view)
12011 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
12013 pspp_sheet_view_search_dialog_hide (widget, tree_view);
12019 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
12020 GdkEventButton *event,
12021 PsppSheetView *tree_view)
12023 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
12025 pspp_sheet_view_search_dialog_hide (widget, tree_view);
12027 if (event->window == tree_view->priv->bin_window)
12028 pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
12034 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
12035 GdkEventScroll *event,
12036 PsppSheetView *tree_view)
12038 gboolean retval = FALSE;
12040 if (event->direction == GDK_SCROLL_UP)
12042 pspp_sheet_view_search_move (widget, tree_view, TRUE);
12045 else if (event->direction == GDK_SCROLL_DOWN)
12047 pspp_sheet_view_search_move (widget, tree_view, FALSE);
12051 /* renew the flush timeout */
12052 if (retval && tree_view->priv->typeselect_flush_timeout
12053 && !tree_view->priv->search_custom_entry_set)
12055 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12056 tree_view->priv->typeselect_flush_timeout =
12057 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12058 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12066 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
12067 GdkEventKey *event,
12068 PsppSheetView *tree_view)
12070 gboolean retval = FALSE;
12072 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
12073 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12075 /* close window and cancel the search */
12076 if (!tree_view->priv->search_custom_entry_set
12077 && (event->keyval == GDK_Escape ||
12078 event->keyval == GDK_Tab ||
12079 event->keyval == GDK_KP_Tab ||
12080 event->keyval == GDK_ISO_Left_Tab))
12082 pspp_sheet_view_search_dialog_hide (widget, tree_view);
12086 /* select previous matching iter */
12087 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
12089 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
12090 gtk_widget_error_bell (widget);
12095 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
12096 && (event->keyval == GDK_g || event->keyval == GDK_G))
12098 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
12099 gtk_widget_error_bell (widget);
12104 /* select next matching iter */
12105 if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
12107 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
12108 gtk_widget_error_bell (widget);
12113 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
12114 && (event->keyval == GDK_g || event->keyval == GDK_G))
12116 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
12117 gtk_widget_error_bell (widget);
12122 /* renew the flush timeout */
12123 if (retval && tree_view->priv->typeselect_flush_timeout
12124 && !tree_view->priv->search_custom_entry_set)
12126 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12127 tree_view->priv->typeselect_flush_timeout =
12128 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12129 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12136 /* this function returns FALSE if there is a search string but
12137 * nothing was found, and TRUE otherwise.
12140 pspp_sheet_view_search_move (GtkWidget *window,
12141 PsppSheetView *tree_view,
12149 GtkTreeModel *model;
12150 PsppSheetSelection *selection;
12152 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
12154 g_return_val_if_fail (text != NULL, FALSE);
12156 len = strlen (text);
12158 if (up && tree_view->priv->selected_iter == 1)
12159 return strlen (text) < 1;
12161 len = strlen (text);
12166 model = pspp_sheet_view_get_model (tree_view);
12167 selection = pspp_sheet_view_get_selection (tree_view);
12170 pspp_sheet_selection_unselect_all (selection);
12171 if (!gtk_tree_model_get_iter_first (model, &iter))
12174 ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
12175 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
12180 tree_view->priv->selected_iter += up?(-1):(1);
12185 /* return to old iter */
12187 gtk_tree_model_get_iter_first (model, &iter);
12188 pspp_sheet_view_search_iter (model, selection,
12190 &count, tree_view->priv->selected_iter);
12196 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
12200 gpointer search_data)
12202 gboolean retval = TRUE;
12204 gchar *normalized_string;
12205 gchar *normalized_key;
12206 gchar *case_normalized_string = NULL;
12207 gchar *case_normalized_key = NULL;
12208 GValue value = {0,};
12209 GValue transformed = {0,};
12211 gtk_tree_model_get_value (model, iter, column, &value);
12213 g_value_init (&transformed, G_TYPE_STRING);
12215 if (!g_value_transform (&value, &transformed))
12217 g_value_unset (&value);
12221 g_value_unset (&value);
12223 str = g_value_get_string (&transformed);
12226 g_value_unset (&transformed);
12230 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
12231 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
12233 if (normalized_string && normalized_key)
12235 case_normalized_string = g_utf8_casefold (normalized_string, -1);
12236 case_normalized_key = g_utf8_casefold (normalized_key, -1);
12238 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
12242 g_value_unset (&transformed);
12243 g_free (normalized_key);
12244 g_free (normalized_string);
12245 g_free (case_normalized_key);
12246 g_free (case_normalized_string);
12252 pspp_sheet_view_search_iter (GtkTreeModel *model,
12253 PsppSheetSelection *selection,
12262 PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
12264 path = gtk_tree_model_get_path (model, iter);
12265 _pspp_sheet_view_find_node (tree_view, path, &node);
12269 gboolean done = FALSE;
12271 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
12276 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
12278 pspp_sheet_selection_select_iter (selection, iter);
12279 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12282 gtk_tree_path_free (path);
12291 node = pspp_sheet_view_node_next (tree_view, node);
12297 has_next = gtk_tree_model_iter_next (model, iter);
12300 gtk_tree_path_next (path);
12303 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12308 gtk_tree_path_free (path);
12310 /* we've run out of tree, done with this func */
12322 pspp_sheet_view_search_init (GtkWidget *entry,
12323 PsppSheetView *tree_view)
12329 GtkTreeModel *model;
12330 PsppSheetSelection *selection;
12332 g_return_if_fail (GTK_IS_ENTRY (entry));
12333 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12335 text = gtk_entry_get_text (GTK_ENTRY (entry));
12337 model = pspp_sheet_view_get_model (tree_view);
12338 selection = pspp_sheet_view_get_selection (tree_view);
12341 pspp_sheet_selection_unselect_all (selection);
12342 if (tree_view->priv->typeselect_flush_timeout
12343 && !tree_view->priv->search_custom_entry_set)
12345 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12346 tree_view->priv->typeselect_flush_timeout =
12347 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12348 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12355 if (!gtk_tree_model_get_iter_first (model, &iter))
12358 ret = pspp_sheet_view_search_iter (model, selection,
12363 tree_view->priv->selected_iter = 1;
12367 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12368 PsppSheetView *tree_view)
12370 if (tree_view->priv->edited_column == NULL)
12373 _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12374 tree_view->priv->edited_column = NULL;
12376 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12377 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12379 g_signal_handlers_disconnect_by_func (cell_editable,
12380 pspp_sheet_view_remove_widget,
12382 g_signal_handlers_disconnect_by_func (cell_editable,
12383 pspp_sheet_view_editable_button_press_event,
12385 g_signal_handlers_disconnect_by_func (cell_editable,
12386 pspp_sheet_view_editable_clicked,
12389 gtk_container_remove (GTK_CONTAINER (tree_view),
12390 GTK_WIDGET (cell_editable));
12392 /* FIXME should only redraw a single node */
12393 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12397 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12398 GtkTreePath *cursor_path)
12401 GdkRectangle background_area;
12402 GdkRectangle cell_area;
12403 GtkCellEditable *editable_widget = NULL;
12404 gchar *path_string;
12405 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12406 gint retval = FALSE;
12409 g_assert (tree_view->priv->focus_column);
12411 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12414 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12415 if (cursor_node < 0)
12418 path_string = gtk_tree_path_to_string (cursor_path);
12419 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12421 pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12422 tree_view->priv->model,
12424 pspp_sheet_view_get_background_area (tree_view,
12426 tree_view->priv->focus_column,
12428 pspp_sheet_view_get_cell_area (tree_view,
12430 tree_view->priv->focus_column,
12433 if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12442 if (editable_widget != NULL)
12446 GtkCellRenderer *cell;
12449 cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12451 _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12454 area.width -= right + left;
12456 pspp_sheet_view_real_start_editing (tree_view,
12457 tree_view->priv->focus_column,
12466 g_free (path_string);
12471 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12472 GdkEventButton *event,
12473 PsppSheetView *sheet_view)
12477 node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12478 "pspp-sheet-view-node"));
12479 return pspp_sheet_view_row_head_clicked (sheet_view,
12481 sheet_view->priv->edited_column,
12486 pspp_sheet_view_editable_clicked (GtkButton *button,
12487 PsppSheetView *sheet_view)
12489 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12494 is_all_selected (GtkWidget *widget)
12496 GtkEntryBuffer *buffer;
12497 gint start_pos, end_pos;
12499 if (!GTK_IS_ENTRY (widget))
12502 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12503 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12504 &start_pos, &end_pos)
12506 && end_pos == gtk_entry_buffer_get_length (buffer));
12510 is_at_left (GtkWidget *widget)
12512 return (GTK_IS_ENTRY (widget)
12513 && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12517 is_at_right (GtkWidget *widget)
12519 GtkEntryBuffer *buffer;
12522 if (!GTK_IS_ENTRY (widget))
12525 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12526 length = gtk_entry_buffer_get_length (buffer);
12527 return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12531 pspp_sheet_view_event (GtkWidget *widget,
12532 GdkEventKey *event,
12533 PsppSheetView *tree_view)
12535 PsppSheetViewColumn *column;
12542 /* Intercept only key press events.
12543 It would make sense to use "key-press-event" instead of "event", but
12544 GtkEntry attaches its own signal handler to "key-press-event" that runs
12545 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12547 if (event->type != GDK_KEY_PRESS)
12550 keyval = event->keyval;
12552 switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12555 switch (event->keyval)
12557 case GDK_Left: case GDK_KP_Left:
12558 case GDK_Home: case GDK_KP_Home:
12559 if (!is_all_selected (widget) && !is_at_left (widget))
12563 case GDK_Right: case GDK_KP_Right:
12564 case GDK_End: case GDK_KP_End:
12565 if (!is_all_selected (widget) && !is_at_right (widget))
12569 case GDK_Up: case GDK_KP_Up:
12570 case GDK_Down: case GDK_KP_Down:
12573 case GDK_Page_Up: case GDK_KP_Page_Up:
12574 case GDK_Page_Down: case GDK_KP_Page_Down:
12585 case GDK_Tab: case GDK_KP_Tab:
12586 case GDK_ISO_Left_Tab:
12595 case GDK_SHIFT_MASK:
12596 switch (event->keyval)
12599 case GDK_ISO_Left_Tab:
12608 case GDK_CONTROL_MASK:
12609 switch (event->keyval)
12611 case GDK_Left: case GDK_KP_Left:
12612 if (!is_all_selected (widget) && !is_at_left (widget))
12616 case GDK_Right: case GDK_KP_Right:
12617 if (!is_all_selected (widget) && !is_at_right (widget))
12621 case GDK_Up: case GDK_KP_Up:
12622 case GDK_Down: case GDK_KP_Down:
12634 row = tree_view->priv->edited_row;
12635 column = tree_view->priv->edited_column;
12636 path = gtk_tree_path_new_from_indices (row, -1);
12638 pspp_sheet_view_stop_editing (tree_view, cancel);
12639 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12641 pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12642 gtk_tree_path_free (path);
12644 handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12645 G_OBJECT (tree_view));
12647 g_signal_stop_emission_by_name (widget, "event");
12649 pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12650 pspp_sheet_view_start_editing (tree_view, path);
12651 gtk_tree_path_free (path);
12657 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12660 PsppSheetView *sheet_view = data;
12662 g_signal_connect (widget, "event",
12663 G_CALLBACK (pspp_sheet_view_event),
12666 if (GTK_IS_CONTAINER (widget))
12667 gtk_container_foreach (GTK_CONTAINER (widget),
12668 pspp_sheet_view_override_cell_keypresses,
12673 pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
12674 PsppSheetViewColumn *column,
12676 GtkCellEditable *cell_editable,
12677 GdkRectangle *cell_area,
12681 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12682 gint pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
12683 GtkRequisition requisition;
12686 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12688 tree_view->priv->edited_column = column;
12689 _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12691 row = gtk_tree_path_get_indices (path)[0];
12692 tree_view->priv->edited_row = row;
12693 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12694 cell_area->y += pre_val - (int)gtk_adjustment_get_value (tree_view->priv->vadjustment);
12696 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12697 pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12698 tree_view->priv->anchor_column = column;
12700 gtk_widget_size_request (GTK_WIDGET (cell_editable), &requisition);
12702 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12704 if (requisition.height < cell_area->height)
12706 gint diff = cell_area->height - requisition.height;
12707 pspp_sheet_view_put (tree_view,
12708 GTK_WIDGET (cell_editable),
12709 cell_area->x, cell_area->y + diff/2,
12710 cell_area->width, requisition.height);
12714 pspp_sheet_view_put (tree_view,
12715 GTK_WIDGET (cell_editable),
12716 cell_area->x, cell_area->y,
12717 cell_area->width, cell_area->height);
12720 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12721 (GdkEvent *)event);
12723 gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12724 g_signal_connect (cell_editable, "remove-widget",
12725 G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12726 if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12727 GTK_IS_BUTTON (cell_editable))
12729 g_signal_connect (cell_editable, "button-press-event",
12730 G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12732 g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12733 GINT_TO_POINTER (row));
12734 g_signal_connect (cell_editable, "clicked",
12735 G_CALLBACK (pspp_sheet_view_editable_clicked),
12739 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12744 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12745 gboolean cancel_editing)
12747 PsppSheetViewColumn *column;
12748 GtkCellRenderer *cell;
12750 if (tree_view->priv->edited_column == NULL)
12754 * This is very evil. We need to do this, because
12755 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12756 * later on. If pspp_sheet_view_row_changed notices
12757 * tree_view->priv->edited_column != NULL, it'll call
12758 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12760 * Please read that again if you intend to modify anything here.
12763 column = tree_view->priv->edited_column;
12764 tree_view->priv->edited_column = NULL;
12766 cell = _pspp_sheet_view_column_get_edited_cell (column);
12767 gtk_cell_renderer_stop_editing (cell, cancel_editing);
12769 if (!cancel_editing)
12770 gtk_cell_editable_editing_done (column->editable_widget);
12772 tree_view->priv->edited_column = column;
12774 gtk_cell_editable_remove_widget (column->editable_widget);
12779 * pspp_sheet_view_set_hover_selection:
12780 * @tree_view: a #PsppSheetView
12781 * @hover: %TRUE to enable hover selection mode
12783 * Enables of disables the hover selection mode of @tree_view.
12784 * Hover selection makes the selected row follow the pointer.
12785 * Currently, this works only for the selection modes
12786 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12791 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12794 hover = hover != FALSE;
12796 if (hover != tree_view->priv->hover_selection)
12798 tree_view->priv->hover_selection = hover;
12800 g_object_notify (G_OBJECT (tree_view), "hover-selection");
12805 * pspp_sheet_view_get_hover_selection:
12806 * @tree_view: a #PsppSheetView
12808 * Returns whether hover selection mode is turned on for @tree_view.
12810 * Return value: %TRUE if @tree_view is in hover selection mode
12815 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12817 return tree_view->priv->hover_selection;
12821 * pspp_sheet_view_set_rubber_banding:
12822 * @tree_view: a #PsppSheetView
12823 * @enable: %TRUE to enable rubber banding
12825 * Enables or disables rubber banding in @tree_view. If the selection mode is
12826 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12827 * banding will allow the user to select multiple rows by dragging the mouse.
12832 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12835 enable = enable != FALSE;
12837 if (enable != tree_view->priv->rubber_banding_enable)
12839 tree_view->priv->rubber_banding_enable = enable;
12841 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12846 * pspp_sheet_view_get_rubber_banding:
12847 * @tree_view: a #PsppSheetView
12849 * Returns whether rubber banding is turned on for @tree_view. If the
12850 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12851 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12852 * select multiple rows by dragging the mouse.
12854 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12859 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12861 return tree_view->priv->rubber_banding_enable;
12865 * pspp_sheet_view_is_rubber_banding_active:
12866 * @tree_view: a #PsppSheetView
12868 * Returns whether a rubber banding operation is currently being done
12871 * Return value: %TRUE if a rubber banding operation is currently being
12872 * done in @tree_view.
12877 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12879 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12881 if (tree_view->priv->rubber_banding_enable
12882 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12889 pspp_sheet_view_grab_notify (GtkWidget *widget,
12890 gboolean was_grabbed)
12892 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12894 tree_view->priv->in_grab = !was_grabbed;
12898 tree_view->priv->pressed_button = -1;
12900 if (tree_view->priv->rubber_band_status)
12901 pspp_sheet_view_stop_rubber_band (tree_view);
12906 pspp_sheet_view_state_changed (GtkWidget *widget,
12907 GtkStateType previous_state)
12909 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12911 if (gtk_widget_get_realized (widget))
12913 GtkStyle *style = gtk_widget_get_style (widget);
12914 gdk_window_set_background (tree_view->priv->bin_window, &style->base[gtk_widget_get_state (widget)]);
12917 gtk_widget_queue_draw (widget);
12921 * pspp_sheet_view_get_grid_lines:
12922 * @tree_view: a #PsppSheetView
12924 * Returns which grid lines are enabled in @tree_view.
12926 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12931 PsppSheetViewGridLines
12932 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12934 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12936 return tree_view->priv->grid_lines;
12940 * pspp_sheet_view_set_grid_lines:
12941 * @tree_view: a #PsppSheetView
12942 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12945 * Sets which grid lines to draw in @tree_view.
12950 pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view,
12951 PsppSheetViewGridLines grid_lines)
12953 PsppSheetViewPrivate *priv;
12954 PsppSheetViewGridLines old_grid_lines;
12956 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12958 priv = tree_view->priv;
12960 old_grid_lines = priv->grid_lines;
12961 priv->grid_lines = grid_lines;
12963 if (old_grid_lines != grid_lines)
12965 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12967 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12972 * pspp_sheet_view_get_special_cells:
12973 * @tree_view: a #PsppSheetView
12975 * Returns which grid lines are enabled in @tree_view.
12977 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12978 * the sheet view contain special cells.
12980 PsppSheetViewSpecialCells
12981 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12983 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12985 return tree_view->priv->special_cells;
12989 * pspp_sheet_view_set_special_cells:
12990 * @tree_view: a #PsppSheetView
12991 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12992 * the sheet view contain special cells.
12994 * Sets whether rows in the sheet view contain special cells, controlling the
12995 * rendering of row selections.
12998 pspp_sheet_view_set_special_cells (PsppSheetView *tree_view,
12999 PsppSheetViewSpecialCells special_cells)
13001 PsppSheetViewPrivate *priv;
13003 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13005 priv = tree_view->priv;
13007 if (priv->special_cells != special_cells)
13009 priv->special_cells = special_cells;
13010 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
13011 g_object_notify (G_OBJECT (tree_view), "special-cells");
13016 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
13018 /* XXX (re)calculate fixed_height if necessary */
13019 return tree_view->priv->fixed_height;
13023 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
13026 g_return_if_fail (fixed_height > 0);
13028 if (tree_view->priv->fixed_height != fixed_height)
13030 tree_view->priv->fixed_height = fixed_height;
13031 g_object_notify (G_OBJECT (tree_view), "fixed-height");
13033 if (!tree_view->priv->fixed_height_set)
13035 tree_view->priv->fixed_height_set = TRUE;
13036 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
13041 * pspp_sheet_view_set_tooltip_row:
13042 * @tree_view: a #PsppSheetView
13043 * @tooltip: a #GtkTooltip
13044 * @path: a #GtkTreePath
13046 * Sets the tip area of @tooltip to be the area covered by the row at @path.
13047 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13048 * See also gtk_tooltip_set_tip_area().
13053 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
13054 GtkTooltip *tooltip,
13057 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13058 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
13060 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
13064 * pspp_sheet_view_set_tooltip_cell:
13065 * @tree_view: a #PsppSheetView
13066 * @tooltip: a #GtkTooltip
13067 * @path: (allow-none): a #GtkTreePath or %NULL
13068 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
13069 * @cell: (allow-none): a #GtkCellRenderer or %NULL
13071 * Sets the tip area of @tooltip to the area @path, @column and @cell have
13072 * in common. For example if @path is %NULL and @column is set, the tip
13073 * area will be set to the full area covered by @column. See also
13074 * gtk_tooltip_set_tip_area().
13076 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13081 pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view,
13082 GtkTooltip *tooltip,
13084 PsppSheetViewColumn *column,
13085 GtkCellRenderer *cell)
13089 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13090 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
13091 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
13092 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
13094 /* Determine x values. */
13095 if (column && cell)
13100 pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
13101 pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
13103 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13106 rect.width = width;
13112 pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
13113 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13116 rect.width = tmp.width;
13120 GtkAllocation allocation;
13121 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
13123 rect.width = allocation.width;
13126 /* Determine y values. */
13131 pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
13132 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13135 rect.height = tmp.height;
13140 rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
13143 gtk_tooltip_set_tip_area (tooltip, &rect);
13147 * pspp_sheet_view_get_tooltip_context:
13148 * @tree_view: a #PsppSheetView
13149 * @x: the x coordinate (relative to widget coordinates)
13150 * @y: the y coordinate (relative to widget coordinates)
13151 * @keyboard_tip: whether this is a keyboard tooltip or not
13152 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
13153 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
13154 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
13156 * This function is supposed to be used in a #GtkWidget::query-tooltip
13157 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
13158 * which are received in the signal handler, should be passed to this
13159 * function without modification.
13161 * The return value indicates whether there is a tree view row at the given
13162 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
13163 * tooltips the row returned will be the cursor row. When %TRUE, then any of
13164 * @model, @path and @iter which have been provided will be set to point to
13165 * that row and the corresponding model. @x and @y will always be converted
13166 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
13168 * Return value: whether or not the given tooltip context points to a row.
13173 pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view,
13176 gboolean keyboard_tip,
13177 GtkTreeModel **model,
13178 GtkTreePath **path,
13181 GtkTreePath *tmppath = NULL;
13183 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
13184 g_return_val_if_fail (x != NULL, FALSE);
13185 g_return_val_if_fail (y != NULL, FALSE);
13189 pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
13196 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
13199 if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
13200 &tmppath, NULL, NULL, NULL))
13205 *model = pspp_sheet_view_get_model (tree_view);
13208 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
13214 gtk_tree_path_free (tmppath);
13220 pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget,
13223 gboolean keyboard_tip,
13224 GtkTooltip *tooltip,
13227 GValue value = { 0, };
13228 GValue transformed = { 0, };
13231 GtkTreeModel *model;
13232 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
13234 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
13237 &model, &path, &iter))
13240 gtk_tree_model_get_value (model, &iter,
13241 tree_view->priv->tooltip_column, &value);
13243 g_value_init (&transformed, G_TYPE_STRING);
13245 if (!g_value_transform (&value, &transformed))
13247 g_value_unset (&value);
13248 gtk_tree_path_free (path);
13253 g_value_unset (&value);
13255 if (!g_value_get_string (&transformed))
13257 g_value_unset (&transformed);
13258 gtk_tree_path_free (path);
13263 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
13264 pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
13266 gtk_tree_path_free (path);
13267 g_value_unset (&transformed);
13273 * pspp_sheet_view_set_tooltip_column:
13274 * @tree_view: a #PsppSheetView
13275 * @column: an integer, which is a valid column number for @tree_view's model
13277 * If you only plan to have simple (text-only) tooltips on full rows, you
13278 * can use this function to have #PsppSheetView handle these automatically
13279 * for you. @column should be set to the column in @tree_view's model
13280 * containing the tooltip texts, or -1 to disable this feature.
13282 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
13283 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
13285 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
13286 * so &, <, etc have to be escaped in the text.
13291 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
13294 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13296 if (column == tree_view->priv->tooltip_column)
13301 g_signal_handlers_disconnect_by_func (tree_view,
13302 pspp_sheet_view_set_tooltip_query_cb,
13304 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13308 if (tree_view->priv->tooltip_column == -1)
13310 g_signal_connect (tree_view, "query-tooltip",
13311 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13312 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13316 tree_view->priv->tooltip_column = column;
13317 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13321 * pspp_sheet_view_get_tooltip_column:
13322 * @tree_view: a #PsppSheetView
13324 * Returns the column of @tree_view's model which is being used for
13325 * displaying tooltips on @tree_view's rows.
13327 * Return value: the index of the tooltip column that is currently being
13328 * used, or -1 if this is disabled.
13333 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13335 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13337 return tree_view->priv->tooltip_column;
13341 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13342 GValue *return_accu,
13343 const GValue *handler_return,
13346 gboolean continue_emission;
13347 gboolean signal_handled;
13349 signal_handled = g_value_get_boolean (handler_return);
13350 g_value_set_boolean (return_accu, signal_handled);
13351 continue_emission = !signal_handled;
13353 return continue_emission;
13358 pspp_sheet_view_grid_lines_get_type (void)
13360 static GType etype = 0;
13361 if (G_UNLIKELY(etype == 0)) {
13362 static const GEnumValue values[] = {
13363 { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13364 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13365 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13366 { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13369 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13375 pspp_sheet_view_special_cells_get_type (void)
13377 static GType etype = 0;
13378 if (G_UNLIKELY(etype == 0)) {
13379 static const GEnumValue values[] = {
13380 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13381 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13382 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13385 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);