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);
1983 gdk_window_move_resize (column->window,
1984 col_allocation.x + (rtl ? 0 : col_allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
1986 TREE_VIEW_DRAG_WIDTH, col_allocation.height);
1989 /* We change the width here. The user might have been resizing columns,
1990 * so the total width of the tree view changes.
1992 tree_view->priv->width = width;
1994 *width_changed = TRUE;
1997 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2001 pspp_sheet_view_size_allocate (GtkWidget *widget,
2002 GtkAllocation *allocation)
2004 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2006 gboolean width_changed = FALSE;
2007 GtkAllocation old_allocation;
2008 gtk_widget_get_allocation (widget, &old_allocation);
2010 if (allocation->width != old_allocation.width)
2011 width_changed = TRUE;
2014 gtk_widget_set_allocation (widget, allocation);
2016 tmp_list = tree_view->priv->children;
2020 GtkAllocation allocation;
2022 PsppSheetViewChild *child = tmp_list->data;
2023 tmp_list = tmp_list->next;
2025 /* totally ignore our child's requisition */
2026 allocation.x = child->x;
2027 allocation.y = child->y;
2028 allocation.width = child->width;
2029 allocation.height = child->height;
2030 gtk_widget_size_allocate (child->widget, &allocation);
2033 /* We size-allocate the columns first because the width of the
2034 * tree view (used in updating the adjustments below) might change.
2036 pspp_sheet_view_size_allocate_columns (widget, &width_changed);
2038 gtk_adjustment_set_page_size (tree_view->priv->hadjustment, allocation->width);
2039 gtk_adjustment_set_page_increment (tree_view->priv->hadjustment, allocation->width * 0.9);
2040 gtk_adjustment_set_step_increment (tree_view->priv->hadjustment, allocation->width * 0.1);
2041 gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0);
2042 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->hadjustment), tree_view->priv->width));
2044 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2046 if (allocation->width < tree_view->priv->width)
2048 if (tree_view->priv->init_hadjust_value)
2050 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2051 tree_view->priv->init_hadjust_value = FALSE;
2053 else if (allocation->width != old_allocation.width)
2055 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));
2058 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));
2062 gtk_adjustment_set_value (tree_view->priv->hadjustment, 0);
2063 tree_view->priv->init_hadjust_value = TRUE;
2067 if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + allocation->width > tree_view->priv->width)
2068 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2070 gtk_adjustment_changed (tree_view->priv->hadjustment);
2072 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2073 gtk_adjustment_set_step_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.1);
2074 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.9);
2075 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
2076 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->vadjustment), tree_view->priv->height));
2078 gtk_adjustment_changed (tree_view->priv->vadjustment);
2080 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2081 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
2082 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2083 else if (gtk_adjustment_get_value (tree_view->priv->vadjustment) + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
2084 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2085 tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
2086 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2087 pspp_sheet_view_top_row_to_dy (tree_view);
2089 pspp_sheet_view_dy_to_top_row (tree_view);
2091 if (gtk_widget_get_realized (widget))
2093 gdk_window_move_resize (gtk_widget_get_window (widget),
2094 allocation->x, allocation->y,
2095 allocation->width, allocation->height);
2096 gdk_window_move_resize (tree_view->priv->header_window,
2097 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2099 MAX (tree_view->priv->width, allocation->width),
2100 tree_view->priv->header_height);
2101 gdk_window_move_resize (tree_view->priv->bin_window,
2102 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2103 TREE_VIEW_HEADER_HEIGHT (tree_view),
2104 MAX (tree_view->priv->width, allocation->width),
2105 allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2108 if (tree_view->priv->row_count == 0)
2109 invalidate_empty_focus (tree_view);
2111 if (gtk_widget_get_realized (widget))
2113 gboolean has_expand_column = FALSE;
2114 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2116 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2118 has_expand_column = TRUE;
2123 /* This little hack only works if we have an LTR locale, and no column has the */
2126 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2127 ! has_expand_column)
2128 invalidate_last_column (tree_view);
2130 gtk_widget_queue_draw (widget);
2135 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2137 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2139 GtkWidget *widget = GTK_WIDGET (tree_view);
2141 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2142 gtk_widget_grab_focus (widget);
2143 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2147 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2150 return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2154 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2157 range_tower_set1 (tree_view->priv->selected, node, 1);
2161 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2164 range_tower_set0 (tree_view->priv->selected, node, 1);
2168 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2171 return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2175 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2178 return node > 0 ? node - 1 : -1;
2182 all_columns_selected (PsppSheetView *tree_view)
2186 for (list = tree_view->priv->columns; list; list = list->next)
2188 PsppSheetViewColumn *column = list->data;
2189 if (column->selectable && !column->selected)
2197 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2199 PsppSheetViewColumn *column,
2200 GdkEventButton *event)
2202 PsppSheetSelection *selection;
2203 PsppSheetSelectionMode mode;
2205 gboolean update_anchor;
2209 g_return_val_if_fail (tree_view != NULL, FALSE);
2210 g_return_val_if_fail (column != NULL, FALSE);
2212 selection = tree_view->priv->selection;
2213 mode = pspp_sheet_selection_get_mode (selection);
2214 if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2217 if (!column->row_head)
2222 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2223 if (event->type != GDK_BUTTON_PRESS
2224 || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2230 path = gtk_tree_path_new_from_indices (node, -1);
2233 pspp_sheet_selection_unselect_all (selection);
2234 pspp_sheet_selection_select_path (selection, path);
2235 pspp_sheet_selection_select_all_columns (selection);
2236 update_anchor = TRUE;
2239 else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2241 if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2242 || !all_columns_selected (tree_view))
2244 pspp_sheet_selection_unselect_all (selection);
2245 pspp_sheet_selection_select_path (selection, path);
2246 pspp_sheet_selection_select_all_columns (selection);
2247 update_anchor = TRUE;
2251 update_anchor = handled = FALSE;
2253 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2254 && modifiers == GDK_CONTROL_MASK)
2256 if (!all_columns_selected (tree_view))
2258 pspp_sheet_selection_unselect_all (selection);
2259 pspp_sheet_selection_select_all_columns (selection);
2262 if (pspp_sheet_selection_path_is_selected (selection, path))
2263 pspp_sheet_selection_unselect_path (selection, path);
2265 pspp_sheet_selection_select_path (selection, path);
2266 update_anchor = TRUE;
2269 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2270 && modifiers == GDK_SHIFT_MASK)
2272 GtkTreeRowReference *anchor = tree_view->priv->anchor;
2273 GtkTreePath *anchor_path;
2275 if (all_columns_selected (tree_view)
2276 && gtk_tree_row_reference_valid (anchor))
2278 update_anchor = FALSE;
2279 anchor_path = gtk_tree_row_reference_get_path (anchor);
2283 update_anchor = TRUE;
2284 anchor_path = gtk_tree_path_copy (path);
2287 pspp_sheet_selection_unselect_all (selection);
2288 pspp_sheet_selection_select_range (selection, anchor_path, path);
2289 pspp_sheet_selection_select_all_columns (selection);
2291 gtk_tree_path_free (anchor_path);
2296 update_anchor = handled = FALSE;
2300 if (tree_view->priv->anchor)
2301 gtk_tree_row_reference_free (tree_view->priv->anchor);
2302 tree_view->priv->anchor =
2303 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2304 tree_view->priv->model,
2308 gtk_tree_path_free (path);
2313 find_click (PsppSheetView *tree_view,
2316 PsppSheetViewColumn **column,
2317 GdkRectangle *background_area,
2318 GdkRectangle *cell_area)
2325 /* find the node that was clicked */
2326 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2329 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2334 background_area->y = y_offset + y;
2335 background_area->height = ROW_HEIGHT (tree_view);
2336 background_area->x = 0;
2338 /* Let the column have a chance at selecting it. */
2339 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2340 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2341 list; list = (rtl ? list->prev : list->next))
2343 PsppSheetViewColumn *candidate = list->data;
2345 if (!candidate->visible)
2348 background_area->width = candidate->width;
2349 if ((background_area->x > x) ||
2350 (background_area->x + background_area->width <= x))
2352 background_area->x += background_area->width;
2356 /* we found the focus column */
2358 pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2360 *column = candidate;
2368 pspp_sheet_view_button_press (GtkWidget *widget,
2369 GdkEventButton *event)
2371 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2373 PsppSheetViewColumn *column = NULL;
2375 GdkRectangle background_area;
2376 GdkRectangle cell_area;
2379 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2380 pspp_sheet_view_stop_editing (tree_view, FALSE);
2383 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2384 * we're done handling the button press.
2387 if (event->window == tree_view->priv->bin_window)
2392 gint pre_val, aft_val;
2393 PsppSheetViewColumn *column = NULL;
2394 GtkCellRenderer *focus_cell = NULL;
2395 gboolean row_double_click = FALSE;
2398 if (tree_view->priv->row_count == 0)
2400 grab_focus_and_unset_draw_keyfocus (tree_view);
2404 if (!find_click (tree_view, event->x, event->y, &node, &column,
2405 &background_area, &cell_area))
2407 grab_focus_and_unset_draw_keyfocus (tree_view);
2411 tree_view->priv->focus_column = column;
2413 if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2417 pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2419 path = _pspp_sheet_view_find_path (tree_view, node);
2421 /* we only handle selection modifications on the first button press
2423 if (event->type == GDK_BUTTON_PRESS)
2425 PsppSheetSelectionMode mode = 0;
2427 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2428 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
2429 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2430 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
2432 focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2434 pspp_sheet_view_column_focus_cell (column, focus_cell);
2436 if (event->state & GDK_CONTROL_MASK)
2438 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, mode);
2439 pspp_sheet_view_real_toggle_cursor_row (tree_view);
2441 else if (event->state & GDK_SHIFT_MASK)
2443 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
2444 pspp_sheet_view_real_select_cursor_row (tree_view, FALSE, mode);
2448 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
2451 if (tree_view->priv->anchor_column == NULL ||
2452 !(event->state & GDK_SHIFT_MASK))
2453 tree_view->priv->anchor_column = column;
2454 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2455 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2456 tree_view->priv->anchor_column,
2460 /* the treeview may have been scrolled because of _set_cursor,
2464 aft_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2465 dval = pre_val - aft_val;
2467 cell_area.y += dval;
2468 background_area.y += dval;
2470 /* Save press to possibly begin a drag
2472 if (!tree_view->priv->in_grab &&
2473 tree_view->priv->pressed_button < 0)
2475 tree_view->priv->pressed_button = event->button;
2476 tree_view->priv->press_start_x = event->x;
2477 tree_view->priv->press_start_y = event->y;
2478 tree_view->priv->press_start_node = node;
2480 if (tree_view->priv->rubber_banding_enable
2481 && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2482 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2484 tree_view->priv->press_start_y += tree_view->priv->dy;
2485 tree_view->priv->rubber_band_x = event->x;
2486 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2487 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2489 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2490 tree_view->priv->rubber_band_ctrl = TRUE;
2491 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2492 tree_view->priv->rubber_band_shift = TRUE;
2497 /* Test if a double click happened on the same row. */
2498 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2500 int double_click_time, double_click_distance;
2502 g_object_get (gtk_settings_get_for_screen (
2503 gtk_widget_get_screen (widget)),
2504 "gtk-double-click-time", &double_click_time,
2505 "gtk-double-click-distance", &double_click_distance,
2508 /* Same conditions as _gdk_event_button_generate */
2509 if (tree_view->priv->last_button_x != -1 &&
2510 (event->time < tree_view->priv->last_button_time + double_click_time) &&
2511 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2512 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2514 /* We do no longer compare paths of this row and the
2515 * row clicked previously. We use the double click
2516 * distance to decide whether this is a valid click,
2517 * allowing the mouse to slightly move over another row.
2519 row_double_click = TRUE;
2521 tree_view->priv->last_button_time = 0;
2522 tree_view->priv->last_button_x = -1;
2523 tree_view->priv->last_button_y = -1;
2527 tree_view->priv->last_button_time = event->time;
2528 tree_view->priv->last_button_x = event->x;
2529 tree_view->priv->last_button_y = event->y;
2533 if (row_double_click)
2535 gtk_grab_remove (widget);
2536 pspp_sheet_view_row_activated (tree_view, path, column);
2538 if (tree_view->priv->pressed_button == event->button)
2539 tree_view->priv->pressed_button = -1;
2542 gtk_tree_path_free (path);
2544 /* If we activated the row through a double click we don't want to grab
2545 * focus back, as moving focus to another widget is pretty common.
2547 if (!row_double_click)
2548 grab_focus_and_unset_draw_keyfocus (tree_view);
2553 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2555 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2557 column = list->data;
2558 if (event->window == column->window &&
2559 column->resizable &&
2564 if (gdk_pointer_grab (column->window, FALSE,
2565 GDK_POINTER_MOTION_HINT_MASK |
2566 GDK_BUTTON1_MOTION_MASK |
2567 GDK_BUTTON_RELEASE_MASK,
2568 NULL, NULL, event->time))
2571 gtk_grab_add (widget);
2572 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2573 column->resized_width = column->width;
2575 /* block attached dnd signal handler */
2576 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2578 g_signal_handlers_block_matched (widget,
2579 G_SIGNAL_MATCH_DATA,
2583 tree_view->priv->drag_pos = i;
2584 tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2586 if (!gtk_widget_has_focus (widget))
2587 gtk_widget_grab_focus (widget);
2595 /* GtkWidget::button_release_event helper */
2597 pspp_sheet_view_button_release_drag_column (GtkWidget *widget,
2598 GdkEventButton *event)
2600 PsppSheetView *tree_view;
2604 tree_view = PSPP_SHEET_VIEW (widget);
2606 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2607 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2608 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2610 /* Move the button back */
2611 g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2613 g_object_ref (tree_view->priv->drag_column->button);
2614 gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2615 gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2616 gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2617 g_object_unref (tree_view->priv->drag_column->button);
2618 gtk_widget_queue_resize (widget);
2619 if (tree_view->priv->drag_column->resizable)
2621 gdk_window_raise (tree_view->priv->drag_column->window);
2622 gdk_window_show (tree_view->priv->drag_column->window);
2625 gdk_window_hide (tree_view->priv->drag_column->window);
2627 gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2631 if (tree_view->priv->cur_reorder &&
2632 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2633 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2634 tree_view->priv->cur_reorder->right_column);
2638 if (tree_view->priv->cur_reorder &&
2639 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2640 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2641 tree_view->priv->cur_reorder->left_column);
2643 tree_view->priv->drag_column = NULL;
2644 gdk_window_hide (tree_view->priv->drag_window);
2646 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2647 g_slice_free (PsppSheetViewColumnReorder, l->data);
2648 g_list_free (tree_view->priv->column_drag_info);
2649 tree_view->priv->column_drag_info = NULL;
2650 tree_view->priv->cur_reorder = NULL;
2652 if (tree_view->priv->drag_highlight_window)
2653 gdk_window_hide (tree_view->priv->drag_highlight_window);
2655 /* Reset our flags */
2656 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2657 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2662 /* GtkWidget::button_release_event helper */
2664 pspp_sheet_view_button_release_column_resize (GtkWidget *widget,
2665 GdkEventButton *event)
2667 PsppSheetView *tree_view;
2670 tree_view = PSPP_SHEET_VIEW (widget);
2672 tree_view->priv->drag_pos = -1;
2674 /* unblock attached dnd signal handler */
2675 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2677 g_signal_handlers_unblock_matched (widget,
2678 G_SIGNAL_MATCH_DATA,
2682 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2683 gtk_grab_remove (widget);
2684 gdk_display_pointer_ungrab (gdk_window_get_display (event->window),
2690 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2691 GdkEventButton *event)
2693 GtkCellEditable *cell_editable;
2698 PsppSheetViewColumn *column;
2699 GdkRectangle background_area;
2700 GdkRectangle cell_area;
2706 if (event->window != tree_view->priv->bin_window)
2709 /* Ignore a released button, if that button wasn't depressed */
2710 if (tree_view->priv->pressed_button != event->button)
2713 if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2717 /* decide if we edit */
2718 path = _pspp_sheet_view_find_path (tree_view, node);
2719 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2720 if (event->button != 1 || modifiers)
2723 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2724 pspp_sheet_view_column_cell_set_cell_data (column,
2725 tree_view->priv->model,
2728 if (!pspp_sheet_view_column_get_quick_edit (column)
2729 && _pspp_sheet_view_column_has_editable_cell (column))
2732 flags = 0; /* FIXME: get the right flags */
2733 path_string = gtk_tree_path_to_string (path);
2735 if (!_pspp_sheet_view_column_cell_event (column,
2743 if (cell_editable == NULL)
2746 pspp_sheet_view_real_set_cursor (tree_view, path,
2747 TRUE, TRUE, 0); /* XXX mode? */
2748 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2751 _pspp_sheet_view_column_get_neighbor_sizes (
2752 column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2755 area.width -= right + left;
2757 pspp_sheet_view_real_start_editing (tree_view,
2764 g_free (path_string);
2765 gtk_tree_path_free (path);
2770 pspp_sheet_view_button_release (GtkWidget *widget,
2771 GdkEventButton *event)
2773 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2775 pspp_sheet_view_stop_editing (tree_view, FALSE);
2776 if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2777 && pspp_sheet_view_button_release_edit (tree_view, event))
2779 if (tree_view->priv->pressed_button == event->button)
2780 tree_view->priv->pressed_button = -1;
2782 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2786 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2787 return pspp_sheet_view_button_release_drag_column (widget, event);
2789 if (tree_view->priv->rubber_band_status)
2790 pspp_sheet_view_stop_rubber_band (tree_view);
2792 if (tree_view->priv->pressed_button == event->button)
2793 tree_view->priv->pressed_button = -1;
2795 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2796 return pspp_sheet_view_button_release_column_resize (widget, event);
2802 pspp_sheet_view_grab_broken (GtkWidget *widget,
2803 GdkEventGrabBroken *event)
2805 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2807 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2808 pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2810 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2811 pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2816 /* GtkWidget::motion_event function set.
2820 do_prelight (PsppSheetView *tree_view,
2822 /* these are in bin_window coords */
2826 int prev_node = tree_view->priv->prelight_node;
2828 if (prev_node != node)
2830 tree_view->priv->prelight_node = node;
2833 _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2836 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2842 prelight_or_select (PsppSheetView *tree_view,
2844 /* these are in bin_window coords */
2848 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2850 if (tree_view->priv->hover_selection &&
2851 (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2852 !(tree_view->priv->edited_column &&
2853 tree_view->priv->edited_column->editable_widget))
2857 if (!pspp_sheet_view_node_is_selected (tree_view, node))
2861 path = _pspp_sheet_view_find_path (tree_view, node);
2862 pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2863 if (pspp_sheet_view_node_is_selected (tree_view, node))
2865 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2866 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE, 0); /* XXX mode? */
2868 gtk_tree_path_free (path);
2872 else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2873 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2876 do_prelight (tree_view, node, x, y);
2880 ensure_unprelighted (PsppSheetView *tree_view)
2882 do_prelight (tree_view,
2884 -1000, -1000); /* coords not possibly over an arrow */
2886 g_assert (tree_view->priv->prelight_node < 0);
2890 update_prelight (PsppSheetView *tree_view,
2897 if (tree_view->priv->row_count == 0)
2902 ensure_unprelighted (tree_view);
2906 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2910 pspp_sheet_view_find_offset (tree_view, new_y, &node);
2913 prelight_or_select (tree_view, node, x, y);
2919 /* Our motion arrow is either a box (in the case of the original spot)
2920 * or an arrow. It is expander_size wide.
2943 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2946 PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2947 GtkWidget *widget = GTK_WIDGET (tree_view);
2948 GdkBitmap *mask = NULL;
2953 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2954 GdkWindowAttr attributes;
2955 guint attributes_mask;
2958 reorder->left_column == tree_view->priv->drag_column ||
2959 reorder->right_column == tree_view->priv->drag_column)
2960 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2961 else if (reorder->left_column || reorder->right_column)
2963 GdkRectangle visible_rect;
2964 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2965 if (reorder->left_column)
2966 x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2968 x = reorder->right_column->allocation.x;
2970 if (x < visible_rect.x)
2971 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2972 else if (x > visible_rect.x + visible_rect.width)
2973 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2975 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2978 /* We want to draw the rectangle over the initial location. */
2979 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2984 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2986 if (tree_view->priv->drag_highlight_window)
2988 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2990 gdk_window_destroy (tree_view->priv->drag_highlight_window);
2993 attributes.window_type = GDK_WINDOW_CHILD;
2994 attributes.wclass = GDK_INPUT_OUTPUT;
2995 attributes.x = tree_view->priv->drag_column_x;
2997 width = attributes.width = tree_view->priv->drag_column->allocation.width;
2998 height = attributes.height = tree_view->priv->drag_column->allocation.height;
2999 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3000 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3001 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3002 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3003 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
3004 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3006 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3007 gc = gdk_gc_new (mask);
3009 gdk_gc_set_foreground (gc, &col);
3010 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3012 gdk_gc_set_foreground(gc, &col);
3013 gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
3014 g_object_unref (gc);
3016 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3018 if (mask) g_object_unref (mask);
3019 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3022 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
3028 width = tree_view->priv->expander_size;
3030 /* Get x, y, width, height of arrow */
3031 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
3032 if (reorder->left_column)
3034 x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
3035 height = reorder->left_column->allocation.height;
3039 x += reorder->right_column->allocation.x - width/2;
3040 height = reorder->right_column->allocation.height;
3042 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3043 height += tree_view->priv->expander_size;
3045 /* Create the new window */
3046 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3048 if (tree_view->priv->drag_highlight_window)
3050 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3052 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3055 attributes.window_type = GDK_WINDOW_TEMP;
3056 attributes.wclass = GDK_INPUT_OUTPUT;
3057 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3058 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3059 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3060 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3063 attributes.width = width;
3064 attributes.height = height;
3065 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3066 &attributes, attributes_mask);
3067 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3069 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3070 gc = gdk_gc_new (mask);
3072 gdk_gc_set_foreground (gc, &col);
3073 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3075 /* Draw the 2 arrows as per above */
3077 gdk_gc_set_foreground (gc, &col);
3078 for (i = 0; i < width; i ++)
3080 if (i == (width/2 - 1))
3082 gdk_draw_line (mask, gc, i, j, i, height - j);
3083 if (i < (width/2 - 1))
3088 g_object_unref (gc);
3089 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3091 if (mask) g_object_unref (mask);
3094 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3095 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3097 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3098 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3104 width = tree_view->priv->expander_size;
3106 /* Get x, y, width, height of arrow */
3107 width = width/2; /* remember, the arrow only takes half the available width */
3108 gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
3109 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3110 x += widget->allocation.width - width;
3112 if (reorder->left_column)
3113 height = reorder->left_column->allocation.height;
3115 height = reorder->right_column->allocation.height;
3117 y -= tree_view->priv->expander_size;
3118 height += 2*tree_view->priv->expander_size;
3120 /* Create the new window */
3121 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3122 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3124 if (tree_view->priv->drag_highlight_window)
3126 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3128 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3131 attributes.window_type = GDK_WINDOW_TEMP;
3132 attributes.wclass = GDK_INPUT_OUTPUT;
3133 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3134 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3135 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3136 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3139 attributes.width = width;
3140 attributes.height = height;
3141 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3142 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3144 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3145 gc = gdk_gc_new (mask);
3147 gdk_gc_set_foreground (gc, &col);
3148 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3150 /* Draw the 2 arrows as per above */
3152 gdk_gc_set_foreground (gc, &col);
3153 j = tree_view->priv->expander_size;
3154 for (i = 0; i < width; i ++)
3157 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3161 gdk_draw_line (mask, gc, k, j, k, height - j);
3162 gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3163 gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3166 g_object_unref (gc);
3167 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3169 if (mask) g_object_unref (mask);
3172 tree_view->priv->drag_column_window_state = arrow_type;
3173 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3177 g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3178 gdk_window_hide (tree_view->priv->drag_highlight_window);
3182 gdk_window_show (tree_view->priv->drag_highlight_window);
3183 gdk_window_raise (tree_view->priv->drag_highlight_window);
3188 pspp_sheet_view_motion_resize_column (GtkWidget *widget,
3189 GdkEventMotion *event)
3193 PsppSheetViewColumn *column;
3194 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3196 column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3198 if (event->is_hint || event->window != gtk_widget_get_window (widget))
3199 gtk_widget_get_pointer (widget, &x, NULL);
3203 if (tree_view->priv->hadjustment)
3204 x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
3206 new_width = pspp_sheet_view_new_column_width (tree_view,
3207 tree_view->priv->drag_pos, &x);
3208 if (x != tree_view->priv->x_drag &&
3209 (new_width != column->fixed_width))
3211 column->use_resized_width = TRUE;
3212 column->resized_width = new_width;
3215 column->resized_width -= tree_view->priv->last_extra_space_per_column;
3217 gtk_widget_queue_resize (widget);
3225 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3227 PsppSheetViewColumnReorder *reorder = NULL;
3231 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3232 for (list = tree_view->priv->column_drag_info; list; list = list->next)
3234 reorder = (PsppSheetViewColumnReorder *) list->data;
3235 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3240 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3243 tree_view->priv->cur_reorder = reorder;
3244 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3248 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3250 GdkRectangle visible_rect;
3255 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3256 y += tree_view->priv->dy;
3258 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3260 /* see if we are near the edge. */
3261 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3264 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3269 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->vadjustment) + offset, 0.0,
3270 gtk_adjustment_get_upper (tree_view->priv->vadjustment) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
3271 gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3275 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3277 GdkRectangle visible_rect;
3282 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3284 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3286 /* See if we are near the edge. */
3287 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3290 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3296 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) + offset,
3297 0.0, gtk_adjustment_get_upper (tree_view->priv->hadjustment) - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
3298 gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3305 pspp_sheet_view_motion_drag_column (GtkWidget *widget,
3306 GdkEventMotion *event)
3308 PsppSheetView *tree_view = (PsppSheetView *) widget;
3309 PsppSheetViewColumn *column = tree_view->priv->drag_column;
3311 GtkAllocation allocation;
3314 if ((column == NULL) ||
3315 (event->window != tree_view->priv->drag_window))
3318 /* Handle moving the header */
3319 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3320 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
3321 x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3322 MAX (tree_view->priv->width, allocation.width) - column->allocation.width);
3323 gdk_window_move (tree_view->priv->drag_window, x, y);
3325 /* autoscroll, if needed */
3326 pspp_sheet_view_horizontal_autoscroll (tree_view);
3327 /* Update the current reorder position and arrow; */
3328 pspp_sheet_view_update_current_reorder (tree_view);
3334 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3336 remove_scroll_timeout (tree_view);
3337 gtk_grab_remove (GTK_WIDGET (tree_view));
3339 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3341 GtkTreePath *tmp_path;
3343 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3345 /* The anchor path should be set to the start path */
3346 tmp_path = _pspp_sheet_view_find_path (tree_view,
3347 tree_view->priv->rubber_band_start_node);
3349 if (tree_view->priv->anchor)
3350 gtk_tree_row_reference_free (tree_view->priv->anchor);
3352 tree_view->priv->anchor =
3353 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3354 tree_view->priv->model,
3357 gtk_tree_path_free (tmp_path);
3359 /* ... and the cursor to the end path */
3360 tmp_path = _pspp_sheet_view_find_path (tree_view,
3361 tree_view->priv->rubber_band_end_node);
3362 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE, 0); /* XXX mode? */
3363 gtk_tree_path_free (tmp_path);
3365 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3368 /* Clear status variables */
3369 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3370 tree_view->priv->rubber_band_shift = 0;
3371 tree_view->priv->rubber_band_ctrl = 0;
3373 tree_view->priv->rubber_band_start_node = -1;
3374 tree_view->priv->rubber_band_end_node = -1;
3378 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3382 gboolean skip_start,
3385 if (start_node == end_node)
3388 /* We skip the first node and jump inside the loop */
3394 /* Small optimization by assuming insensitive nodes are never
3399 if (tree_view->priv->rubber_band_shift)
3400 pspp_sheet_view_node_select (tree_view, start_node);
3401 else if (tree_view->priv->rubber_band_ctrl)
3403 /* Toggle the selection state */
3404 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3405 pspp_sheet_view_node_unselect (tree_view, start_node);
3407 pspp_sheet_view_node_select (tree_view, start_node);
3410 pspp_sheet_view_node_select (tree_view, start_node);
3414 /* Mirror the above */
3415 if (tree_view->priv->rubber_band_shift)
3416 pspp_sheet_view_node_unselect (tree_view, start_node);
3417 else if (tree_view->priv->rubber_band_ctrl)
3419 /* Toggle the selection state */
3420 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3421 pspp_sheet_view_node_unselect (tree_view, start_node);
3423 pspp_sheet_view_node_select (tree_view, start_node);
3426 pspp_sheet_view_node_unselect (tree_view, start_node);
3429 _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3431 if (start_node == end_node)
3436 start_node = pspp_sheet_view_node_next (tree_view, start_node);
3439 /* Ran out of tree */
3442 if (skip_end && start_node == end_node)
3449 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3452 return node * tree_view->priv->fixed_height;
3456 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3460 int fixed_height = tree_view->priv->fixed_height;
3461 if (fixed_height <= 0
3463 || height >= tree_view->priv->row_count * fixed_height)
3470 *new_node = height / fixed_height;
3471 return height % fixed_height;
3476 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3481 pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3482 pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3484 /* Handle the start area first */
3485 if (tree_view->priv->rubber_band_start_node < 0)
3487 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3494 else if (start_node < tree_view->priv->rubber_band_start_node)
3496 /* New node is above the old one; selection became bigger */
3497 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3499 tree_view->priv->rubber_band_start_node,
3504 else if (start_node > tree_view->priv->rubber_band_start_node)
3506 /* New node is below the old one; selection became smaller */
3507 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3508 tree_view->priv->rubber_band_start_node,
3515 tree_view->priv->rubber_band_start_node = start_node;
3517 /* Next, handle the end area */
3518 if (tree_view->priv->rubber_band_end_node < 0)
3520 /* In the event this happens, start_node was also -1; this case is
3524 else if (end_node < 0)
3526 /* Find the last node in the tree */
3527 pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3530 /* Selection reached end of the tree */
3531 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3532 tree_view->priv->rubber_band_end_node,
3538 else if (end_node > tree_view->priv->rubber_band_end_node)
3540 /* New node is below the old one; selection became bigger */
3541 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3542 tree_view->priv->rubber_band_end_node,
3548 else if (end_node < tree_view->priv->rubber_band_end_node)
3550 /* New node is above the old one; selection became smaller */
3551 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3553 tree_view->priv->rubber_band_end_node,
3559 tree_view->priv->rubber_band_end_node = end_node;
3562 #define GDK_RECTANGLE_PTR(X) ((GdkRectangle *)(X))
3565 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3568 cairo_rectangle_int_t old_area;
3569 cairo_rectangle_int_t new_area;
3570 cairo_rectangle_int_t common;
3571 cairo_region_t *invalid_region;
3572 PsppSheetViewColumn *column;
3574 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3575 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3576 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3577 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3579 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3582 y = MAX (y, 0) + tree_view->priv->dy;
3584 new_area.x = MIN (tree_view->priv->press_start_x, x);
3585 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3586 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3587 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3589 invalid_region = cairo_region_create_rectangle (&old_area);
3590 cairo_region_union_rectangle (invalid_region, &new_area);
3592 gdk_rectangle_intersect (GDK_RECTANGLE_PTR (&old_area),
3593 GDK_RECTANGLE_PTR (&new_area), GDK_RECTANGLE_PTR (&common));
3594 if (common.width > 2 && common.height > 2)
3596 cairo_region_t *common_region;
3598 /* make sure the border is invalidated */
3604 common_region = cairo_region_create_rectangle (&common);
3606 cairo_region_subtract (invalid_region, common_region);
3607 cairo_region_destroy (common_region);
3610 #if GTK_MAJOR_VERSION == 3
3611 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
3614 cairo_rectangle_int_t extents;
3616 cairo_region_get_extents (invalid_region, &extents);
3617 ereg = gdk_region_rectangle (GDK_RECTANGLE_PTR (&extents));
3618 gdk_window_invalidate_region (tree_view->priv->bin_window, ereg, TRUE);
3619 gdk_region_destroy (ereg);
3623 cairo_region_destroy (invalid_region);
3625 tree_view->priv->rubber_band_x = x;
3626 tree_view->priv->rubber_band_y = y;
3627 pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3629 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3630 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3631 tree_view->priv->anchor_column,
3634 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3636 pspp_sheet_view_update_rubber_band_selection (tree_view);
3641 pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view,
3646 GdkRectangle rubber_rect;
3650 rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3651 rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3652 rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3653 rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3655 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3658 cr = gdk_cairo_create (tree_view->priv->bin_window);
3659 cairo_set_line_width (cr, 1.0);
3661 style = gtk_widget_get_style (GTK_WIDGET (tree_view));
3662 cairo_set_source_rgba (cr,
3663 style->fg[GTK_STATE_NORMAL].red / 65535.,
3664 style->fg[GTK_STATE_NORMAL].green / 65535.,
3665 style->fg[GTK_STATE_NORMAL].blue / 65535.,
3668 gdk_cairo_rectangle (cr, &rect);
3672 cairo_set_source_rgb (cr,
3673 style->fg[GTK_STATE_NORMAL].red / 65535.,
3674 style->fg[GTK_STATE_NORMAL].green / 65535.,
3675 style->fg[GTK_STATE_NORMAL].blue / 65535.);
3677 cairo_rectangle (cr,
3678 rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3679 rubber_rect.width - 1, rubber_rect.height - 1);
3688 pspp_sheet_view_motion_bin_window (GtkWidget *widget,
3689 GdkEventMotion *event)
3691 PsppSheetView *tree_view;
3695 tree_view = (PsppSheetView *) widget;
3697 if (tree_view->priv->row_count == 0)
3700 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3702 GdkRectangle background_area, cell_area;
3703 PsppSheetViewColumn *column;
3705 if (find_click (tree_view, event->x, event->y, &node, &column,
3706 &background_area, &cell_area)
3707 && tree_view->priv->focus_column == column
3708 && tree_view->priv->press_start_node == node)
3711 gtk_grab_add (GTK_WIDGET (tree_view));
3712 pspp_sheet_view_update_rubber_band (tree_view);
3714 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3716 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3718 pspp_sheet_view_update_rubber_band (tree_view);
3720 add_scroll_timeout (tree_view);
3723 /* only check for an initiated drag when a button is pressed */
3724 if (tree_view->priv->pressed_button >= 0
3725 && !tree_view->priv->rubber_band_status)
3726 pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3728 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3732 pspp_sheet_view_find_offset (tree_view, new_y, &node);
3734 tree_view->priv->event_last_x = event->x;
3735 tree_view->priv->event_last_y = event->y;
3737 prelight_or_select (tree_view, node, event->x, event->y);
3743 pspp_sheet_view_motion (GtkWidget *widget,
3744 GdkEventMotion *event)
3746 PsppSheetView *tree_view;
3748 tree_view = (PsppSheetView *) widget;
3750 /* Resizing a column */
3751 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3752 return pspp_sheet_view_motion_resize_column (widget, event);
3755 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3756 return pspp_sheet_view_motion_drag_column (widget, event);
3758 /* Sanity check it */
3759 if (event->window == tree_view->priv->bin_window)
3760 return pspp_sheet_view_motion_bin_window (widget, event);
3765 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3766 * the tree is empty.
3769 invalidate_empty_focus (PsppSheetView *tree_view)
3773 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3778 area.width = gdk_window_get_width (tree_view->priv->bin_window);
3779 area.height = gdk_window_get_height (tree_view->priv->bin_window);
3780 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3783 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3787 draw_empty_focus (PsppSheetView *tree_view)
3789 GtkWidget *widget = GTK_WIDGET (tree_view);
3791 cairo_t *cr = gdk_cairo_create (tree_view->priv->bin_window);
3793 if (!gtk_widget_has_focus (widget))
3796 w = gdk_window_get_width (tree_view->priv->bin_window);
3797 h = gdk_window_get_height (tree_view->priv->bin_window);
3803 gtk_paint_focus (gtk_widget_get_style (widget),
3805 gtk_widget_get_state (widget),
3813 pspp_sheet_view_draw_vertical_grid_lines (PsppSheetView *tree_view,
3815 gint n_visible_columns,
3819 GList *list = tree_view->priv->columns;
3823 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3824 && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3827 /* Only draw the lines for visible rows and columns */
3828 for (list = tree_view->priv->columns; list; list = list->next, i++)
3830 PsppSheetViewColumn *column = list->data;
3833 if (! column->visible)
3836 current_x += column->width;
3838 /* Generally the grid lines should fit within the column, but for the
3839 last visible column we put it just past the end of the column.
3840 (Otherwise horizontal grid lines sometimes stick out by one pixel.) */
3842 if (i != n_visible_columns - 1)
3845 cairo_set_line_width (cr, 1.0);
3846 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
3847 cairo_move_to (cr, x + 0.5, min_y);
3848 cairo_line_to (cr, x + 0.5, max_y - min_y);
3853 /* Warning: Very scary function.
3854 * Modify at your own risk
3856 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3857 * FIXME: It's not...
3860 pspp_sheet_view_bin_expose (GtkWidget *widget,
3863 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3868 int drag_highlight = -1;
3871 gint y_offset, cell_offset;
3873 GdkRectangle background_area;
3874 GdkRectangle cell_area;
3876 gint bin_window_width;
3877 gint bin_window_height;
3878 GtkTreePath *cursor_path;
3879 GtkTreePath *drag_dest_path;
3880 GList *first_column, *last_column;
3881 gint vertical_separator;
3882 gint horizontal_separator;
3883 gint focus_line_width;
3884 gboolean allow_rules;
3885 gboolean has_special_cell;
3887 gint n_visible_columns;
3888 gint grid_line_width;
3889 gboolean row_ending_details;
3890 gboolean draw_vgrid_lines, draw_hgrid_lines;
3894 GtkAllocation allocation;
3895 gtk_widget_get_allocation (widget, &allocation);
3897 GdkRectangle exposed_rect;
3898 gdk_cairo_get_clip_rectangle (cr, &exposed_rect);
3902 Zarea.height = allocation.height;
3904 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3906 gtk_widget_style_get (widget,
3907 "horizontal-separator", &horizontal_separator,
3908 "vertical-separator", &vertical_separator,
3909 "allow-rules", &allow_rules,
3910 "focus-line-width", &focus_line_width,
3911 "row-ending-details", &row_ending_details,
3914 if (tree_view->priv->row_count == 0)
3916 draw_empty_focus (tree_view);
3921 /* clip event->area to the visible area */
3922 if (Zarea.height < 0.5)
3926 validate_visible_area (tree_view);
3928 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, Zarea.y);
3932 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3934 gdk_window_get_width (tree_view->priv->bin_window);
3937 gdk_window_get_height (tree_view->priv->bin_window);
3940 if (tree_view->priv->height < bin_window_height)
3942 gtk_paint_flat_box (gtk_widget_get_style (widget),
3944 gtk_widget_get_state (widget),
3948 0, tree_view->priv->height,
3950 bin_window_height - tree_view->priv->height);
3956 /* find the path for the node */
3957 path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3958 gtk_tree_model_get_iter (tree_view->priv->model,
3961 gtk_tree_path_free (path);
3964 drag_dest_path = NULL;
3966 if (tree_view->priv->cursor)
3967 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3970 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3972 if (tree_view->priv->drag_dest_row)
3973 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3976 _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3980 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3981 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3983 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
3984 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3986 if (draw_vgrid_lines || draw_hgrid_lines)
3987 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
3989 n_visible_columns = 0;
3990 for (list = tree_view->priv->columns; list; list = list->next)
3992 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
3994 n_visible_columns ++;
3997 /* Find the last column */
3998 for (last_column = g_list_last (tree_view->priv->columns);
3999 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
4000 last_column = last_column->prev)
4004 for (first_column = g_list_first (tree_view->priv->columns);
4005 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
4006 first_column = first_column->next)
4009 /* Actually process the expose event. To do this, we want to
4010 * start at the first node of the event, and walk the tree in
4011 * order, drawing each successive node.
4018 gboolean is_first = FALSE;
4019 gboolean is_last = FALSE;
4020 gboolean done = FALSE;
4023 max_height = ROW_HEIGHT (tree_view);
4027 background_area.y = y_offset + Zarea.y;
4028 background_area.height = max_height;
4029 max_y = background_area.y + max_height;
4033 if (node == tree_view->priv->prelight_node)
4034 flags |= GTK_CELL_RENDERER_PRELIT;
4036 selected = pspp_sheet_view_node_is_selected (tree_view, node);
4040 if (tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
4042 /* we *need* to set cell data on all cells before the call
4043 * to _has_special_cell, else _has_special_cell() does not
4044 * return a correct value.
4046 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4048 list = (rtl ? list->prev : list->next))
4050 PsppSheetViewColumn *column = list->data;
4051 pspp_sheet_view_column_cell_set_cell_data (column,
4052 tree_view->priv->model,
4056 has_special_cell = pspp_sheet_view_has_special_cell (tree_view);
4059 has_special_cell = tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
4061 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4063 list = (rtl ? list->prev : list->next))
4065 PsppSheetViewColumn *column = list->data;
4066 const gchar *detail = NULL;
4067 gboolean selected_column;
4070 if (!column->visible)
4073 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
4074 selected_column = column->selected && column->selectable;
4076 selected_column = TRUE;
4079 if (cell_offset > Zarea.x + Zarea.width ||
4080 cell_offset + column->width < Zarea.x)
4082 cell_offset += column->width;
4087 if (selected && selected_column)
4088 flags |= GTK_CELL_RENDERER_SELECTED;
4090 flags &= ~GTK_CELL_RENDERER_SELECTED;
4092 if (column->show_sort_indicator)
4093 flags |= GTK_CELL_RENDERER_SORTED;
4095 flags &= ~GTK_CELL_RENDERER_SORTED;
4098 flags |= GTK_CELL_RENDERER_FOCUSED;
4100 flags &= ~GTK_CELL_RENDERER_FOCUSED;
4102 background_area.x = cell_offset;
4103 background_area.width = column->width;
4105 cell_area = background_area;
4106 cell_area.y += vertical_separator / 2;
4107 cell_area.x += horizontal_separator / 2;
4108 cell_area.height -= vertical_separator;
4109 cell_area.width -= horizontal_separator;
4111 if (draw_vgrid_lines)
4113 if (list == first_column)
4115 cell_area.width -= grid_line_width / 2;
4117 else if (list == last_column)
4119 cell_area.x += grid_line_width / 2;
4120 cell_area.width -= grid_line_width / 2;
4124 cell_area.x += grid_line_width / 2;
4125 cell_area.width -= grid_line_width;
4129 if (draw_hgrid_lines)
4131 cell_area.y += grid_line_width / 2;
4132 cell_area.height -= grid_line_width;
4136 if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4138 if (!gdk_rectangle_intersect (&background_area, &exposed_rect, NULL))
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;
6717 if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view)) != focus_column->button)
6718 gtk_widget_grab_focus (focus_column->button);
6720 if (clamp_column_visible)
6721 pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6724 /* Returns TRUE if the focus is within the headers, after the focus operation is
6728 pspp_sheet_view_header_focus (PsppSheetView *tree_view,
6729 GtkDirectionType dir,
6730 gboolean clamp_column_visible)
6732 GtkWidget *focus_child;
6733 PsppSheetViewColumn *focus_column;
6734 GList *last_column, *first_column;
6738 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6741 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
6743 first_column = tree_view->priv->columns;
6744 while (first_column)
6746 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6748 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6750 first_column = first_column->next;
6753 /* No headers are visible, or are focusable. We can't focus in or out.
6755 if (first_column == NULL)
6758 last_column = g_list_last (tree_view->priv->columns);
6761 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6763 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6765 last_column = last_column->prev;
6769 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6773 case GTK_DIR_TAB_BACKWARD:
6774 case GTK_DIR_TAB_FORWARD:
6777 if (focus_child == NULL)
6779 if (tree_view->priv->focus_column != NULL &&
6780 pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6781 focus_column = tree_view->priv->focus_column;
6783 focus_column = first_column->data;
6784 pspp_sheet_view_focus_column (tree_view, focus_column,
6785 clamp_column_visible);
6792 if (focus_child == NULL)
6794 if (tree_view->priv->focus_column != NULL)
6795 focus_column = tree_view->priv->focus_column;
6796 else if (dir == GTK_DIR_LEFT)
6797 focus_column = last_column->data;
6799 focus_column = first_column->data;
6800 pspp_sheet_view_focus_column (tree_view, focus_column,
6801 clamp_column_visible);
6805 if (gtk_widget_child_focus (focus_child, dir))
6807 /* The focus moves inside the button. */
6808 /* This is probably a great example of bad UI */
6809 if (clamp_column_visible)
6810 pspp_sheet_view_clamp_column_visible (tree_view,
6811 tree_view->priv->focus_column,
6816 /* We need to move the focus among the row of buttons. */
6817 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6818 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6821 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6822 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6824 gtk_widget_error_bell (GTK_WIDGET (tree_view));
6830 PsppSheetViewColumn *column;
6832 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6833 tmp_list = tmp_list->next;
6835 tmp_list = tmp_list->prev;
6837 if (tmp_list == NULL)
6839 g_warning ("Internal button not found");
6842 column = tmp_list->data;
6843 if (column->visible &&
6844 pspp_sheet_view_column_can_focus (column))
6848 pspp_sheet_view_focus_column (tree_view, column,
6849 clamp_column_visible);
6857 g_assert_not_reached ();
6864 /* This function returns in 'path' the first focusable path, if the given path
6865 * is already focusable, it's the returned one.
6869 search_first_focusable_path (PsppSheetView *tree_view,
6871 gboolean search_forward,
6874 /* XXX this function is trivial given that the sheetview doesn't support
6878 if (!path || !*path)
6881 _pspp_sheet_view_find_node (tree_view, *path, &node);
6889 return (*path != NULL);
6893 pspp_sheet_view_focus (GtkWidget *widget,
6894 GtkDirectionType direction)
6896 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6897 GtkContainer *container = GTK_CONTAINER (widget);
6898 GtkWidget *focus_child;
6900 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6903 focus_child = gtk_container_get_focus_child (container);
6905 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6906 /* Case 1. Headers currently have focus. */
6913 pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6915 case GTK_DIR_TAB_BACKWARD:
6918 case GTK_DIR_TAB_FORWARD:
6920 gtk_widget_grab_focus (widget);
6923 g_assert_not_reached ();
6928 /* Case 2. We don't have focus at all. */
6929 if (!gtk_widget_has_focus (widget))
6931 if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6932 gtk_widget_grab_focus (widget);
6936 /* Case 3. We have focus already. */
6937 if (direction == GTK_DIR_TAB_BACKWARD)
6938 return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6939 else if (direction == GTK_DIR_TAB_FORWARD)
6942 /* Other directions caught by the keybindings */
6943 gtk_widget_grab_focus (widget);
6948 pspp_sheet_view_grab_focus (GtkWidget *widget)
6950 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6952 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6956 pspp_sheet_view_style_set (GtkWidget *widget,
6957 GtkStyle *previous_style)
6959 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6961 PsppSheetViewColumn *column;
6963 if (gtk_widget_get_realized (widget))
6965 gdk_window_set_background (tree_view->priv->bin_window, >k_widget_get_style (widget)->base[gtk_widget_get_state (widget)]);
6966 gtk_style_set_background (gtk_widget_get_style (widget), tree_view->priv->header_window, GTK_STATE_NORMAL);
6967 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6970 gtk_widget_style_get (widget,
6971 "expander-size", &tree_view->priv->expander_size,
6973 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6975 for (list = tree_view->priv->columns; list; list = list->next)
6977 column = list->data;
6978 _pspp_sheet_view_column_cell_set_dirty (column);
6981 tree_view->priv->fixed_height = -1;
6983 /* Invalidate cached button style. */
6984 if (tree_view->priv->button_style)
6986 g_object_unref (tree_view->priv->button_style);
6987 tree_view->priv->button_style = NULL;
6990 gtk_widget_queue_resize (widget);
6995 pspp_sheet_view_set_focus_child (GtkContainer *container,
6998 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
7001 for (list = tree_view->priv->columns; list; list = list->next)
7003 if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
7005 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7010 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
7014 pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
7015 GtkAdjustment *hadj,
7016 GtkAdjustment *vadj)
7018 gboolean need_adjust = FALSE;
7020 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7023 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
7025 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7027 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
7029 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7031 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
7033 g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
7034 pspp_sheet_view_adjustment_changed,
7036 g_object_unref (tree_view->priv->hadjustment);
7039 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
7041 g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
7042 pspp_sheet_view_adjustment_changed,
7044 g_object_unref (tree_view->priv->vadjustment);
7047 if (tree_view->priv->hadjustment != hadj)
7049 tree_view->priv->hadjustment = hadj;
7050 g_object_ref_sink (tree_view->priv->hadjustment);
7052 g_signal_connect (tree_view->priv->hadjustment, "value-changed",
7053 G_CALLBACK (pspp_sheet_view_adjustment_changed),
7058 if (tree_view->priv->vadjustment != vadj)
7060 tree_view->priv->vadjustment = vadj;
7061 g_object_ref_sink (tree_view->priv->vadjustment);
7063 g_signal_connect (tree_view->priv->vadjustment, "value-changed",
7064 G_CALLBACK (pspp_sheet_view_adjustment_changed),
7070 pspp_sheet_view_adjustment_changed (NULL, tree_view);
7075 pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
7076 GtkMovementStep step,
7079 PsppSheetSelectMode mode;
7080 GdkModifierType state;
7082 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
7083 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
7084 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
7085 step == GTK_MOVEMENT_DISPLAY_LINES ||
7086 step == GTK_MOVEMENT_PAGES ||
7087 step == GTK_MOVEMENT_BUFFER_ENDS ||
7088 step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
7090 if (tree_view->priv->row_count == 0)
7092 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7095 pspp_sheet_view_stop_editing (tree_view, FALSE);
7096 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7097 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7100 if (gtk_get_current_event_state (&state))
7102 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7103 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
7104 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
7105 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
7107 /* else we assume not pressed */
7111 case GTK_MOVEMENT_LOGICAL_POSITIONS:
7112 pspp_sheet_view_move_cursor_tab (tree_view, count);
7114 case GTK_MOVEMENT_VISUAL_POSITIONS:
7115 pspp_sheet_view_move_cursor_left_right (tree_view, count, mode);
7117 case GTK_MOVEMENT_DISPLAY_LINES:
7118 pspp_sheet_view_move_cursor_up_down (tree_view, count, mode);
7120 case GTK_MOVEMENT_PAGES:
7121 pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode);
7123 case GTK_MOVEMENT_BUFFER_ENDS:
7124 pspp_sheet_view_move_cursor_start_end (tree_view, count, mode);
7126 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7127 pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode);
7130 g_assert_not_reached ();
7137 pspp_sheet_view_put (PsppSheetView *tree_view,
7138 GtkWidget *child_widget,
7139 /* in bin_window coordinates */
7145 PsppSheetViewChild *child;
7147 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7148 g_return_if_fail (GTK_IS_WIDGET (child_widget));
7150 child = g_slice_new (PsppSheetViewChild);
7152 child->widget = child_widget;
7155 child->width = width;
7156 child->height = height;
7158 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7160 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7161 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7163 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7167 _pspp_sheet_view_child_move_resize (PsppSheetView *tree_view,
7169 /* in tree coordinates */
7175 PsppSheetViewChild *child = NULL;
7177 GdkRectangle allocation;
7179 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7180 g_return_if_fail (GTK_IS_WIDGET (widget));
7182 for (list = tree_view->priv->children; list; list = list->next)
7184 if (((PsppSheetViewChild *)list->data)->widget == widget)
7193 allocation.x = child->x = x;
7194 allocation.y = child->y = y;
7195 allocation.width = child->width = width;
7196 allocation.height = child->height = height;
7198 if (gtk_widget_get_realized (widget))
7199 gtk_widget_size_allocate (widget, &allocation);
7203 /* TreeModel Callbacks
7207 pspp_sheet_view_row_changed (GtkTreeModel *model,
7212 PsppSheetView *tree_view = (PsppSheetView *)data;
7214 gboolean free_path = FALSE;
7215 GtkTreePath *cursor_path;
7217 g_return_if_fail (path != NULL || iter != NULL);
7219 if (tree_view->priv->cursor != NULL)
7220 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7224 if (tree_view->priv->edited_column &&
7225 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7226 pspp_sheet_view_stop_editing (tree_view, TRUE);
7228 if (cursor_path != NULL)
7229 gtk_tree_path_free (cursor_path);
7233 path = gtk_tree_model_get_path (model, iter);
7236 else if (iter == NULL)
7237 gtk_tree_model_get_iter (model, iter, path);
7239 _pspp_sheet_view_find_node (tree_view,
7245 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7246 pspp_sheet_view_node_queue_redraw (tree_view, node);
7250 gtk_tree_path_free (path);
7254 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7259 PsppSheetView *tree_view = (PsppSheetView *) data;
7262 gint height = tree_view->priv->fixed_height;
7263 gboolean free_path = FALSE;
7264 gboolean node_visible = TRUE;
7266 g_return_if_fail (path != NULL || iter != NULL);
7270 path = gtk_tree_model_get_path (model, iter);
7273 else if (iter == NULL)
7274 gtk_tree_model_get_iter (model, iter, path);
7276 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7278 /* Update all row-references */
7279 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7280 indices = gtk_tree_path_get_indices (path);
7281 tmpnode = indices[0];
7283 range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7287 if (node_visible && node_is_visible (tree_view, tmpnode))
7288 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7290 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7293 install_presize_handler (tree_view);
7295 gtk_tree_path_free (path);
7299 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7303 PsppSheetView *tree_view = (PsppSheetView *)data;
7306 g_return_if_fail (path != NULL);
7308 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7310 _pspp_sheet_view_find_node (tree_view, path, &node);
7315 range_tower_delete (tree_view->priv->selected, node, 1);
7317 /* Ensure we don't have a dangling pointer to a dead node */
7318 ensure_unprelighted (tree_view);
7320 /* Cancel editting if we've started */
7321 pspp_sheet_view_stop_editing (tree_view, TRUE);
7323 if (tree_view->priv->destroy_count_func)
7325 gint child_count = 0;
7326 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7329 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7331 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7333 gtk_tree_row_reference_free (tree_view->priv->top_row);
7334 tree_view->priv->top_row = NULL;
7337 install_scroll_sync_handler (tree_view);
7339 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7342 if (helper_data.changed)
7343 g_signal_emit_by_name (tree_view->priv->selection, "changed");
7348 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7349 GtkTreePath *parent,
7354 PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7357 /* XXX need to adjust selection */
7358 len = gtk_tree_model_iter_n_children (model, iter);
7363 gtk_tree_row_reference_reordered (G_OBJECT (data),
7368 if (gtk_tree_path_get_depth (parent) != 0)
7371 if (tree_view->priv->edited_column)
7372 pspp_sheet_view_stop_editing (tree_view, TRUE);
7374 /* we need to be unprelighted */
7375 ensure_unprelighted (tree_view);
7377 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7379 pspp_sheet_view_dy_to_top_row (tree_view);
7383 /* Internal tree functions
7388 pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view,
7389 PsppSheetViewColumn *column,
7393 PsppSheetViewColumn *tmp_column = NULL;
7404 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7407 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7409 list = (rtl ? list->prev : list->next))
7411 tmp_column = list->data;
7413 if (tmp_column == column)
7416 if (tmp_column->visible)
7417 total_width += tmp_column->width;
7420 if (tmp_column != column)
7422 g_warning (G_STRLOC": passed-in column isn't in the tree");
7431 if (column->visible)
7432 *x2 = total_width + column->width;
7434 *x2 = total_width; /* width of 0 */
7438 /* Make sure the node is visible vertically */
7440 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7443 gint node_dy, height;
7444 GtkTreePath *path = NULL;
7446 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7449 /* just return if the node is visible, avoiding a costly expose */
7450 node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7451 height = ROW_HEIGHT (tree_view);
7452 if (node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment)
7453 && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
7454 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
7457 path = _pspp_sheet_view_find_path (tree_view, node);
7460 /* We process updates because we want to clear old selected items when we scroll.
7461 * if this is removed, we get a "selection streak" at the bottom. */
7462 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7463 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7464 gtk_tree_path_free (path);
7469 pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
7470 PsppSheetViewColumn *column,
7471 gboolean focus_to_cell)
7478 x = column->allocation.x;
7479 width = column->allocation.width;
7481 if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7483 /* The column is larger than the horizontal page size. If the
7484 * column has cells which can be focussed individually, then we make
7485 * sure the cell which gets focus is fully visible (if even the
7486 * focus cell is bigger than the page size, we make sure the
7487 * left-hand side of the cell is visible).
7489 * If the column does not have those so-called special cells, we
7490 * make sure the left-hand side of the column is visible.
7493 if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7495 GtkTreePath *cursor_path;
7496 GdkRectangle background_area, cell_area, focus_area;
7498 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7500 pspp_sheet_view_get_cell_area (tree_view,
7501 cursor_path, column, &cell_area);
7502 pspp_sheet_view_get_background_area (tree_view,
7503 cursor_path, column,
7506 gtk_tree_path_free (cursor_path);
7508 _pspp_sheet_view_column_get_focus_area (column,
7514 width = focus_area.width;
7516 if (width < gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7518 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7519 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7520 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7521 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7522 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7526 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7528 gtk_adjustment_get_lower (tree_view->priv->hadjustment),
7529 gtk_adjustment_get_upper (tree_view->priv->hadjustment)
7530 - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)));
7534 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7535 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7536 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7537 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7538 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7543 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7548 path = gtk_tree_path_new ();
7550 gtk_tree_path_append_index (path, node);
7555 _pspp_sheet_view_find_node (PsppSheetView *tree_view,
7559 gint *indices = gtk_tree_path_get_indices (path);
7560 gint depth = gtk_tree_path_get_depth (path);
7563 if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7569 pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
7572 gboolean add_shifted_binding,
7573 GtkMovementStep step,
7577 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7582 if (add_shifted_binding)
7583 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7588 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7591 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7596 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7603 pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view,
7604 PsppSheetViewColumn *column)
7606 PsppSheetViewColumn *left_column;
7607 PsppSheetViewColumn *cur_column = NULL;
7608 PsppSheetViewColumnReorder *reorder;
7613 /* We want to precalculate the motion list such that we know what column slots
7617 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7619 /* First, identify all possible drop spots */
7621 tmp_list = g_list_last (tree_view->priv->columns);
7623 tmp_list = g_list_first (tree_view->priv->columns);
7627 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7628 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7630 if (cur_column->visible == FALSE)
7633 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7634 if (left_column != column && cur_column != column &&
7635 tree_view->priv->column_drop_func &&
7636 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7638 left_column = cur_column;
7641 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7642 reorder->left_column = left_column;
7643 left_column = reorder->right_column = cur_column;
7645 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7648 /* Add the last one */
7649 if (tree_view->priv->column_drop_func == NULL ||
7650 ((left_column != column) &&
7651 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7653 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7654 reorder->left_column = left_column;
7655 reorder->right_column = NULL;
7656 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7659 /* We quickly check to see if it even makes sense to reorder columns. */
7660 /* If there is nothing that can be moved, then we return */
7662 if (tree_view->priv->column_drag_info == NULL)
7665 /* We know there are always 2 slots possbile, as you can always return column. */
7666 /* If that's all there is, return */
7667 if (tree_view->priv->column_drag_info->next == NULL ||
7668 (tree_view->priv->column_drag_info->next->next == NULL &&
7669 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7670 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7672 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7673 g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7674 g_list_free (tree_view->priv->column_drag_info);
7675 tree_view->priv->column_drag_info = NULL;
7678 /* We fill in the ranges for the columns, now that we've isolated them */
7679 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7681 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7683 reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7685 reorder->left_align = left;
7686 if (tmp_list->next != NULL)
7688 g_assert (tmp_list->next->data);
7689 left = reorder->right_align = (reorder->right_column->allocation.x +
7690 reorder->right_column->allocation.width +
7691 ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7695 gint width = gdk_window_get_width (tree_view->priv->header_window);
7696 reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7702 _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view,
7703 PsppSheetViewColumn *column)
7705 GdkEvent *send_event;
7706 GtkAllocation allocation;
7708 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7709 GdkDisplay *display = gdk_screen_get_display (screen);
7711 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7712 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7713 g_return_if_fail (column->button);
7715 pspp_sheet_view_set_column_drag_info (tree_view, column);
7717 if (tree_view->priv->column_drag_info == NULL)
7720 if (tree_view->priv->drag_window == NULL)
7722 GdkWindowAttr attributes;
7723 guint attributes_mask;
7725 attributes.window_type = GDK_WINDOW_CHILD;
7726 attributes.wclass = GDK_INPUT_OUTPUT;
7727 attributes.x = column->allocation.x;
7729 attributes.width = column->allocation.width;
7730 attributes.height = column->allocation.height;
7731 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7732 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7733 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL ;
7735 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7738 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7741 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7742 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7744 gtk_grab_remove (column->button);
7746 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7747 send_event->crossing.send_event = TRUE;
7748 send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button)));
7749 send_event->crossing.subwindow = NULL;
7750 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7751 send_event->crossing.time = GDK_CURRENT_TIME;
7753 gtk_propagate_event (column->button, send_event);
7754 gdk_event_free (send_event);
7756 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7757 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7758 send_event->button.send_event = TRUE;
7759 send_event->button.time = GDK_CURRENT_TIME;
7760 send_event->button.x = -1;
7761 send_event->button.y = -1;
7762 send_event->button.axes = NULL;
7763 send_event->button.state = 0;
7764 send_event->button.button = 1;
7765 send_event->button.device =
7766 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
7768 send_event->button.x_root = 0;
7769 send_event->button.y_root = 0;
7771 gtk_propagate_event (column->button, send_event);
7772 gdk_event_free (send_event);
7774 /* Kids, don't try this at home */
7775 g_object_ref (column->button);
7776 gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7777 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7778 gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7779 g_object_unref (column->button);
7781 tree_view->priv->drag_column_x = column->allocation.x;
7782 allocation = column->allocation;
7784 gtk_widget_size_allocate (column->button, &allocation);
7785 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7787 tree_view->priv->drag_column = column;
7788 gdk_window_show (tree_view->priv->drag_window);
7790 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7792 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7793 while (gtk_events_pending ())
7794 gtk_main_iteration ();
7796 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7797 gdk_pointer_grab (tree_view->priv->drag_window,
7799 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7800 NULL, NULL, GDK_CURRENT_TIME);
7801 gdk_keyboard_grab (tree_view->priv->drag_window,
7807 _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view,
7809 const GdkRectangle *clip_rect)
7812 GtkAllocation allocation;
7814 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7817 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
7819 rect.width = MAX (tree_view->priv->width, allocation.width);
7821 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7822 rect.height = ROW_HEIGHT (tree_view);
7826 GdkRectangle new_rect;
7828 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7830 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7834 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7839 pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
7841 const GdkRectangle *clip_rect)
7845 _pspp_sheet_view_find_node (tree_view, path, &node);
7848 _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7852 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7855 GtkTreePath *cursor_path;
7857 if ((tree_view->priv->row_count == 0) ||
7858 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7862 if (tree_view->priv->cursor)
7863 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7865 if (cursor_path == NULL)
7867 /* There's no cursor. Move the cursor to the first selected row, if any
7868 * are selected, otherwise to the first row in the sheetview.
7870 GList *selected_rows;
7871 GtkTreeModel *model;
7872 PsppSheetSelection *selection;
7874 selection = pspp_sheet_view_get_selection (tree_view);
7875 selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7879 /* XXX we could avoid doing O(n) work to get this result */
7880 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7881 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7882 g_list_free (selected_rows);
7886 cursor_path = gtk_tree_path_new_first ();
7887 search_first_focusable_path (tree_view, &cursor_path,
7891 gtk_tree_row_reference_free (tree_view->priv->cursor);
7892 tree_view->priv->cursor = NULL;
7896 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7897 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7898 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0);
7900 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0);
7906 /* Now find a column for the cursor. */
7907 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7909 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7910 gtk_tree_path_free (cursor_path);
7912 if (tree_view->priv->focus_column == NULL)
7915 for (list = tree_view->priv->columns; list; list = list->next)
7917 if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7919 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7920 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7921 pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7931 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7933 PsppSheetSelectMode mode)
7935 gint selection_count;
7936 int cursor_node = -1;
7937 int new_cursor_node = -1;
7938 GtkTreePath *cursor_path = NULL;
7939 gboolean grab_focus = TRUE;
7941 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7945 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7946 /* FIXME: we lost the cursor; should we get the first? */
7949 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7950 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7952 if (cursor_node < 0)
7953 /* FIXME: we lost the cursor; should we get the first? */
7956 selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7958 if (selection_count == 0
7959 && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7960 && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
7962 /* Don't move the cursor, but just select the current node */
7963 new_cursor_node = cursor_node;
7968 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7970 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7973 gtk_tree_path_free (cursor_path);
7975 if (new_cursor_node)
7977 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7979 search_first_focusable_path (tree_view, &cursor_path,
7984 gtk_tree_path_free (cursor_path);
7988 * If the list has only one item and multi-selection is set then select
7989 * the row (if not yet selected).
7991 if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7992 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7993 new_cursor_node < 0)
7996 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7998 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
8000 if (new_cursor_node < 0
8001 && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
8003 new_cursor_node = cursor_node;
8007 new_cursor_node = -1;
8011 if (new_cursor_node >= 0)
8013 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
8014 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode);
8015 gtk_tree_path_free (cursor_path);
8019 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8021 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8023 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
8025 GTK_DIR_UP : GTK_DIR_DOWN))
8027 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8030 gtk_widget_child_focus (toplevel,
8032 GTK_DIR_TAB_BACKWARD :
8033 GTK_DIR_TAB_FORWARD);
8040 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8045 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8047 return new_cursor_node >= 0;
8051 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
8053 PsppSheetSelectMode mode)
8055 int cursor_node = -1;
8056 GtkTreePath *old_cursor_path = NULL;
8057 GtkTreePath *cursor_path = NULL;
8058 int start_cursor_node = -1;
8061 gint vertical_separator;
8063 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8066 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8067 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8069 /* This is sorta weird. Focus in should give us a cursor */
8072 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
8073 _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
8075 if (cursor_node < 0)
8077 /* FIXME: we lost the cursor. Should we try to get one? */
8078 gtk_tree_path_free (old_cursor_path);
8082 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8083 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
8084 y += tree_view->priv->cursor_offset;
8085 y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment);
8086 y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment), (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator);
8088 if (y >= tree_view->priv->height)
8089 y = tree_view->priv->height - 1;
8091 tree_view->priv->cursor_offset =
8092 pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
8094 if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
8096 cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
8097 tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
8100 y -= tree_view->priv->cursor_offset;
8101 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8103 start_cursor_node = cursor_node;
8105 if (! search_first_focusable_path (tree_view, &cursor_path,
8109 /* It looks like we reached the end of the view without finding
8110 * a focusable row. We will step backwards to find the last
8113 cursor_node = start_cursor_node;
8114 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8116 search_first_focusable_path (tree_view, &cursor_path,
8125 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8127 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode);
8130 pspp_sheet_view_scroll_to_point (tree_view, -1, y);
8131 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8132 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8134 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
8135 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8137 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8140 gtk_tree_path_free (old_cursor_path);
8141 gtk_tree_path_free (cursor_path);
8145 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8147 PsppSheetSelectMode mode)
8149 int cursor_node = -1;
8150 GtkTreePath *cursor_path = NULL;
8151 PsppSheetViewColumn *column;
8154 gboolean found_column = FALSE;
8157 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8159 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8162 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8163 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8167 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8168 if (cursor_node < 0)
8170 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8172 gtk_tree_path_free (cursor_path);
8175 gtk_tree_path_free (cursor_path);
8177 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8178 if (tree_view->priv->focus_column)
8180 for (; list; list = (rtl ? list->prev : list->next))
8182 if (list->data == tree_view->priv->focus_column)
8189 gboolean left, right;
8191 column = list->data;
8192 if (column->visible == FALSE || column->row_head)
8195 pspp_sheet_view_column_cell_set_cell_data (column,
8196 tree_view->priv->model,
8201 right = list->prev ? TRUE : FALSE;
8202 left = list->next ? TRUE : FALSE;
8206 left = list->prev ? TRUE : FALSE;
8207 right = list->next ? TRUE : FALSE;
8210 if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8212 tree_view->priv->focus_column = column;
8213 found_column = TRUE;
8218 list = rtl ? list->prev : list->next;
8220 list = rtl ? list->next : list->prev;
8225 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8226 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8227 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8231 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8234 pspp_sheet_view_clamp_column_visible (tree_view,
8235 tree_view->priv->focus_column, TRUE);
8239 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8241 PsppSheetSelectMode mode)
8243 int cursor_node = -1;
8244 GtkTreePath *cursor_path = NULL;
8245 PsppSheetViewColumn *column;
8246 PsppSheetViewColumn *found_column;
8251 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8253 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8256 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8257 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8261 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8262 if (cursor_node < 0)
8264 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8266 gtk_tree_path_free (cursor_path);
8269 gtk_tree_path_free (cursor_path);
8271 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8272 if (tree_view->priv->focus_column)
8274 for (; list; list = (rtl ? list->prev : list->next))
8276 if (list->data == tree_view->priv->focus_column)
8281 found_column = NULL;
8284 gboolean left, right;
8286 column = list->data;
8287 if (column->visible == FALSE || column->row_head)
8290 pspp_sheet_view_column_cell_set_cell_data (column,
8291 tree_view->priv->model,
8296 right = list->prev ? TRUE : FALSE;
8297 left = list->next ? TRUE : FALSE;
8301 left = list->prev ? TRUE : FALSE;
8302 right = list->next ? TRUE : FALSE;
8305 if (column->tabbable
8306 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8307 found_column = column;
8311 list = rtl ? list->prev : list->next;
8313 list = rtl ? list->next : list->prev;
8318 tree_view->priv->focus_column = found_column;
8319 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8320 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8321 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8324 pspp_sheet_view_clamp_column_visible (tree_view,
8325 tree_view->priv->focus_column, TRUE);
8329 try_move_cursor_tab (PsppSheetView *tree_view,
8330 gboolean start_at_focus_column,
8333 PsppSheetViewColumn *column;
8335 int cursor_node = -1;
8336 GtkTreePath *cursor_path = NULL;
8340 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8341 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8345 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8346 if (cursor_node < 0)
8348 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8350 gtk_tree_path_free (cursor_path);
8353 gtk_tree_path_free (cursor_path);
8355 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8356 if (start_at_focus_column)
8359 ? g_list_last (tree_view->priv->columns)
8360 : g_list_first (tree_view->priv->columns));
8361 if (tree_view->priv->focus_column)
8363 for (; list; list = (rtl ? list->prev : list->next))
8365 if (list->data == tree_view->priv->focus_column)
8372 list = (rtl ^ (count == 1)
8373 ? g_list_first (tree_view->priv->columns)
8374 : g_list_last (tree_view->priv->columns));
8379 gboolean left, right;
8381 column = list->data;
8382 if (column->visible == FALSE || !column->tabbable)
8385 pspp_sheet_view_column_cell_set_cell_data (column,
8386 tree_view->priv->model,
8391 right = list->prev ? TRUE : FALSE;
8392 left = list->next ? TRUE : FALSE;
8396 left = list->prev ? TRUE : FALSE;
8397 right = list->next ? TRUE : FALSE;
8400 if (column->tabbable
8401 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8403 tree_view->priv->focus_column = column;
8404 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8405 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8406 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8411 list = rtl ? list->prev : list->next;
8413 list = rtl ? list->next : list->prev;
8420 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8423 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8426 if (!try_move_cursor_tab (tree_view, TRUE, count))
8428 if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0)
8429 && !try_move_cursor_tab (tree_view, FALSE, count))
8430 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8433 pspp_sheet_view_clamp_column_visible (tree_view,
8434 tree_view->priv->focus_column, TRUE);
8438 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8440 PsppSheetSelectMode mode)
8444 GtkTreePath *old_path;
8446 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8449 g_return_if_fail (tree_view->priv->row_count > 0);
8451 pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8455 /* Now go forward to find the first focusable row. */
8456 path = _pspp_sheet_view_find_path (tree_view, 0);
8457 search_first_focusable_path (tree_view, &path,
8458 TRUE, &cursor_node);
8462 /* Now go backwards to find last focusable row. */
8463 path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8464 search_first_focusable_path (tree_view, &path,
8465 FALSE, &cursor_node);
8471 if (gtk_tree_path_compare (old_path, path))
8473 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
8474 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8478 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8482 gtk_tree_path_free (old_path);
8483 gtk_tree_path_free (path);
8487 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8489 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8492 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8493 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8496 pspp_sheet_selection_select_all (tree_view->priv->selection);
8502 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8504 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8507 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8508 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8511 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8517 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8518 gboolean start_editing,
8519 PsppSheetSelectMode mode)
8522 int cursor_node = -1;
8523 GtkTreePath *cursor_path = NULL;
8525 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8528 if (tree_view->priv->cursor)
8529 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8531 if (cursor_path == NULL)
8534 _pspp_sheet_view_find_node (tree_view, cursor_path,
8537 if (cursor_node < 0)
8539 gtk_tree_path_free (cursor_path);
8543 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing &&
8544 tree_view->priv->focus_column)
8546 if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8548 gtk_tree_path_free (cursor_path);
8553 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8559 /* We bail out if the original (tree, node) don't exist anymore after
8560 * handling the selection-changed callback. We do return TRUE because
8561 * the key press has been handled at this point.
8563 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8565 if (cursor_node != new_node)
8568 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8570 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8571 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8573 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8574 pspp_sheet_view_row_activated (tree_view, cursor_path,
8575 tree_view->priv->focus_column);
8577 gtk_tree_path_free (cursor_path);
8583 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8586 int cursor_node = -1;
8587 GtkTreePath *cursor_path = NULL;
8589 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8593 if (tree_view->priv->cursor)
8594 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8596 if (cursor_path == NULL)
8599 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8600 if (cursor_node < 0)
8602 gtk_tree_path_free (cursor_path);
8606 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8609 PSPP_SHEET_SELECT_MODE_TOGGLE,
8612 /* We bail out if the original (tree, node) don't exist anymore after
8613 * handling the selection-changed callback. We do return TRUE because
8614 * the key press has been handled at this point.
8616 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8618 if (cursor_node != new_node)
8621 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8623 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8624 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8625 gtk_tree_path_free (cursor_path);
8631 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8633 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8634 tree_view->priv->typeselect_flush_timeout = 0;
8639 /* Cut and paste from gtkwindow.c */
8641 send_focus_change (GtkWidget *widget,
8644 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8646 fevent->focus_change.type = GDK_FOCUS_CHANGE;
8647 fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
8648 fevent->focus_change.in = in;
8650 gtk_widget_send_focus_change (widget, fevent);
8651 gdk_event_free (fevent);
8655 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8657 GtkWidget *frame, *vbox, *toplevel;
8660 if (tree_view->priv->search_custom_entry_set)
8663 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8664 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8666 if (tree_view->priv->search_window != NULL)
8668 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8669 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8670 GTK_WINDOW (tree_view->priv->search_window));
8671 else if (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)))
8672 gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
8673 GTK_WINDOW (tree_view->priv->search_window));
8674 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8678 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8679 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8681 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8682 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8683 GTK_WINDOW (tree_view->priv->search_window));
8685 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8686 GDK_WINDOW_TYPE_HINT_UTILITY);
8687 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8688 g_signal_connect (tree_view->priv->search_window, "delete-event",
8689 G_CALLBACK (pspp_sheet_view_search_delete_event),
8691 g_signal_connect (tree_view->priv->search_window, "key-press-event",
8692 G_CALLBACK (pspp_sheet_view_search_key_press_event),
8694 g_signal_connect (tree_view->priv->search_window, "button-press-event",
8695 G_CALLBACK (pspp_sheet_view_search_button_press_event),
8697 g_signal_connect (tree_view->priv->search_window, "scroll-event",
8698 G_CALLBACK (pspp_sheet_view_search_scroll_event),
8701 frame = gtk_frame_new (NULL);
8702 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8703 gtk_widget_show (frame);
8704 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8706 vbox = gtk_vbox_new (FALSE, 0);
8707 gtk_widget_show (vbox);
8708 gtk_container_add (GTK_CONTAINER (frame), vbox);
8709 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8712 tree_view->priv->search_entry = gtk_entry_new ();
8713 gtk_widget_show (tree_view->priv->search_entry);
8714 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8715 G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8717 g_signal_connect (tree_view->priv->search_entry,
8718 "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8722 g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8724 G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8728 gtk_container_add (GTK_CONTAINER (vbox),
8729 tree_view->priv->search_entry);
8731 gtk_widget_realize (tree_view->priv->search_entry);
8734 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8735 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8738 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8739 gboolean keybinding)
8741 /* We only start interactive search if we have focus or the columns
8742 * have focus. If one of our children have focus, we don't want to
8746 gboolean found_focus = FALSE;
8747 GtkWidgetClass *entry_parent_class;
8749 if (!tree_view->priv->enable_search && !keybinding)
8752 if (tree_view->priv->search_custom_entry_set)
8755 if (tree_view->priv->search_window != NULL &&
8756 gtk_widget_get_visible (tree_view->priv->search_window))
8759 for (list = tree_view->priv->columns; list; list = list->next)
8761 PsppSheetViewColumn *column;
8763 column = list->data;
8764 if (! column->visible)
8767 if (column->button && gtk_widget_has_focus (column->button))
8774 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8780 if (tree_view->priv->search_column < 0)
8783 pspp_sheet_view_ensure_interactive_directory (tree_view);
8786 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8789 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8790 gtk_widget_show (tree_view->priv->search_window);
8791 if (tree_view->priv->search_entry_changed_id == 0)
8793 tree_view->priv->search_entry_changed_id =
8794 g_signal_connect (tree_view->priv->search_entry, "changed",
8795 G_CALLBACK (pspp_sheet_view_search_init),
8799 tree_view->priv->typeselect_flush_timeout =
8800 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8801 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8804 /* Grab focus will select all the text. We don't want that to happen, so we
8805 * call the parent instance and bypass the selection change. This is probably
8806 * really non-kosher. */
8807 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8808 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8810 /* send focus-in event */
8811 send_focus_change (tree_view->priv->search_entry, TRUE);
8813 /* search first matching iter */
8814 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8820 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8822 return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8825 /* this function returns the new width of the column being resized given
8826 * the column and x position of the cursor; the x cursor position is passed
8827 * in as a pointer and automagicly corrected if it's beyond min/max limits
8830 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8834 PsppSheetViewColumn *column;
8838 /* first translate the x position from gtk_widget_get_window (widget)
8839 * to clist->clist_window
8841 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8842 column = g_list_nth (tree_view->priv->columns, i)->data;
8843 width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8845 /* Clamp down the value */
8846 if (column->min_width == -1)
8847 width = MAX (column->button_request, width);
8849 width = MAX (column->min_width, width);
8850 if (column->max_width != -1)
8851 width = MIN (width, column->max_width);
8853 *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8859 /* FIXME this adjust_allocation is a big cut-and-paste from
8860 * GtkCList, needs to be some "official" way to do this
8870 /* The window to which gtk_widget_get_window (widget) is relative */
8871 #define ALLOCATION_WINDOW(widget) \
8872 (!gtk_widget_get_has_window (widget) ? \
8873 gtk_widget_get_window (widget) : \
8874 gdk_window_get_parent (gtk_widget_get_window (widget)))
8877 adjust_allocation_recurse (GtkWidget *widget,
8880 ScrollData *scroll_data = data;
8881 GtkAllocation allocation;
8882 gtk_widget_get_allocation (widget, &allocation);
8883 /* Need to really size allocate instead of just poking
8884 * into widget->allocation if the widget is not realized.
8885 * FIXME someone figure out why this was.
8887 if (!gtk_widget_get_realized (widget))
8889 if (gtk_widget_get_visible (widget))
8891 GdkRectangle tmp_rectangle = allocation;
8892 tmp_rectangle.x += scroll_data->dx;
8893 tmp_rectangle.y += scroll_data->dy;
8895 gtk_widget_size_allocate (widget, &tmp_rectangle);
8900 if (ALLOCATION_WINDOW (widget) == scroll_data->window)
8902 allocation.x += scroll_data->dx;
8903 allocation.y += scroll_data->dy;
8905 if (GTK_IS_CONTAINER (widget))
8906 gtk_container_forall (GTK_CONTAINER (widget),
8907 adjust_allocation_recurse,
8914 adjust_allocation (GtkWidget *widget,
8918 ScrollData scroll_data;
8920 if (gtk_widget_get_realized (widget))
8921 scroll_data.window = ALLOCATION_WINDOW (widget);
8923 scroll_data.window = NULL;
8925 scroll_data.dx = dx;
8926 scroll_data.dy = dy;
8928 adjust_allocation_recurse (widget, &scroll_data);
8932 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8936 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8937 PsppSheetView *tree_view)
8939 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8944 gdk_window_move (tree_view->priv->bin_window,
8945 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8946 TREE_VIEW_HEADER_HEIGHT (tree_view));
8947 gdk_window_move (tree_view->priv->header_window,
8948 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8950 dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8953 update_prelight (tree_view,
8954 tree_view->priv->event_last_x,
8955 tree_view->priv->event_last_y - dy);
8957 if (tree_view->priv->edited_column &&
8958 GTK_IS_WIDGET (tree_view->priv->edited_column->editable_widget))
8962 PsppSheetViewChild *child = NULL;
8964 widget = GTK_WIDGET (tree_view->priv->edited_column->editable_widget);
8965 adjust_allocation (widget, 0, dy);
8967 for (list = tree_view->priv->children; list; list = list->next)
8969 child = (PsppSheetViewChild *)list->data;
8970 if (child->widget == widget)
8978 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8980 if (tree_view->priv->dy != (int) gtk_adjustment_get_value (tree_view->priv->vadjustment))
8982 /* update our dy and top_row */
8983 tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8985 if (!tree_view->priv->in_top_row_to_dy)
8986 pspp_sheet_view_dy_to_top_row (tree_view);
8997 * pspp_sheet_view_new:
8999 * Creates a new #PsppSheetView widget.
9001 * Return value: A newly created #PsppSheetView widget.
9004 pspp_sheet_view_new (void)
9006 return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
9010 * pspp_sheet_view_new_with_model:
9011 * @model: the model.
9013 * Creates a new #PsppSheetView widget with the model initialized to @model.
9015 * Return value: A newly created #PsppSheetView widget.
9018 pspp_sheet_view_new_with_model (GtkTreeModel *model)
9020 return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
9027 * pspp_sheet_view_get_model:
9028 * @tree_view: a #PsppSheetView
9030 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
9033 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
9036 pspp_sheet_view_get_model (PsppSheetView *tree_view)
9038 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9040 return tree_view->priv->model;
9044 * pspp_sheet_view_set_model:
9045 * @tree_view: A #GtkTreeNode.
9046 * @model: (allow-none): The model.
9048 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
9049 * set, it will remove it before setting the new model. If @model is %NULL,
9050 * then it will unset the old model.
9053 pspp_sheet_view_set_model (PsppSheetView *tree_view,
9054 GtkTreeModel *model)
9056 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9057 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
9059 if (model == tree_view->priv->model)
9062 if (tree_view->priv->scroll_to_path)
9064 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9065 tree_view->priv->scroll_to_path = NULL;
9068 if (tree_view->priv->model)
9070 GList *tmplist = tree_view->priv->columns;
9072 if (tree_view->priv->selected)
9073 range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
9074 pspp_sheet_view_stop_editing (tree_view, TRUE);
9076 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9077 pspp_sheet_view_row_changed,
9079 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9080 pspp_sheet_view_row_inserted,
9082 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9083 pspp_sheet_view_row_deleted,
9085 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9086 pspp_sheet_view_rows_reordered,
9089 for (; tmplist; tmplist = tmplist->next)
9090 _pspp_sheet_view_column_unset_model (tmplist->data,
9091 tree_view->priv->model);
9093 tree_view->priv->prelight_node = -1;
9095 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
9096 tree_view->priv->drag_dest_row = NULL;
9097 gtk_tree_row_reference_free (tree_view->priv->cursor);
9098 tree_view->priv->cursor = NULL;
9099 gtk_tree_row_reference_free (tree_view->priv->anchor);
9100 tree_view->priv->anchor = NULL;
9101 gtk_tree_row_reference_free (tree_view->priv->top_row);
9102 tree_view->priv->top_row = NULL;
9103 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9104 tree_view->priv->scroll_to_path = NULL;
9106 tree_view->priv->scroll_to_column = NULL;
9108 g_object_unref (tree_view->priv->model);
9110 tree_view->priv->search_column = -1;
9111 tree_view->priv->fixed_height = -1;
9112 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
9113 tree_view->priv->last_button_x = -1;
9114 tree_view->priv->last_button_y = -1;
9117 tree_view->priv->model = model;
9119 if (tree_view->priv->model)
9123 if (tree_view->priv->search_column == -1)
9125 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
9127 GType type = gtk_tree_model_get_column_type (model, i);
9129 if (g_value_type_transformable (type, G_TYPE_STRING))
9131 tree_view->priv->search_column = i;
9137 g_object_ref (tree_view->priv->model);
9138 g_signal_connect (tree_view->priv->model,
9140 G_CALLBACK (pspp_sheet_view_row_changed),
9142 g_signal_connect (tree_view->priv->model,
9144 G_CALLBACK (pspp_sheet_view_row_inserted),
9146 g_signal_connect (tree_view->priv->model,
9148 G_CALLBACK (pspp_sheet_view_row_deleted),
9150 g_signal_connect (tree_view->priv->model,
9152 G_CALLBACK (pspp_sheet_view_rows_reordered),
9155 tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
9157 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
9158 install_presize_handler (tree_view);
9161 g_object_notify (G_OBJECT (tree_view), "model");
9163 if (tree_view->priv->selection)
9164 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
9166 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9167 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9171 * pspp_sheet_view_get_selection:
9172 * @tree_view: A #PsppSheetView.
9174 * Gets the #PsppSheetSelection associated with @tree_view.
9176 * Return value: A #PsppSheetSelection object.
9178 PsppSheetSelection *
9179 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
9181 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9183 return tree_view->priv->selection;
9187 * pspp_sheet_view_get_hadjustment:
9188 * @tree_view: A #PsppSheetView
9190 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
9192 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9196 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
9198 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9200 return pspp_sheet_view_do_get_hadjustment (tree_view);
9203 static GtkAdjustment *
9204 pspp_sheet_view_do_get_hadjustment (PsppSheetView *tree_view)
9206 return tree_view->priv->hadjustment;
9210 * pspp_sheet_view_set_hadjustment:
9211 * @tree_view: A #PsppSheetView
9212 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9214 * Sets the #GtkAdjustment for the current horizontal aspect.
9217 pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view,
9218 GtkAdjustment *adjustment)
9220 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9222 pspp_sheet_view_set_adjustments (tree_view,
9224 tree_view->priv->vadjustment);
9226 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9230 pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
9231 GtkAdjustment *adjustment)
9233 PsppSheetViewPrivate *priv = tree_view->priv;
9235 if (adjustment && priv->hadjustment == adjustment)
9238 if (priv->hadjustment != NULL)
9240 g_signal_handlers_disconnect_by_func (priv->hadjustment,
9241 pspp_sheet_view_adjustment_changed,
9243 g_object_unref (priv->hadjustment);
9246 if (adjustment == NULL)
9247 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9250 g_signal_connect (adjustment, "value-changed",
9251 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9252 priv->hadjustment = g_object_ref_sink (adjustment);
9253 /* FIXME: Adjustment should probably be populated here with fresh values, but
9254 * internal details are too complicated for me to decipher right now.
9256 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9258 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9262 * pspp_sheet_view_get_vadjustment:
9263 * @tree_view: A #PsppSheetView
9265 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9267 * Return value: (transfer none): A #GtkAdjustment object, or %NULL
9268 * if none is currently being used.
9270 * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
9273 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9275 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9277 return pspp_sheet_view_do_get_vadjustment (tree_view);
9280 static GtkAdjustment *
9281 pspp_sheet_view_do_get_vadjustment (PsppSheetView *tree_view)
9283 return tree_view->priv->vadjustment;
9287 * pspp_sheet_view_set_vadjustment:
9288 * @tree_view: A #PsppSheetView
9289 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9291 * Sets the #GtkAdjustment for the current vertical aspect.
9293 * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
9296 pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view,
9297 GtkAdjustment *adjustment)
9299 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9300 g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
9302 pspp_sheet_view_do_set_vadjustment (tree_view, adjustment);
9306 pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
9307 GtkAdjustment *adjustment)
9309 PsppSheetViewPrivate *priv = tree_view->priv;
9311 if (adjustment && priv->vadjustment == adjustment)
9314 if (priv->vadjustment != NULL)
9316 g_signal_handlers_disconnect_by_func (priv->vadjustment,
9317 pspp_sheet_view_adjustment_changed,
9319 g_object_unref (priv->vadjustment);
9322 if (adjustment == NULL)
9323 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9326 g_signal_connect (adjustment, "value-changed",
9327 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9328 priv->vadjustment = g_object_ref_sink (adjustment);
9329 /* FIXME: Adjustment should probably be populated here with fresh values, but
9330 * internal details are too complicated for me to decipher right now.
9332 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9333 g_object_notify (G_OBJECT (tree_view), "vadjustment");
9336 /* Column and header operations */
9339 * pspp_sheet_view_get_headers_visible:
9340 * @tree_view: A #PsppSheetView.
9342 * Returns %TRUE if the headers on the @tree_view are visible.
9344 * Return value: Whether the headers are visible or not.
9347 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9349 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9351 return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9355 * pspp_sheet_view_set_headers_visible:
9356 * @tree_view: A #PsppSheetView.
9357 * @headers_visible: %TRUE if the headers are visible
9359 * Sets the visibility state of the headers.
9362 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9363 gboolean headers_visible)
9367 PsppSheetViewColumn *column;
9368 GtkAllocation allocation;
9370 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9372 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9374 headers_visible = !! headers_visible;
9376 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9379 if (headers_visible)
9380 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9382 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9384 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9386 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9387 if (headers_visible)
9389 gdk_window_move_resize (tree_view->priv->bin_window, x, y + TREE_VIEW_HEADER_HEIGHT (tree_view),
9390 tree_view->priv->width, allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view));
9392 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9393 pspp_sheet_view_map_buttons (tree_view);
9397 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9399 for (list = tree_view->priv->columns; list; list = list->next)
9401 column = list->data;
9403 gtk_widget_unmap (column->button);
9405 gdk_window_hide (tree_view->priv->header_window);
9409 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view));
9410 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2);
9411 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
9412 gtk_adjustment_set_upper (tree_view->priv->vadjustment, tree_view->priv->height);
9413 gtk_adjustment_changed (tree_view->priv->vadjustment);
9415 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9417 g_object_notify (G_OBJECT (tree_view), "headers-visible");
9421 * pspp_sheet_view_columns_autosize:
9422 * @tree_view: A #PsppSheetView.
9424 * Resizes all columns to their optimal width. Only works after the
9425 * treeview has been realized.
9428 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9430 gboolean dirty = FALSE;
9432 PsppSheetViewColumn *column;
9434 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9436 for (list = tree_view->priv->columns; list; list = list->next)
9438 column = list->data;
9439 _pspp_sheet_view_column_cell_set_dirty (column);
9444 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9448 * pspp_sheet_view_set_headers_clickable:
9449 * @tree_view: A #PsppSheetView.
9450 * @setting: %TRUE if the columns are clickable.
9452 * Allow the column title buttons to be clicked.
9455 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9460 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9462 for (list = tree_view->priv->columns; list; list = list->next)
9463 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9465 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9470 * pspp_sheet_view_get_headers_clickable:
9471 * @tree_view: A #PsppSheetView.
9473 * Returns whether all header columns are clickable.
9475 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9480 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9484 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9486 for (list = tree_view->priv->columns; list; list = list->next)
9487 if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9494 * pspp_sheet_view_set_rules_hint
9495 * @tree_view: a #PsppSheetView
9496 * @setting: %TRUE if the tree requires reading across rows
9498 * This function tells GTK+ that the user interface for your
9499 * application requires users to read across tree rows and associate
9500 * cells with one another. By default, GTK+ will then render the tree
9501 * with alternating row colors. Do <emphasis>not</emphasis> use it
9502 * just because you prefer the appearance of the ruled tree; that's a
9503 * question for the theme. Some themes will draw tree rows in
9504 * alternating colors even when rules are turned off, and users who
9505 * prefer that appearance all the time can choose those themes. You
9506 * should call this function only as a <emphasis>semantic</emphasis>
9507 * hint to the theme engine that your tree makes alternating colors
9508 * useful from a functional standpoint (since it has lots of columns,
9513 pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view,
9516 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9518 setting = setting != FALSE;
9520 if (tree_view->priv->has_rules != setting)
9522 tree_view->priv->has_rules = setting;
9523 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9526 g_object_notify (G_OBJECT (tree_view), "rules-hint");
9530 * pspp_sheet_view_get_rules_hint
9531 * @tree_view: a #PsppSheetView
9533 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9535 * Return value: %TRUE if rules are useful for the user of this tree
9538 pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view)
9540 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9542 return tree_view->priv->has_rules;
9545 /* Public Column functions
9549 * pspp_sheet_view_append_column:
9550 * @tree_view: A #PsppSheetView.
9551 * @column: The #PsppSheetViewColumn to add.
9553 * Appends @column to the list of columns.
9555 * Return value: The number of columns in @tree_view after appending.
9558 pspp_sheet_view_append_column (PsppSheetView *tree_view,
9559 PsppSheetViewColumn *column)
9561 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9562 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9563 g_return_val_if_fail (column->tree_view == NULL, -1);
9565 return pspp_sheet_view_insert_column (tree_view, column, -1);
9570 * pspp_sheet_view_remove_column:
9571 * @tree_view: A #PsppSheetView.
9572 * @column: The #PsppSheetViewColumn to remove.
9574 * Removes @column from @tree_view.
9576 * Return value: The number of columns in @tree_view after removing.
9579 pspp_sheet_view_remove_column (PsppSheetView *tree_view,
9580 PsppSheetViewColumn *column)
9582 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9583 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9584 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9586 if (tree_view->priv->focus_column == column)
9587 tree_view->priv->focus_column = NULL;
9589 if (tree_view->priv->edited_column == column)
9591 pspp_sheet_view_stop_editing (tree_view, TRUE);
9593 /* no need to, but just to be sure ... */
9594 tree_view->priv->edited_column = NULL;
9597 _pspp_sheet_view_column_unset_tree_view (column);
9599 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9600 tree_view->priv->n_columns--;
9602 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9606 _pspp_sheet_view_column_unrealize_button (column);
9607 for (list = tree_view->priv->columns; list; list = list->next)
9609 PsppSheetViewColumn *tmp_column;
9611 tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9612 if (tmp_column->visible)
9613 _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9616 if (tree_view->priv->n_columns == 0 &&
9617 pspp_sheet_view_get_headers_visible (tree_view) &&
9618 tree_view->priv->header_window)
9619 gdk_window_hide (tree_view->priv->header_window);
9621 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9624 g_object_unref (column);
9625 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9627 return tree_view->priv->n_columns;
9631 * pspp_sheet_view_insert_column:
9632 * @tree_view: A #PsppSheetView.
9633 * @column: The #PsppSheetViewColumn to be inserted.
9634 * @position: The position to insert @column in.
9636 * This inserts the @column into the @tree_view at @position. If @position is
9637 * -1, then the column is inserted at the end.
9639 * Return value: The number of columns in @tree_view after insertion.
9642 pspp_sheet_view_insert_column (PsppSheetView *tree_view,
9643 PsppSheetViewColumn *column,
9646 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9647 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9648 g_return_val_if_fail (column->tree_view == NULL, -1);
9650 g_object_ref_sink (column);
9652 if (tree_view->priv->n_columns == 0 &&
9653 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9654 pspp_sheet_view_get_headers_visible (tree_view))
9656 gdk_window_show (tree_view->priv->header_window);
9659 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9661 tree_view->priv->n_columns++;
9663 _pspp_sheet_view_column_set_tree_view (column, tree_view);
9665 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9669 _pspp_sheet_view_column_realize_button (column);
9671 for (list = tree_view->priv->columns; list; list = list->next)
9673 column = PSPP_SHEET_VIEW_COLUMN (list->data);
9674 if (column->visible)
9675 _pspp_sheet_view_column_cell_set_dirty (column);
9677 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9680 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9682 return tree_view->priv->n_columns;
9686 * pspp_sheet_view_insert_column_with_attributes:
9687 * @tree_view: A #PsppSheetView
9688 * @position: The position to insert the new column in.
9689 * @title: The title to set the header to.
9690 * @cell: The #GtkCellRenderer.
9691 * @Varargs: A %NULL-terminated list of attributes.
9693 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9694 * @position. If @position is -1, then the newly created column is inserted at
9695 * the end. The column is initialized with the attributes given.
9697 * Return value: The number of columns in @tree_view after insertion.
9700 pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view,
9703 GtkCellRenderer *cell,
9706 PsppSheetViewColumn *column;
9711 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9713 column = pspp_sheet_view_column_new ();
9714 pspp_sheet_view_column_set_title (column, title);
9715 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9717 va_start (args, cell);
9719 attribute = va_arg (args, gchar *);
9721 while (attribute != NULL)
9723 column_id = va_arg (args, gint);
9724 pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9725 attribute = va_arg (args, gchar *);
9730 pspp_sheet_view_insert_column (tree_view, column, position);
9732 return tree_view->priv->n_columns;
9736 * pspp_sheet_view_insert_column_with_data_func:
9737 * @tree_view: a #PsppSheetView
9738 * @position: Position to insert, -1 for append
9739 * @title: column title
9740 * @cell: cell renderer for column
9741 * @func: function to set attributes of cell renderer
9742 * @data: data for @func
9743 * @dnotify: destroy notifier for @data
9745 * Convenience function that inserts a new column into the #PsppSheetView
9746 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9747 * attributes (normally using data from the model). See also
9748 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9750 * Return value: number of columns in the tree view post-insert
9753 pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view,
9756 GtkCellRenderer *cell,
9757 PsppSheetCellDataFunc func,
9759 GDestroyNotify dnotify)
9761 PsppSheetViewColumn *column;
9763 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9765 column = pspp_sheet_view_column_new ();
9766 pspp_sheet_view_column_set_title (column, title);
9767 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9768 pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9770 pspp_sheet_view_insert_column (tree_view, column, position);
9772 return tree_view->priv->n_columns;
9776 * pspp_sheet_view_get_column:
9777 * @tree_view: A #PsppSheetView.
9778 * @n: The position of the column, counting from 0.
9780 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9782 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9785 PsppSheetViewColumn *
9786 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9789 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9791 if (n < 0 || n >= tree_view->priv->n_columns)
9794 if (tree_view->priv->columns == NULL)
9797 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9801 * pspp_sheet_view_get_columns:
9802 * @tree_view: A #PsppSheetView
9804 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9805 * The returned list must be freed with g_list_free ().
9807 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9810 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9812 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9814 return g_list_copy (tree_view->priv->columns);
9818 * pspp_sheet_view_move_column_after:
9819 * @tree_view: A #PsppSheetView
9820 * @column: The #PsppSheetViewColumn to be moved.
9821 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9823 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9824 * @column is placed in the first position.
9827 pspp_sheet_view_move_column_after (PsppSheetView *tree_view,
9828 PsppSheetViewColumn *column,
9829 PsppSheetViewColumn *base_column)
9831 GList *column_list_el, *base_el = NULL;
9833 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9835 column_list_el = g_list_find (tree_view->priv->columns, column);
9836 g_return_if_fail (column_list_el != NULL);
9840 base_el = g_list_find (tree_view->priv->columns, base_column);
9841 g_return_if_fail (base_el != NULL);
9844 if (column_list_el->prev == base_el)
9847 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9848 if (base_el == NULL)
9850 column_list_el->prev = NULL;
9851 column_list_el->next = tree_view->priv->columns;
9852 if (column_list_el->next)
9853 column_list_el->next->prev = column_list_el;
9854 tree_view->priv->columns = column_list_el;
9858 column_list_el->prev = base_el;
9859 column_list_el->next = base_el->next;
9860 if (column_list_el->next)
9861 column_list_el->next->prev = column_list_el;
9862 base_el->next = column_list_el;
9865 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9867 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9868 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9871 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9875 * pspp_sheet_view_set_column_drag_function:
9876 * @tree_view: A #PsppSheetView.
9877 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9878 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9879 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9881 * Sets a user function for determining where a column may be dropped when
9882 * dragged. This function is called on every column pair in turn at the
9883 * beginning of a column drag to determine where a drop can take place. The
9884 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9885 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9886 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9887 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9888 * @tree_view reverts to the default behavior of allowing all columns to be
9889 * dropped everywhere.
9892 pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view,
9893 PsppSheetViewColumnDropFunc func,
9895 GDestroyNotify destroy)
9897 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9899 if (tree_view->priv->column_drop_func_data_destroy)
9900 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9902 tree_view->priv->column_drop_func = func;
9903 tree_view->priv->column_drop_func_data = user_data;
9904 tree_view->priv->column_drop_func_data_destroy = destroy;
9908 * pspp_sheet_view_scroll_to_point:
9909 * @tree_view: a #PsppSheetView
9910 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9911 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9913 * Scrolls the tree view such that the top-left corner of the visible
9914 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9915 * in tree coordinates. The @tree_view must be realized before
9916 * this function is called. If it isn't, you probably want to be
9917 * using pspp_sheet_view_scroll_to_cell().
9919 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9922 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9926 GtkAdjustment *hadj;
9927 GtkAdjustment *vadj;
9929 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9930 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9932 hadj = tree_view->priv->hadjustment;
9933 vadj = tree_view->priv->vadjustment;
9936 gtk_adjustment_set_value (hadj, CLAMP (tree_x, gtk_adjustment_get_lower (hadj), gtk_adjustment_get_upper (hadj) - gtk_adjustment_get_page_size (hadj)));
9938 gtk_adjustment_set_value (vadj, CLAMP (tree_y, gtk_adjustment_get_lower (vadj), gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj)));
9942 * pspp_sheet_view_scroll_to_cell:
9943 * @tree_view: A #PsppSheetView.
9944 * @path: (allow-none): The path of the row to move to, or %NULL.
9945 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9946 * @use_align: whether to use alignment arguments, or %FALSE.
9947 * @row_align: The vertical alignment of the row specified by @path.
9948 * @col_align: The horizontal alignment of the column specified by @column.
9950 * Moves the alignments of @tree_view to the position specified by @column and
9951 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9952 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9953 * or @path need to be non-%NULL. @row_align determines where the row is
9954 * placed, and @col_align determines where @column is placed. Both are expected
9955 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9956 * right/bottom alignment, 0.5 means center.
9958 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9959 * tree does the minimum amount of work to scroll the cell onto the screen.
9960 * This means that the cell will be scrolled to the edge closest to its current
9961 * position. If the cell is currently visible on the screen, nothing is done.
9963 * This function only works if the model is set, and @path is a valid row on the
9964 * model. If the model changes before the @tree_view is realized, the centered
9965 * path will be modified to reflect this change.
9968 pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view,
9970 PsppSheetViewColumn *column,
9975 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9976 g_return_if_fail (tree_view->priv->model != NULL);
9977 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9978 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9979 g_return_if_fail (path != NULL || column != NULL);
9982 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9983 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9985 row_align = CLAMP (row_align, 0.0, 1.0);
9986 col_align = CLAMP (col_align, 0.0, 1.0);
9989 /* Note: Despite the benefits that come from having one code path for the
9990 * scrolling code, we short-circuit validate_visible_area's immplementation as
9991 * it is much slower than just going to the point.
9993 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9994 !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9995 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9997 if (tree_view->priv->scroll_to_path)
9998 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
10000 tree_view->priv->scroll_to_path = NULL;
10001 tree_view->priv->scroll_to_column = NULL;
10004 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
10006 tree_view->priv->scroll_to_column = column;
10007 tree_view->priv->scroll_to_use_align = use_align;
10008 tree_view->priv->scroll_to_row_align = row_align;
10009 tree_view->priv->scroll_to_col_align = col_align;
10011 install_presize_handler (tree_view);
10015 GdkRectangle cell_rect;
10016 GdkRectangle vis_rect;
10017 gint dest_x, dest_y;
10019 pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
10020 pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
10022 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
10024 dest_x = vis_rect.x;
10025 dest_y = vis_rect.y;
10031 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
10035 if (cell_rect.x < vis_rect.x)
10036 dest_x = cell_rect.x;
10037 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
10038 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
10046 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
10047 dest_y = MAX (dest_y, 0);
10051 if (cell_rect.y < vis_rect.y)
10052 dest_y = cell_rect.y;
10053 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
10054 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
10058 pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
10063 * pspp_sheet_view_row_activated:
10064 * @tree_view: A #PsppSheetView
10065 * @path: The #GtkTreePath to be activated.
10066 * @column: The #PsppSheetViewColumn to be activated.
10068 * Activates the cell determined by @path and @column.
10071 pspp_sheet_view_row_activated (PsppSheetView *tree_view,
10073 PsppSheetViewColumn *column)
10075 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10077 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
10082 * pspp_sheet_view_get_reorderable:
10083 * @tree_view: a #PsppSheetView
10085 * Retrieves whether the user can reorder the tree via drag-and-drop. See
10086 * pspp_sheet_view_set_reorderable().
10088 * Return value: %TRUE if the tree can be reordered.
10091 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
10093 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10095 return tree_view->priv->reorderable;
10099 * pspp_sheet_view_set_reorderable:
10100 * @tree_view: A #PsppSheetView.
10101 * @reorderable: %TRUE, if the tree can be reordered.
10103 * This function is a convenience function to allow you to reorder
10104 * models that support the #GtkDragSourceIface and the
10105 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
10106 * these. If @reorderable is %TRUE, then the user can reorder the
10107 * model by dragging and dropping rows. The developer can listen to
10108 * these changes by connecting to the model's row_inserted and
10109 * row_deleted signals. The reordering is implemented by setting up
10110 * the tree view as a drag source and destination. Therefore, drag and
10111 * drop can not be used in a reorderable view for any other purpose.
10113 * This function does not give you any degree of control over the order -- any
10114 * reordering is allowed. If more control is needed, you should probably
10115 * handle drag and drop manually.
10118 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
10119 gboolean reorderable)
10121 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10123 reorderable = reorderable != FALSE;
10125 if (tree_view->priv->reorderable == reorderable)
10130 const GtkTargetEntry row_targets[] = {
10131 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
10134 pspp_sheet_view_enable_model_drag_source (tree_view,
10137 G_N_ELEMENTS (row_targets),
10139 pspp_sheet_view_enable_model_drag_dest (tree_view,
10141 G_N_ELEMENTS (row_targets),
10146 pspp_sheet_view_unset_rows_drag_source (tree_view);
10147 pspp_sheet_view_unset_rows_drag_dest (tree_view);
10150 tree_view->priv->reorderable = reorderable;
10152 g_object_notify (G_OBJECT (tree_view), "reorderable");
10155 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
10156 is pressed, other rows will be unselected.
10158 If CLAMP_NODE is true, then the sheetview will scroll to make the row
10161 pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
10163 gboolean clear_and_select,
10164 gboolean clamp_node,
10165 PsppSheetSelectMode mode)
10169 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10171 GtkTreePath *cursor_path;
10172 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10173 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
10174 gtk_tree_path_free (cursor_path);
10177 gtk_tree_row_reference_free (tree_view->priv->cursor);
10178 tree_view->priv->cursor = NULL;
10180 _pspp_sheet_view_find_node (tree_view, path, &node);
10181 tree_view->priv->cursor =
10182 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
10183 tree_view->priv->model,
10186 if (tree_view->priv->row_count > 0)
10190 if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
10191 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
10195 /* We have to re-find tree and node here again, somebody might have
10196 * cleared the node or the whole tree in the PsppSheetSelection::changed
10197 * callback. If the nodes differ we bail out here.
10199 _pspp_sheet_view_find_node (tree_view, path, &new_node);
10201 if (node != new_node)
10206 pspp_sheet_view_clamp_node_visible (tree_view, node);
10207 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
10211 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
10215 * pspp_sheet_view_get_cursor:
10216 * @tree_view: A #PsppSheetView
10217 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
10218 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
10220 * Fills in @path and @focus_column with the current path and focus column. If
10221 * the cursor isn't currently set, then *@path will be %NULL. If no column
10222 * currently has focus, then *@focus_column will be %NULL.
10224 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
10225 * you are done with it.
10228 pspp_sheet_view_get_cursor (PsppSheetView *tree_view,
10229 GtkTreePath **path,
10230 PsppSheetViewColumn **focus_column)
10232 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10236 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10237 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10244 *focus_column = tree_view->priv->focus_column;
10249 * pspp_sheet_view_set_cursor:
10250 * @tree_view: A #PsppSheetView
10251 * @path: A #GtkTreePath
10252 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10253 * @start_editing: %TRUE if the specified cell should start being edited.
10255 * Sets the current keyboard focus to be at @path, and selects it. This is
10256 * useful when you want to focus the user's attention on a particular row. If
10257 * @focus_column is not %NULL, then focus is given to the column specified by
10258 * it. Additionally, if @focus_column is specified, and @start_editing is
10259 * %TRUE, then editing should be started in the specified cell.
10260 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10261 * in order to give keyboard focus to the widget. Please note that editing
10262 * can only happen when the widget is realized.
10264 * If @path is invalid for @model, the current cursor (if any) will be unset
10265 * and the function will return without failing.
10268 pspp_sheet_view_set_cursor (PsppSheetView *tree_view,
10270 PsppSheetViewColumn *focus_column,
10271 gboolean start_editing)
10273 pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10274 NULL, start_editing);
10278 * pspp_sheet_view_set_cursor_on_cell:
10279 * @tree_view: A #PsppSheetView
10280 * @path: A #GtkTreePath
10281 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10282 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10283 * @start_editing: %TRUE if the specified cell should start being edited.
10285 * Sets the current keyboard focus to be at @path, and selects it. This is
10286 * useful when you want to focus the user's attention on a particular row. If
10287 * @focus_column is not %NULL, then focus is given to the column specified by
10288 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10289 * contains 2 or more editable or activatable cells, then focus is given to
10290 * the cell specified by @focus_cell. Additionally, if @focus_column is
10291 * specified, and @start_editing is %TRUE, then editing should be started in
10292 * the specified cell. This function is often followed by
10293 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10294 * widget. Please note that editing can only happen when the widget is
10297 * If @path is invalid for @model, the current cursor (if any) will be unset
10298 * and the function will return without failing.
10303 pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view,
10305 PsppSheetViewColumn *focus_column,
10306 GtkCellRenderer *focus_cell,
10307 gboolean start_editing)
10309 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10310 g_return_if_fail (path != NULL);
10311 g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10313 if (!tree_view->priv->model)
10318 g_return_if_fail (focus_column);
10319 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10322 /* cancel the current editing, if it exists */
10323 if (tree_view->priv->edited_column &&
10324 tree_view->priv->edited_column->editable_widget)
10325 pspp_sheet_view_stop_editing (tree_view, TRUE);
10327 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
10329 if (focus_column && focus_column->visible)
10332 gboolean column_in_tree = FALSE;
10334 for (list = tree_view->priv->columns; list; list = list->next)
10335 if (list->data == focus_column)
10337 column_in_tree = TRUE;
10340 g_return_if_fail (column_in_tree);
10341 tree_view->priv->focus_column = focus_column;
10343 pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10345 pspp_sheet_view_start_editing (tree_view, path);
10347 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10348 pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10354 * pspp_sheet_view_get_bin_window:
10355 * @tree_view: A #PsppSheetView
10357 * Returns the window that @tree_view renders to. This is used primarily to
10358 * compare to <literal>event->window</literal> to confirm that the event on
10359 * @tree_view is on the right window.
10361 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10364 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10366 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10368 return tree_view->priv->bin_window;
10372 * pspp_sheet_view_get_path_at_pos:
10373 * @tree_view: A #PsppSheetView.
10374 * @x: The x position to be identified (relative to bin_window).
10375 * @y: The y position to be identified (relative to bin_window).
10376 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10377 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10378 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10379 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10381 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10382 * (please see pspp_sheet_view_get_bin_window()).
10383 * That is, @x and @y are relative to an events coordinates. @x and @y must
10384 * come from an event on the @tree_view only where <literal>event->window ==
10385 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10386 * things like popup menus. If @path is non-%NULL, then it will be filled
10387 * with the #GtkTreePath at that point. This path should be freed with
10388 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10389 * with the column at that point. @cell_x and @cell_y return the coordinates
10390 * relative to the cell background (i.e. the @background_area passed to
10391 * gtk_cell_renderer_render()). This function is only meaningful if
10392 * @tree_view is realized. Therefore this function will always return %FALSE
10393 * if @tree_view is not realized or does not have a model.
10395 * For converting widget coordinates (eg. the ones you get from
10396 * GtkWidget::query-tooltip), please see
10397 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10399 * Return value: %TRUE if a row exists at that coordinate.
10402 pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view,
10405 GtkTreePath **path,
10406 PsppSheetViewColumn **column,
10413 g_return_val_if_fail (tree_view != NULL, FALSE);
10420 if (tree_view->priv->bin_window == NULL)
10423 if (tree_view->priv->row_count == 0)
10426 if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment))
10429 if (x < 0 || y < 0)
10432 if (column || cell_x)
10434 PsppSheetViewColumn *tmp_column;
10435 PsppSheetViewColumn *last_column = NULL;
10437 gint remaining_x = x;
10438 gboolean found = FALSE;
10441 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10442 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10444 list = (rtl ? list->prev : list->next))
10446 tmp_column = list->data;
10448 if (tmp_column->visible == FALSE)
10451 last_column = tmp_column;
10452 if (remaining_x <= tmp_column->width)
10457 *column = tmp_column;
10460 *cell_x = remaining_x;
10464 remaining_x -= tmp_column->width;
10467 /* If found is FALSE and there is a last_column, then it the remainder
10468 * space is in that area
10475 *column = last_column;
10478 *cell_x = last_column->width + remaining_x;
10487 y_offset = pspp_sheet_view_find_offset (tree_view,
10488 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10495 *cell_y = y_offset;
10498 *path = _pspp_sheet_view_find_path (tree_view, node);
10503 /* Computes 'cell_area' from 'background_area', which must be the background
10504 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10505 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10506 the cell area as passed to _pspp_sheet_view_column_cell_render().
10508 'column' is required to properly adjust 'cell_area->x' and
10509 'cell_area->width'. It may be set to NULL if these values are not of
10510 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10513 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
10514 PsppSheetViewColumn *column,
10515 const GdkRectangle *background_area,
10516 gboolean subtract_focus_rect,
10517 GdkRectangle *cell_area)
10519 gint vertical_separator;
10520 gint horizontal_separator;
10522 *cell_area = *background_area;
10524 gtk_widget_style_get (GTK_WIDGET (tree_view),
10525 "vertical-separator", &vertical_separator,
10526 "horizontal-separator", &horizontal_separator,
10528 cell_area->x += horizontal_separator / 2;
10529 cell_area->y += vertical_separator / 2;
10530 cell_area->width -= horizontal_separator;
10531 cell_area->height -= vertical_separator;
10533 if (subtract_focus_rect)
10535 int focus_line_width;
10537 gtk_widget_style_get (GTK_WIDGET (tree_view),
10538 "focus-line-width", &focus_line_width,
10540 cell_area->x += focus_line_width;
10541 cell_area->y += focus_line_width;
10542 cell_area->width -= 2 * focus_line_width;
10543 cell_area->height -= 2 * focus_line_width;
10546 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10548 gint grid_line_width;
10549 gtk_widget_style_get (GTK_WIDGET (tree_view),
10550 "grid-line-width", &grid_line_width,
10553 if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10554 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10557 PsppSheetViewColumn *first_column, *last_column;
10560 /* Find the last visible column. */
10561 last_column = NULL;
10562 for (list = g_list_last (tree_view->priv->columns);
10566 PsppSheetViewColumn *c = list->data;
10574 /* Find the first visible column. */
10575 first_column = NULL;
10576 for (list = g_list_first (tree_view->priv->columns);
10580 PsppSheetViewColumn *c = list->data;
10588 if (column == first_column)
10590 cell_area->width -= grid_line_width / 2;
10592 else if (column == last_column)
10594 cell_area->x += grid_line_width / 2;
10595 cell_area->width -= grid_line_width / 2;
10599 cell_area->x += grid_line_width / 2;
10600 cell_area->width -= grid_line_width;
10604 if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10605 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10607 cell_area->y += grid_line_width / 2;
10608 cell_area->height -= grid_line_width;
10612 if (column == NULL)
10615 cell_area->width = 0;
10620 * pspp_sheet_view_get_cell_area:
10621 * @tree_view: a #PsppSheetView
10622 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10623 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10624 * @rect: rectangle to fill with cell rect
10626 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10627 * row specified by @path and the column specified by @column. If @path is
10628 * %NULL, or points to a path not currently displayed, the @y and @height fields
10629 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10630 * fields will be filled with 0. The sum of all cell rects does not cover the
10631 * entire tree; there are extra pixels in between rows, for example. The
10632 * returned rectangle is equivalent to the @cell_area passed to
10633 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10637 pspp_sheet_view_get_cell_area (PsppSheetView *tree_view,
10639 PsppSheetViewColumn *column,
10640 GdkRectangle *rect)
10642 GdkRectangle background_area;
10644 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10645 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10646 g_return_if_fail (rect != NULL);
10647 g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10648 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10650 pspp_sheet_view_get_background_area (tree_view, path, column,
10652 pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10657 * pspp_sheet_view_get_background_area:
10658 * @tree_view: a #PsppSheetView
10659 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10660 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10661 * @rect: rectangle to fill with cell background rect
10663 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10664 * row specified by @path and the column specified by @column. If @path is
10665 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10666 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10667 * fields will be filled with 0. The returned rectangle is equivalent to the
10668 * @background_area passed to gtk_cell_renderer_render(). These background
10669 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10670 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10671 * itself, excluding surrounding borders.
10675 pspp_sheet_view_get_background_area (PsppSheetView *tree_view,
10677 PsppSheetViewColumn *column,
10678 GdkRectangle *rect)
10682 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10683 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10684 g_return_if_fail (rect != NULL);
10693 /* Get vertical coords */
10695 _pspp_sheet_view_find_node (tree_view, path, &node);
10699 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10701 rect->height = ROW_HEIGHT (tree_view);
10708 pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10709 rect->width = x2 - rect->x;
10714 * pspp_sheet_view_get_visible_rect:
10715 * @tree_view: a #PsppSheetView
10716 * @visible_rect: rectangle to fill
10718 * Fills @visible_rect with the currently-visible region of the
10719 * buffer, in tree coordinates. Convert to bin_window coordinates with
10720 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10721 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10722 * scrollable area of the tree.
10725 pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view,
10726 GdkRectangle *visible_rect)
10730 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10732 widget = GTK_WIDGET (tree_view);
10736 GtkAllocation allocation;
10737 gtk_widget_get_allocation (widget, &allocation);
10738 visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
10739 visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
10740 visible_rect->width = allocation.width;
10741 visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10746 * pspp_sheet_view_widget_to_tree_coords:
10747 * @tree_view: a #PsppSheetView
10748 * @wx: X coordinate relative to bin_window
10749 * @wy: Y coordinate relative to bin_window
10750 * @tx: return location for tree X coordinate
10751 * @ty: return location for tree Y coordinate
10753 * Converts bin_window coordinates to coordinates for the
10754 * tree (the full scrollable area of the tree).
10756 * Deprecated: 2.12: Due to historial reasons the name of this function is
10757 * incorrect. For converting coordinates relative to the widget to
10758 * bin_window coordinates, please see
10759 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10763 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10769 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10772 *tx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10774 *ty = wy + tree_view->priv->dy;
10778 * pspp_sheet_view_tree_to_widget_coords:
10779 * @tree_view: a #PsppSheetView
10780 * @tx: tree X coordinate
10781 * @ty: tree Y coordinate
10782 * @wx: return location for X coordinate relative to bin_window
10783 * @wy: return location for Y coordinate relative to bin_window
10785 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10786 * to bin_window coordinates.
10788 * Deprecated: 2.12: Due to historial reasons the name of this function is
10789 * incorrect. For converting bin_window coordinates to coordinates relative
10790 * to bin_window, please see
10791 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10795 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10801 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10804 *wx = tx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10806 *wy = ty - tree_view->priv->dy;
10811 * pspp_sheet_view_convert_widget_to_tree_coords:
10812 * @tree_view: a #PsppSheetView
10813 * @wx: X coordinate relative to the widget
10814 * @wy: Y coordinate relative to the widget
10815 * @tx: return location for tree X coordinate
10816 * @ty: return location for tree Y coordinate
10818 * Converts widget coordinates to coordinates for the
10819 * tree (the full scrollable area of the tree).
10824 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10832 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10834 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10837 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10843 * pspp_sheet_view_convert_tree_to_widget_coords:
10844 * @tree_view: a #PsppSheetView
10845 * @tx: X coordinate relative to the tree
10846 * @ty: Y coordinate relative to the tree
10847 * @wx: return location for widget X coordinate
10848 * @wy: return location for widget Y coordinate
10850 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10851 * to widget coordinates.
10856 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10864 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10866 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10869 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10875 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10876 * @tree_view: a #PsppSheetView
10877 * @wx: X coordinate relative to the widget
10878 * @wy: Y coordinate relative to the widget
10879 * @bx: return location for bin_window X coordinate
10880 * @by: return location for bin_window Y coordinate
10882 * Converts widget coordinates to coordinates for the bin_window
10883 * (see pspp_sheet_view_get_bin_window()).
10888 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10894 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10897 *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10899 *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10903 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10904 * @tree_view: a #PsppSheetView
10905 * @bx: bin_window X coordinate
10906 * @by: bin_window Y coordinate
10907 * @wx: return location for widget X coordinate
10908 * @wy: return location for widget Y coordinate
10910 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10911 * to widget relative coordinates.
10916 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10922 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10925 *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10927 *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10931 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10932 * @tree_view: a #PsppSheetView
10933 * @tx: tree X coordinate
10934 * @ty: tree Y coordinate
10935 * @bx: return location for X coordinate relative to bin_window
10936 * @by: return location for Y coordinate relative to bin_window
10938 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10939 * to bin_window coordinates.
10944 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10950 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10955 *by = ty - tree_view->priv->dy;
10959 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10960 * @tree_view: a #PsppSheetView
10961 * @bx: X coordinate relative to bin_window
10962 * @by: Y coordinate relative to bin_window
10963 * @tx: return location for tree X coordinate
10964 * @ty: return location for tree Y coordinate
10966 * Converts bin_window coordinates to coordinates for the
10967 * tree (the full scrollable area of the tree).
10972 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10978 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10983 *ty = by + tree_view->priv->dy;
10989 * pspp_sheet_view_get_visible_range:
10990 * @tree_view: A #PsppSheetView
10991 * @start_path: (allow-none): Return location for start of region, or %NULL.
10992 * @end_path: (allow-none): Return location for end of region, or %NULL.
10994 * Sets @start_path and @end_path to be the first and last visible path.
10995 * Note that there may be invisible paths in between.
10997 * The paths should be freed with gtk_tree_path_free() after use.
10999 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
11004 pspp_sheet_view_get_visible_range (PsppSheetView *tree_view,
11005 GtkTreePath **start_path,
11006 GtkTreePath **end_path)
11011 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11013 if (!tree_view->priv->row_count)
11020 pspp_sheet_view_find_offset (tree_view,
11021 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
11024 *start_path = _pspp_sheet_view_find_path (tree_view, node);
11033 if (tree_view->priv->height < gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
11034 y = tree_view->priv->height - 1;
11036 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1;
11038 pspp_sheet_view_find_offset (tree_view, y, &node);
11040 *end_path = _pspp_sheet_view_find_path (tree_view, node);
11049 unset_reorderable (PsppSheetView *tree_view)
11051 if (tree_view->priv->reorderable)
11053 tree_view->priv->reorderable = FALSE;
11054 g_object_notify (G_OBJECT (tree_view), "reorderable");
11059 * pspp_sheet_view_enable_model_drag_source:
11060 * @tree_view: a #PsppSheetView
11061 * @start_button_mask: Mask of allowed buttons to start drag
11062 * @targets: the table of targets that the drag will support
11063 * @n_targets: the number of items in @targets
11064 * @actions: the bitmask of possible actions for a drag from this
11067 * Turns @tree_view into a drag source for automatic DND. Calling this
11068 * method sets #PsppSheetView:reorderable to %FALSE.
11071 pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view,
11072 GdkModifierType start_button_mask,
11073 const GtkTargetEntry *targets,
11075 GdkDragAction actions)
11077 TreeViewDragInfo *di;
11079 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11081 gtk_drag_source_set (GTK_WIDGET (tree_view),
11087 di = ensure_info (tree_view);
11089 di->start_button_mask = start_button_mask;
11090 di->source_actions = actions;
11091 di->source_set = TRUE;
11093 unset_reorderable (tree_view);
11097 * pspp_sheet_view_enable_model_drag_dest:
11098 * @tree_view: a #PsppSheetView
11099 * @targets: the table of targets that the drag will support
11100 * @n_targets: the number of items in @targets
11101 * @actions: the bitmask of possible actions for a drag from this
11104 * Turns @tree_view into a drop destination for automatic DND. Calling
11105 * this method sets #PsppSheetView:reorderable to %FALSE.
11108 pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view,
11109 const GtkTargetEntry *targets,
11111 GdkDragAction actions)
11113 TreeViewDragInfo *di;
11115 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11117 gtk_drag_dest_set (GTK_WIDGET (tree_view),
11123 di = ensure_info (tree_view);
11124 di->dest_set = TRUE;
11126 unset_reorderable (tree_view);
11130 * pspp_sheet_view_unset_rows_drag_source:
11131 * @tree_view: a #PsppSheetView
11133 * Undoes the effect of
11134 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
11135 * #PsppSheetView:reorderable to %FALSE.
11138 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
11140 TreeViewDragInfo *di;
11142 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11144 di = get_info (tree_view);
11148 if (di->source_set)
11150 gtk_drag_source_unset (GTK_WIDGET (tree_view));
11151 di->source_set = FALSE;
11154 if (!di->dest_set && !di->source_set)
11155 remove_info (tree_view);
11158 unset_reorderable (tree_view);
11162 * pspp_sheet_view_unset_rows_drag_dest:
11163 * @tree_view: a #PsppSheetView
11165 * Undoes the effect of
11166 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
11167 * #PsppSheetView:reorderable to %FALSE.
11170 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
11172 TreeViewDragInfo *di;
11174 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11176 di = get_info (tree_view);
11182 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
11183 di->dest_set = FALSE;
11186 if (!di->dest_set && !di->source_set)
11187 remove_info (tree_view);
11190 unset_reorderable (tree_view);
11194 * pspp_sheet_view_set_drag_dest_row:
11195 * @tree_view: a #PsppSheetView
11196 * @path: (allow-none): The path of the row to highlight, or %NULL.
11197 * @pos: Specifies whether to drop before, after or into the row
11199 * Sets the row that is highlighted for feedback.
11202 pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view,
11204 PsppSheetViewDropPosition pos)
11206 GtkTreePath *current_dest;
11208 /* Note; this function is exported to allow a custom DND
11209 * implementation, so it can't touch TreeViewDragInfo
11212 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11214 current_dest = NULL;
11216 if (tree_view->priv->drag_dest_row)
11218 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11219 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
11222 /* special case a drop on an empty model */
11223 tree_view->priv->empty_view_drop = 0;
11225 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
11226 && gtk_tree_path_get_depth (path) == 1
11227 && gtk_tree_path_get_indices (path)[0] == 0)
11231 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
11235 tree_view->priv->empty_view_drop = 1;
11238 tree_view->priv->drag_dest_pos = pos;
11242 tree_view->priv->drag_dest_row =
11243 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
11244 pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
11247 tree_view->priv->drag_dest_row = NULL;
11251 int node, new_node;
11253 _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11254 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11258 new_node = pspp_sheet_view_node_next (tree_view, node);
11260 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11262 new_node = pspp_sheet_view_node_prev (tree_view, node);
11264 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11266 gtk_tree_path_free (current_dest);
11271 * pspp_sheet_view_get_drag_dest_row:
11272 * @tree_view: a #PsppSheetView
11273 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11274 * @pos: (allow-none): Return location for the drop position, or %NULL
11276 * Gets information about the row that is highlighted for feedback.
11279 pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view,
11280 GtkTreePath **path,
11281 PsppSheetViewDropPosition *pos)
11283 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11287 if (tree_view->priv->drag_dest_row)
11288 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11291 if (tree_view->priv->empty_view_drop)
11292 *path = gtk_tree_path_new_from_indices (0, -1);
11299 *pos = tree_view->priv->drag_dest_pos;
11303 * pspp_sheet_view_get_dest_row_at_pos:
11304 * @tree_view: a #PsppSheetView
11305 * @drag_x: the position to determine the destination row for
11306 * @drag_y: the position to determine the destination row for
11307 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11308 * @pos: (allow-none): Return location for the drop position, or %NULL
11310 * Determines the destination row for a given position. @drag_x and
11311 * @drag_y are expected to be in widget coordinates. This function is only
11312 * meaningful if @tree_view is realized. Therefore this function will always
11313 * return %FALSE if @tree_view is not realized or does not have a model.
11315 * Return value: whether there is a row at the given position, %TRUE if this
11316 * is indeed the case.
11319 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view,
11322 GtkTreePath **path,
11323 PsppSheetViewDropPosition *pos)
11327 gdouble offset_into_row;
11330 PsppSheetViewColumn *column = NULL;
11331 GtkTreePath *tmp_path = NULL;
11333 /* Note; this function is exported to allow a custom DND
11334 * implementation, so it can't touch TreeViewDragInfo
11337 g_return_val_if_fail (tree_view != NULL, FALSE);
11338 g_return_val_if_fail (drag_x >= 0, FALSE);
11339 g_return_val_if_fail (drag_y >= 0, FALSE);
11344 if (tree_view->priv->bin_window == NULL)
11347 if (tree_view->priv->row_count == 0)
11350 /* If in the top third of a row, we drop before that row; if
11351 * in the bottom third, drop after that row; if in the middle,
11352 * and the row has children, drop into the row.
11354 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11357 if (!pspp_sheet_view_get_path_at_pos (tree_view,
11366 pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11369 offset_into_row = cell_y;
11374 gtk_tree_path_free (tmp_path);
11378 third = cell.height / 3.0;
11382 if (offset_into_row < third)
11384 *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11386 else if (offset_into_row < (cell.height / 2.0))
11388 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11390 else if (offset_into_row < third * 2.0)
11392 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11396 *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11404 #if GTK3_TRANSITION
11405 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11407 * pspp_sheet_view_create_row_drag_icon:
11408 * @tree_view: a #PsppSheetView
11409 * @path: a #GtkTreePath in @tree_view
11411 * Creates a #GdkPixmap representation of the row at @path.
11412 * This image is used for a drag icon.
11414 * Return value: a newly-allocated pixmap of the drag icon.
11417 pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view,
11424 GdkRectangle background_area;
11425 GdkRectangle expose_area;
11427 /* start drawing inside the black outline */
11429 GdkDrawable *drawable;
11430 gint bin_window_width;
11433 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11434 g_return_val_if_fail (path != NULL, NULL);
11436 widget = GTK_WIDGET (tree_view);
11438 if (!gtk_widget_get_realized (widget))
11441 _pspp_sheet_view_find_node (tree_view,
11448 if (!gtk_tree_model_get_iter (tree_view->priv->model,
11455 background_area.y = y;
11456 background_area.height = ROW_HEIGHT (tree_view);
11458 bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
11460 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11461 bin_window_width + 2,
11462 background_area.height + 2,
11467 expose_area.width = bin_window_width + 2;
11468 expose_area.height = background_area.height + 2;
11470 #if GTK3_TRANSITION
11471 gdk_draw_rectangle (drawable,
11472 widget->style->base_gc [gtk_widget_get_state (widget)],
11475 bin_window_width + 2,
11476 background_area.height + 2);
11479 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11481 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11483 list = (rtl ? list->prev : list->next))
11485 PsppSheetViewColumn *column = list->data;
11486 GdkRectangle cell_area;
11487 gint vertical_separator;
11489 if (!column->visible)
11492 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11494 background_area.x = cell_offset;
11495 background_area.width = column->width;
11497 gtk_widget_style_get (widget,
11498 "vertical-separator", &vertical_separator,
11501 cell_area = background_area;
11503 cell_area.y += vertical_separator / 2;
11504 cell_area.height -= vertical_separator;
11506 if (pspp_sheet_view_column_cell_is_visible (column))
11507 _pspp_sheet_view_column_cell_render (column,
11513 cell_offset += column->width;
11516 #if GTK3_TRANSITION
11517 gdk_draw_rectangle (drawable,
11518 widget->style->black_gc,
11521 bin_window_width + 1,
11522 background_area.height + 1);
11530 * pspp_sheet_view_set_destroy_count_func:
11531 * @tree_view: A #PsppSheetView
11532 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11533 * @data: (allow-none): User data to be passed to @func, or %NULL
11534 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11536 * This function should almost never be used. It is meant for private use by
11537 * ATK for determining the number of visible children that are removed when a row is deleted.
11540 pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view,
11541 PsppSheetDestroyCountFunc func,
11543 GDestroyNotify destroy)
11545 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11547 if (tree_view->priv->destroy_count_destroy)
11548 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11550 tree_view->priv->destroy_count_func = func;
11551 tree_view->priv->destroy_count_data = data;
11552 tree_view->priv->destroy_count_destroy = destroy;
11557 * Interactive search
11561 * pspp_sheet_view_set_enable_search:
11562 * @tree_view: A #PsppSheetView
11563 * @enable_search: %TRUE, if the user can search interactively
11565 * If @enable_search is set, then the user can type in text to search through
11566 * the tree interactively (this is sometimes called "typeahead find").
11568 * Note that even if this is %FALSE, the user can still initiate a search
11569 * using the "start-interactive-search" key binding.
11572 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11573 gboolean enable_search)
11575 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11577 enable_search = !!enable_search;
11579 if (tree_view->priv->enable_search != enable_search)
11581 tree_view->priv->enable_search = enable_search;
11582 g_object_notify (G_OBJECT (tree_view), "enable-search");
11587 * pspp_sheet_view_get_enable_search:
11588 * @tree_view: A #PsppSheetView
11590 * Returns whether or not the tree allows to start interactive searching
11591 * by typing in text.
11593 * Return value: whether or not to let the user search interactively
11596 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11598 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11600 return tree_view->priv->enable_search;
11605 * pspp_sheet_view_get_search_column:
11606 * @tree_view: A #PsppSheetView
11608 * Gets the column searched on by the interactive search code.
11610 * Return value: the column the interactive search code searches in.
11613 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11615 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11617 return (tree_view->priv->search_column);
11621 * pspp_sheet_view_set_search_column:
11622 * @tree_view: A #PsppSheetView
11623 * @column: the column of the model to search in, or -1 to disable searching
11625 * Sets @column as the column where the interactive search code should
11626 * search in for the current model.
11628 * If the search column is set, users can use the "start-interactive-search"
11629 * key binding to bring up search popup. The enable-search property controls
11630 * whether simply typing text will also start an interactive search.
11632 * Note that @column refers to a column of the current model. The search
11633 * column is reset to -1 when the model is changed.
11636 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11639 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11640 g_return_if_fail (column >= -1);
11642 if (tree_view->priv->search_column == column)
11645 tree_view->priv->search_column = column;
11646 g_object_notify (G_OBJECT (tree_view), "search-column");
11650 * pspp_sheet_view_get_search_equal_func:
11651 * @tree_view: A #PsppSheetView
11653 * Returns the compare function currently in use.
11655 * Return value: the currently used compare function for the search code.
11658 PsppSheetViewSearchEqualFunc
11659 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11661 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11663 return tree_view->priv->search_equal_func;
11667 * pspp_sheet_view_set_search_equal_func:
11668 * @tree_view: A #PsppSheetView
11669 * @search_equal_func: the compare function to use during the search
11670 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11671 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11673 * Sets the compare function for the interactive search capabilities; note
11674 * that somewhat like strcmp() returning 0 for equality
11675 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11678 pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view,
11679 PsppSheetViewSearchEqualFunc search_equal_func,
11680 gpointer search_user_data,
11681 GDestroyNotify search_destroy)
11683 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11684 g_return_if_fail (search_equal_func != NULL);
11686 if (tree_view->priv->search_destroy)
11687 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11689 tree_view->priv->search_equal_func = search_equal_func;
11690 tree_view->priv->search_user_data = search_user_data;
11691 tree_view->priv->search_destroy = search_destroy;
11692 if (tree_view->priv->search_equal_func == NULL)
11693 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11697 * pspp_sheet_view_get_search_entry:
11698 * @tree_view: A #PsppSheetView
11700 * Returns the #GtkEntry which is currently in use as interactive search
11701 * entry for @tree_view. In case the built-in entry is being used, %NULL
11702 * will be returned.
11704 * Return value: the entry currently in use as search entry.
11709 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11711 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11713 if (tree_view->priv->search_custom_entry_set)
11714 return GTK_ENTRY (tree_view->priv->search_entry);
11720 * pspp_sheet_view_set_search_entry:
11721 * @tree_view: A #PsppSheetView
11722 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11724 * Sets the entry which the interactive search code will use for this
11725 * @tree_view. This is useful when you want to provide a search entry
11726 * in our interface at all time at a fixed position. Passing %NULL for
11727 * @entry will make the interactive search code use the built-in popup
11733 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11736 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11737 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11739 if (tree_view->priv->search_custom_entry_set)
11741 if (tree_view->priv->search_entry_changed_id)
11743 g_signal_handler_disconnect (tree_view->priv->search_entry,
11744 tree_view->priv->search_entry_changed_id);
11745 tree_view->priv->search_entry_changed_id = 0;
11747 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11748 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11751 g_object_unref (tree_view->priv->search_entry);
11753 else if (tree_view->priv->search_window)
11755 gtk_widget_destroy (tree_view->priv->search_window);
11757 tree_view->priv->search_window = NULL;
11762 tree_view->priv->search_entry = g_object_ref (entry);
11763 tree_view->priv->search_custom_entry_set = TRUE;
11765 if (tree_view->priv->search_entry_changed_id == 0)
11767 tree_view->priv->search_entry_changed_id =
11768 g_signal_connect (tree_view->priv->search_entry, "changed",
11769 G_CALLBACK (pspp_sheet_view_search_init),
11773 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11774 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11777 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11781 tree_view->priv->search_entry = NULL;
11782 tree_view->priv->search_custom_entry_set = FALSE;
11787 * pspp_sheet_view_set_search_position_func:
11788 * @tree_view: A #PsppSheetView
11789 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11790 * to use the default search position function
11791 * @data: (allow-none): user data to pass to @func, or %NULL
11792 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11794 * Sets the function to use when positioning the search dialog.
11799 pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view,
11800 PsppSheetViewSearchPositionFunc func,
11801 gpointer user_data,
11802 GDestroyNotify destroy)
11804 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11806 if (tree_view->priv->search_position_destroy)
11807 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11809 tree_view->priv->search_position_func = func;
11810 tree_view->priv->search_position_user_data = user_data;
11811 tree_view->priv->search_position_destroy = destroy;
11812 if (tree_view->priv->search_position_func == NULL)
11813 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11817 * pspp_sheet_view_get_search_position_func:
11818 * @tree_view: A #PsppSheetView
11820 * Returns the positioning function currently in use.
11822 * Return value: the currently used function for positioning the search dialog.
11826 PsppSheetViewSearchPositionFunc
11827 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11829 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11831 return tree_view->priv->search_position_func;
11836 pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
11837 PsppSheetView *tree_view)
11839 if (tree_view->priv->disable_popdown)
11842 if (tree_view->priv->search_entry_changed_id)
11844 g_signal_handler_disconnect (tree_view->priv->search_entry,
11845 tree_view->priv->search_entry_changed_id);
11846 tree_view->priv->search_entry_changed_id = 0;
11848 if (tree_view->priv->typeselect_flush_timeout)
11850 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11851 tree_view->priv->typeselect_flush_timeout = 0;
11854 if (gtk_widget_get_visible (search_dialog))
11856 /* send focus-in event */
11857 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11858 gtk_widget_hide (search_dialog);
11859 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11860 send_focus_change (GTK_WIDGET (tree_view), TRUE);
11865 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11866 GtkWidget *search_dialog,
11867 gpointer user_data)
11870 gint tree_x, tree_y;
11871 gint tree_width, tree_height;
11872 GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
11873 GdkScreen *screen = gdk_window_get_screen (tree_window);
11874 GtkRequisition requisition;
11876 GdkRectangle monitor;
11878 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11879 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11881 gtk_widget_realize (search_dialog);
11883 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11884 tree_width = gdk_window_get_width (tree_window);
11885 tree_height = gdk_window_get_height (tree_window);
11887 gtk_widget_size_request (search_dialog, &requisition);
11889 if (tree_x + tree_width > gdk_screen_get_width (screen))
11890 x = gdk_screen_get_width (screen) - requisition.width;
11891 else if (tree_x + tree_width - requisition.width < 0)
11894 x = tree_x + tree_width - requisition.width;
11896 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11897 y = gdk_screen_get_height (screen) - requisition.height;
11898 else if (tree_y + tree_height < 0) /* isn't really possible ... */
11901 y = tree_y + tree_height;
11903 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11907 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11911 PsppSheetView *tree_view = (PsppSheetView *)data;
11913 tree_view->priv->disable_popdown = 1;
11914 g_signal_connect (menu, "hide",
11915 G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11918 #if GTK3_TRANSITION
11919 /* Because we're visible but offscreen, we just set a flag in the preedit
11923 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11924 PsppSheetView *tree_view)
11926 tree_view->priv->imcontext_changed = 1;
11927 if (tree_view->priv->typeselect_flush_timeout)
11929 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11930 tree_view->priv->typeselect_flush_timeout =
11931 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11932 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11940 pspp_sheet_view_search_activate (GtkEntry *entry,
11941 PsppSheetView *tree_view)
11946 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11949 /* If we have a row selected and it's the cursor row, we activate
11951 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11953 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11955 _pspp_sheet_view_find_node (tree_view, path, &node);
11957 if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11958 pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11960 gtk_tree_path_free (path);
11965 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11967 PsppSheetView *tree_view = (PsppSheetView *)data;
11969 tree_view->priv->disable_popdown = 0;
11975 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11978 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11982 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11983 GdkEventAny *event,
11984 PsppSheetView *tree_view)
11986 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11988 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11994 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11995 GdkEventButton *event,
11996 PsppSheetView *tree_view)
11998 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
12000 pspp_sheet_view_search_dialog_hide (widget, tree_view);
12002 if (event->window == tree_view->priv->bin_window)
12003 pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
12009 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
12010 GdkEventScroll *event,
12011 PsppSheetView *tree_view)
12013 gboolean retval = FALSE;
12015 if (event->direction == GDK_SCROLL_UP)
12017 pspp_sheet_view_search_move (widget, tree_view, TRUE);
12020 else if (event->direction == GDK_SCROLL_DOWN)
12022 pspp_sheet_view_search_move (widget, tree_view, FALSE);
12026 /* renew the flush timeout */
12027 if (retval && tree_view->priv->typeselect_flush_timeout
12028 && !tree_view->priv->search_custom_entry_set)
12030 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12031 tree_view->priv->typeselect_flush_timeout =
12032 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12033 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12041 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
12042 GdkEventKey *event,
12043 PsppSheetView *tree_view)
12045 gboolean retval = FALSE;
12047 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
12048 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12050 /* close window and cancel the search */
12051 if (!tree_view->priv->search_custom_entry_set
12052 && (event->keyval == GDK_Escape ||
12053 event->keyval == GDK_Tab ||
12054 event->keyval == GDK_KP_Tab ||
12055 event->keyval == GDK_ISO_Left_Tab))
12057 pspp_sheet_view_search_dialog_hide (widget, tree_view);
12061 /* select previous matching iter */
12062 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
12064 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
12065 gtk_widget_error_bell (widget);
12070 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
12071 && (event->keyval == GDK_g || event->keyval == GDK_G))
12073 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
12074 gtk_widget_error_bell (widget);
12079 /* select next matching iter */
12080 if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
12082 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
12083 gtk_widget_error_bell (widget);
12088 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
12089 && (event->keyval == GDK_g || event->keyval == GDK_G))
12091 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
12092 gtk_widget_error_bell (widget);
12097 /* renew the flush timeout */
12098 if (retval && tree_view->priv->typeselect_flush_timeout
12099 && !tree_view->priv->search_custom_entry_set)
12101 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12102 tree_view->priv->typeselect_flush_timeout =
12103 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12104 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12111 /* this function returns FALSE if there is a search string but
12112 * nothing was found, and TRUE otherwise.
12115 pspp_sheet_view_search_move (GtkWidget *window,
12116 PsppSheetView *tree_view,
12124 GtkTreeModel *model;
12125 PsppSheetSelection *selection;
12127 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
12129 g_return_val_if_fail (text != NULL, FALSE);
12131 len = strlen (text);
12133 if (up && tree_view->priv->selected_iter == 1)
12134 return strlen (text) < 1;
12136 len = strlen (text);
12141 model = pspp_sheet_view_get_model (tree_view);
12142 selection = pspp_sheet_view_get_selection (tree_view);
12145 pspp_sheet_selection_unselect_all (selection);
12146 if (!gtk_tree_model_get_iter_first (model, &iter))
12149 ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
12150 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
12155 tree_view->priv->selected_iter += up?(-1):(1);
12160 /* return to old iter */
12162 gtk_tree_model_get_iter_first (model, &iter);
12163 pspp_sheet_view_search_iter (model, selection,
12165 &count, tree_view->priv->selected_iter);
12171 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
12175 gpointer search_data)
12177 gboolean retval = TRUE;
12179 gchar *normalized_string;
12180 gchar *normalized_key;
12181 gchar *case_normalized_string = NULL;
12182 gchar *case_normalized_key = NULL;
12183 GValue value = {0,};
12184 GValue transformed = {0,};
12186 gtk_tree_model_get_value (model, iter, column, &value);
12188 g_value_init (&transformed, G_TYPE_STRING);
12190 if (!g_value_transform (&value, &transformed))
12192 g_value_unset (&value);
12196 g_value_unset (&value);
12198 str = g_value_get_string (&transformed);
12201 g_value_unset (&transformed);
12205 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
12206 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
12208 if (normalized_string && normalized_key)
12210 case_normalized_string = g_utf8_casefold (normalized_string, -1);
12211 case_normalized_key = g_utf8_casefold (normalized_key, -1);
12213 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
12217 g_value_unset (&transformed);
12218 g_free (normalized_key);
12219 g_free (normalized_string);
12220 g_free (case_normalized_key);
12221 g_free (case_normalized_string);
12227 pspp_sheet_view_search_iter (GtkTreeModel *model,
12228 PsppSheetSelection *selection,
12237 PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
12239 path = gtk_tree_model_get_path (model, iter);
12240 _pspp_sheet_view_find_node (tree_view, path, &node);
12244 gboolean done = FALSE;
12246 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
12251 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
12253 pspp_sheet_selection_select_iter (selection, iter);
12254 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12257 gtk_tree_path_free (path);
12266 node = pspp_sheet_view_node_next (tree_view, node);
12272 has_next = gtk_tree_model_iter_next (model, iter);
12275 gtk_tree_path_next (path);
12278 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12283 gtk_tree_path_free (path);
12285 /* we've run out of tree, done with this func */
12297 pspp_sheet_view_search_init (GtkWidget *entry,
12298 PsppSheetView *tree_view)
12304 GtkTreeModel *model;
12305 PsppSheetSelection *selection;
12307 g_return_if_fail (GTK_IS_ENTRY (entry));
12308 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12310 text = gtk_entry_get_text (GTK_ENTRY (entry));
12312 model = pspp_sheet_view_get_model (tree_view);
12313 selection = pspp_sheet_view_get_selection (tree_view);
12316 pspp_sheet_selection_unselect_all (selection);
12317 if (tree_view->priv->typeselect_flush_timeout
12318 && !tree_view->priv->search_custom_entry_set)
12320 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12321 tree_view->priv->typeselect_flush_timeout =
12322 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12323 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12330 if (!gtk_tree_model_get_iter_first (model, &iter))
12333 ret = pspp_sheet_view_search_iter (model, selection,
12338 tree_view->priv->selected_iter = 1;
12342 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12343 PsppSheetView *tree_view)
12345 if (tree_view->priv->edited_column == NULL)
12348 _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12349 tree_view->priv->edited_column = NULL;
12351 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12352 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12354 g_signal_handlers_disconnect_by_func (cell_editable,
12355 pspp_sheet_view_remove_widget,
12357 g_signal_handlers_disconnect_by_func (cell_editable,
12358 pspp_sheet_view_editable_button_press_event,
12360 g_signal_handlers_disconnect_by_func (cell_editable,
12361 pspp_sheet_view_editable_clicked,
12364 gtk_container_remove (GTK_CONTAINER (tree_view),
12365 GTK_WIDGET (cell_editable));
12367 /* FIXME should only redraw a single node */
12368 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12372 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12373 GtkTreePath *cursor_path)
12376 GdkRectangle background_area;
12377 GdkRectangle cell_area;
12378 GtkCellEditable *editable_widget = NULL;
12379 gchar *path_string;
12380 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12381 gint retval = FALSE;
12384 g_assert (tree_view->priv->focus_column);
12386 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12389 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12390 if (cursor_node < 0)
12393 path_string = gtk_tree_path_to_string (cursor_path);
12394 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12396 pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12397 tree_view->priv->model,
12399 pspp_sheet_view_get_background_area (tree_view,
12401 tree_view->priv->focus_column,
12403 pspp_sheet_view_get_cell_area (tree_view,
12405 tree_view->priv->focus_column,
12408 if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12417 if (editable_widget != NULL)
12421 GtkCellRenderer *cell;
12424 cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12426 _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12429 area.width -= right + left;
12431 pspp_sheet_view_real_start_editing (tree_view,
12432 tree_view->priv->focus_column,
12441 g_free (path_string);
12446 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12447 GdkEventButton *event,
12448 PsppSheetView *sheet_view)
12452 node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12453 "pspp-sheet-view-node"));
12454 return pspp_sheet_view_row_head_clicked (sheet_view,
12456 sheet_view->priv->edited_column,
12461 pspp_sheet_view_editable_clicked (GtkButton *button,
12462 PsppSheetView *sheet_view)
12464 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12469 is_all_selected (GtkWidget *widget)
12471 GtkEntryBuffer *buffer;
12472 gint start_pos, end_pos;
12474 if (!GTK_IS_ENTRY (widget))
12477 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12478 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12479 &start_pos, &end_pos)
12481 && end_pos == gtk_entry_buffer_get_length (buffer));
12485 is_at_left (GtkWidget *widget)
12487 return (GTK_IS_ENTRY (widget)
12488 && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12492 is_at_right (GtkWidget *widget)
12494 GtkEntryBuffer *buffer;
12497 if (!GTK_IS_ENTRY (widget))
12500 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12501 length = gtk_entry_buffer_get_length (buffer);
12502 return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12506 pspp_sheet_view_event (GtkWidget *widget,
12507 GdkEventKey *event,
12508 PsppSheetView *tree_view)
12510 PsppSheetViewColumn *column;
12517 /* Intercept only key press events.
12518 It would make sense to use "key-press-event" instead of "event", but
12519 GtkEntry attaches its own signal handler to "key-press-event" that runs
12520 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12522 if (event->type != GDK_KEY_PRESS)
12525 keyval = event->keyval;
12527 switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12530 switch (event->keyval)
12532 case GDK_Left: case GDK_KP_Left:
12533 case GDK_Home: case GDK_KP_Home:
12534 if (!is_all_selected (widget) && !is_at_left (widget))
12538 case GDK_Right: case GDK_KP_Right:
12539 case GDK_End: case GDK_KP_End:
12540 if (!is_all_selected (widget) && !is_at_right (widget))
12544 case GDK_Up: case GDK_KP_Up:
12545 case GDK_Down: case GDK_KP_Down:
12548 case GDK_Page_Up: case GDK_KP_Page_Up:
12549 case GDK_Page_Down: case GDK_KP_Page_Down:
12560 case GDK_Tab: case GDK_KP_Tab:
12561 case GDK_ISO_Left_Tab:
12570 case GDK_SHIFT_MASK:
12571 switch (event->keyval)
12574 case GDK_ISO_Left_Tab:
12583 case GDK_CONTROL_MASK:
12584 switch (event->keyval)
12586 case GDK_Left: case GDK_KP_Left:
12587 if (!is_all_selected (widget) && !is_at_left (widget))
12591 case GDK_Right: case GDK_KP_Right:
12592 if (!is_all_selected (widget) && !is_at_right (widget))
12596 case GDK_Up: case GDK_KP_Up:
12597 case GDK_Down: case GDK_KP_Down:
12609 row = tree_view->priv->edited_row;
12610 column = tree_view->priv->edited_column;
12611 path = gtk_tree_path_new_from_indices (row, -1);
12613 pspp_sheet_view_stop_editing (tree_view, cancel);
12614 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12616 pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12617 gtk_tree_path_free (path);
12619 handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12620 G_OBJECT (tree_view));
12622 g_signal_stop_emission_by_name (widget, "event");
12624 pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12625 pspp_sheet_view_start_editing (tree_view, path);
12626 gtk_tree_path_free (path);
12632 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12635 PsppSheetView *sheet_view = data;
12637 g_signal_connect (widget, "event",
12638 G_CALLBACK (pspp_sheet_view_event),
12641 if (GTK_IS_CONTAINER (widget))
12642 gtk_container_foreach (GTK_CONTAINER (widget),
12643 pspp_sheet_view_override_cell_keypresses,
12648 pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
12649 PsppSheetViewColumn *column,
12651 GtkCellEditable *cell_editable,
12652 GdkRectangle *cell_area,
12656 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12657 gint pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
12658 GtkRequisition requisition;
12661 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12663 tree_view->priv->edited_column = column;
12664 _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12666 row = gtk_tree_path_get_indices (path)[0];
12667 tree_view->priv->edited_row = row;
12668 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12669 cell_area->y += pre_val - (int)gtk_adjustment_get_value (tree_view->priv->vadjustment);
12671 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12672 pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12673 tree_view->priv->anchor_column = column;
12675 gtk_widget_size_request (GTK_WIDGET (cell_editable), &requisition);
12677 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12679 if (requisition.height < cell_area->height)
12681 gint diff = cell_area->height - requisition.height;
12682 pspp_sheet_view_put (tree_view,
12683 GTK_WIDGET (cell_editable),
12684 cell_area->x, cell_area->y + diff/2,
12685 cell_area->width, requisition.height);
12689 pspp_sheet_view_put (tree_view,
12690 GTK_WIDGET (cell_editable),
12691 cell_area->x, cell_area->y,
12692 cell_area->width, cell_area->height);
12695 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12696 (GdkEvent *)event);
12698 gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12699 g_signal_connect (cell_editable, "remove-widget",
12700 G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12701 if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12702 GTK_IS_BUTTON (cell_editable))
12704 g_signal_connect (cell_editable, "button-press-event",
12705 G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12707 g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12708 GINT_TO_POINTER (row));
12709 g_signal_connect (cell_editable, "clicked",
12710 G_CALLBACK (pspp_sheet_view_editable_clicked),
12714 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12719 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12720 gboolean cancel_editing)
12722 PsppSheetViewColumn *column;
12723 GtkCellRenderer *cell;
12725 if (tree_view->priv->edited_column == NULL)
12729 * This is very evil. We need to do this, because
12730 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12731 * later on. If pspp_sheet_view_row_changed notices
12732 * tree_view->priv->edited_column != NULL, it'll call
12733 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12735 * Please read that again if you intend to modify anything here.
12738 column = tree_view->priv->edited_column;
12739 tree_view->priv->edited_column = NULL;
12741 cell = _pspp_sheet_view_column_get_edited_cell (column);
12742 gtk_cell_renderer_stop_editing (cell, cancel_editing);
12744 if (!cancel_editing)
12745 gtk_cell_editable_editing_done (column->editable_widget);
12747 tree_view->priv->edited_column = column;
12749 gtk_cell_editable_remove_widget (column->editable_widget);
12754 * pspp_sheet_view_set_hover_selection:
12755 * @tree_view: a #PsppSheetView
12756 * @hover: %TRUE to enable hover selection mode
12758 * Enables of disables the hover selection mode of @tree_view.
12759 * Hover selection makes the selected row follow the pointer.
12760 * Currently, this works only for the selection modes
12761 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12766 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12769 hover = hover != FALSE;
12771 if (hover != tree_view->priv->hover_selection)
12773 tree_view->priv->hover_selection = hover;
12775 g_object_notify (G_OBJECT (tree_view), "hover-selection");
12780 * pspp_sheet_view_get_hover_selection:
12781 * @tree_view: a #PsppSheetView
12783 * Returns whether hover selection mode is turned on for @tree_view.
12785 * Return value: %TRUE if @tree_view is in hover selection mode
12790 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12792 return tree_view->priv->hover_selection;
12796 * pspp_sheet_view_set_rubber_banding:
12797 * @tree_view: a #PsppSheetView
12798 * @enable: %TRUE to enable rubber banding
12800 * Enables or disables rubber banding in @tree_view. If the selection mode is
12801 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12802 * banding will allow the user to select multiple rows by dragging the mouse.
12807 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12810 enable = enable != FALSE;
12812 if (enable != tree_view->priv->rubber_banding_enable)
12814 tree_view->priv->rubber_banding_enable = enable;
12816 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12821 * pspp_sheet_view_get_rubber_banding:
12822 * @tree_view: a #PsppSheetView
12824 * Returns whether rubber banding is turned on for @tree_view. If the
12825 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12826 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12827 * select multiple rows by dragging the mouse.
12829 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12834 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12836 return tree_view->priv->rubber_banding_enable;
12840 * pspp_sheet_view_is_rubber_banding_active:
12841 * @tree_view: a #PsppSheetView
12843 * Returns whether a rubber banding operation is currently being done
12846 * Return value: %TRUE if a rubber banding operation is currently being
12847 * done in @tree_view.
12852 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12854 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12856 if (tree_view->priv->rubber_banding_enable
12857 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12864 pspp_sheet_view_grab_notify (GtkWidget *widget,
12865 gboolean was_grabbed)
12867 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12869 tree_view->priv->in_grab = !was_grabbed;
12873 tree_view->priv->pressed_button = -1;
12875 if (tree_view->priv->rubber_band_status)
12876 pspp_sheet_view_stop_rubber_band (tree_view);
12881 pspp_sheet_view_state_changed (GtkWidget *widget,
12882 GtkStateType previous_state)
12884 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12886 if (gtk_widget_get_realized (widget))
12888 GtkStyle *style = gtk_widget_get_style (widget);
12889 gdk_window_set_background (tree_view->priv->bin_window, &style->base[gtk_widget_get_state (widget)]);
12892 gtk_widget_queue_draw (widget);
12896 * pspp_sheet_view_get_grid_lines:
12897 * @tree_view: a #PsppSheetView
12899 * Returns which grid lines are enabled in @tree_view.
12901 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12906 PsppSheetViewGridLines
12907 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12909 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12911 return tree_view->priv->grid_lines;
12915 * pspp_sheet_view_set_grid_lines:
12916 * @tree_view: a #PsppSheetView
12917 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12920 * Sets which grid lines to draw in @tree_view.
12925 pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view,
12926 PsppSheetViewGridLines grid_lines)
12928 PsppSheetViewPrivate *priv;
12929 PsppSheetViewGridLines old_grid_lines;
12931 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12933 priv = tree_view->priv;
12935 old_grid_lines = priv->grid_lines;
12936 priv->grid_lines = grid_lines;
12938 if (old_grid_lines != grid_lines)
12940 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12942 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12947 * pspp_sheet_view_get_special_cells:
12948 * @tree_view: a #PsppSheetView
12950 * Returns which grid lines are enabled in @tree_view.
12952 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12953 * the sheet view contain special cells.
12955 PsppSheetViewSpecialCells
12956 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12958 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12960 return tree_view->priv->special_cells;
12964 * pspp_sheet_view_set_special_cells:
12965 * @tree_view: a #PsppSheetView
12966 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12967 * the sheet view contain special cells.
12969 * Sets whether rows in the sheet view contain special cells, controlling the
12970 * rendering of row selections.
12973 pspp_sheet_view_set_special_cells (PsppSheetView *tree_view,
12974 PsppSheetViewSpecialCells special_cells)
12976 PsppSheetViewPrivate *priv;
12978 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12980 priv = tree_view->priv;
12982 if (priv->special_cells != special_cells)
12984 priv->special_cells = special_cells;
12985 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12986 g_object_notify (G_OBJECT (tree_view), "special-cells");
12991 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12993 /* XXX (re)calculate fixed_height if necessary */
12994 return tree_view->priv->fixed_height;
12998 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
13001 g_return_if_fail (fixed_height > 0);
13003 if (tree_view->priv->fixed_height != fixed_height)
13005 tree_view->priv->fixed_height = fixed_height;
13006 g_object_notify (G_OBJECT (tree_view), "fixed-height");
13008 if (!tree_view->priv->fixed_height_set)
13010 tree_view->priv->fixed_height_set = TRUE;
13011 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
13016 * pspp_sheet_view_set_tooltip_row:
13017 * @tree_view: a #PsppSheetView
13018 * @tooltip: a #GtkTooltip
13019 * @path: a #GtkTreePath
13021 * Sets the tip area of @tooltip to be the area covered by the row at @path.
13022 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13023 * See also gtk_tooltip_set_tip_area().
13028 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
13029 GtkTooltip *tooltip,
13032 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13033 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
13035 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
13039 * pspp_sheet_view_set_tooltip_cell:
13040 * @tree_view: a #PsppSheetView
13041 * @tooltip: a #GtkTooltip
13042 * @path: (allow-none): a #GtkTreePath or %NULL
13043 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
13044 * @cell: (allow-none): a #GtkCellRenderer or %NULL
13046 * Sets the tip area of @tooltip to the area @path, @column and @cell have
13047 * in common. For example if @path is %NULL and @column is set, the tip
13048 * area will be set to the full area covered by @column. See also
13049 * gtk_tooltip_set_tip_area().
13051 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13056 pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view,
13057 GtkTooltip *tooltip,
13059 PsppSheetViewColumn *column,
13060 GtkCellRenderer *cell)
13064 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13065 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
13066 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
13067 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
13069 /* Determine x values. */
13070 if (column && cell)
13075 pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
13076 pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
13078 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13081 rect.width = width;
13087 pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
13088 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13091 rect.width = tmp.width;
13095 GtkAllocation allocation;
13096 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
13098 rect.width = allocation.width;
13101 /* Determine y values. */
13106 pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
13107 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13110 rect.height = tmp.height;
13115 rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
13118 gtk_tooltip_set_tip_area (tooltip, &rect);
13122 * pspp_sheet_view_get_tooltip_context:
13123 * @tree_view: a #PsppSheetView
13124 * @x: the x coordinate (relative to widget coordinates)
13125 * @y: the y coordinate (relative to widget coordinates)
13126 * @keyboard_tip: whether this is a keyboard tooltip or not
13127 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
13128 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
13129 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
13131 * This function is supposed to be used in a #GtkWidget::query-tooltip
13132 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
13133 * which are received in the signal handler, should be passed to this
13134 * function without modification.
13136 * The return value indicates whether there is a tree view row at the given
13137 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
13138 * tooltips the row returned will be the cursor row. When %TRUE, then any of
13139 * @model, @path and @iter which have been provided will be set to point to
13140 * that row and the corresponding model. @x and @y will always be converted
13141 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
13143 * Return value: whether or not the given tooltip context points to a row.
13148 pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view,
13151 gboolean keyboard_tip,
13152 GtkTreeModel **model,
13153 GtkTreePath **path,
13156 GtkTreePath *tmppath = NULL;
13158 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
13159 g_return_val_if_fail (x != NULL, FALSE);
13160 g_return_val_if_fail (y != NULL, FALSE);
13164 pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
13171 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
13174 if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
13175 &tmppath, NULL, NULL, NULL))
13180 *model = pspp_sheet_view_get_model (tree_view);
13183 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
13189 gtk_tree_path_free (tmppath);
13195 pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget,
13198 gboolean keyboard_tip,
13199 GtkTooltip *tooltip,
13202 GValue value = { 0, };
13203 GValue transformed = { 0, };
13206 GtkTreeModel *model;
13207 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
13209 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
13212 &model, &path, &iter))
13215 gtk_tree_model_get_value (model, &iter,
13216 tree_view->priv->tooltip_column, &value);
13218 g_value_init (&transformed, G_TYPE_STRING);
13220 if (!g_value_transform (&value, &transformed))
13222 g_value_unset (&value);
13223 gtk_tree_path_free (path);
13228 g_value_unset (&value);
13230 if (!g_value_get_string (&transformed))
13232 g_value_unset (&transformed);
13233 gtk_tree_path_free (path);
13238 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
13239 pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
13241 gtk_tree_path_free (path);
13242 g_value_unset (&transformed);
13248 * pspp_sheet_view_set_tooltip_column:
13249 * @tree_view: a #PsppSheetView
13250 * @column: an integer, which is a valid column number for @tree_view's model
13252 * If you only plan to have simple (text-only) tooltips on full rows, you
13253 * can use this function to have #PsppSheetView handle these automatically
13254 * for you. @column should be set to the column in @tree_view's model
13255 * containing the tooltip texts, or -1 to disable this feature.
13257 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
13258 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
13260 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
13261 * so &, <, etc have to be escaped in the text.
13266 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
13269 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13271 if (column == tree_view->priv->tooltip_column)
13276 g_signal_handlers_disconnect_by_func (tree_view,
13277 pspp_sheet_view_set_tooltip_query_cb,
13279 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13283 if (tree_view->priv->tooltip_column == -1)
13285 g_signal_connect (tree_view, "query-tooltip",
13286 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13287 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13291 tree_view->priv->tooltip_column = column;
13292 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13296 * pspp_sheet_view_get_tooltip_column:
13297 * @tree_view: a #PsppSheetView
13299 * Returns the column of @tree_view's model which is being used for
13300 * displaying tooltips on @tree_view's rows.
13302 * Return value: the index of the tooltip column that is currently being
13303 * used, or -1 if this is disabled.
13308 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13310 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13312 return tree_view->priv->tooltip_column;
13316 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13317 GValue *return_accu,
13318 const GValue *handler_return,
13321 gboolean continue_emission;
13322 gboolean signal_handled;
13324 signal_handled = g_value_get_boolean (handler_return);
13325 g_value_set_boolean (return_accu, signal_handled);
13326 continue_emission = !signal_handled;
13328 return continue_emission;
13333 pspp_sheet_view_grid_lines_get_type (void)
13335 static GType etype = 0;
13336 if (G_UNLIKELY(etype == 0)) {
13337 static const GEnumValue values[] = {
13338 { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13339 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13340 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13341 { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13344 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13350 pspp_sheet_view_special_cells_get_type (void)
13352 static GType etype = 0;
13353 if (G_UNLIKELY(etype == 0)) {
13354 static const GEnumValue values[] = {
13355 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13356 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13357 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13360 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);