1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012, 2013 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Library General Public
22 * License as published by the Free Software Foundation; either
23 * version 2 of the License, or (at your option) any later version.
25 * This library is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Library General Public License for more details.
30 * You should have received a copy of the GNU Library General Public
31 * License along with this library; if not, write to the
32 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
33 * Boston, MA 02111-1307, USA.
38 #include "ui/gui/pspp-sheet-private.h"
42 #include <gdk/gdkkeysyms.h>
43 #include <gdk/gdkkeysyms-compat.h>
46 #include "ui/gui/psppire-marshal.h"
47 #include "ui/gui/pspp-sheet-selection.h"
49 #define P_(STRING) STRING
50 #define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
51 #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
53 /* Many keyboard shortcuts for Mac are the same as for X
54 * except they use Command key instead of Control (e.g. Cut,
55 * Copy, Paste). This symbol is for those simple cases. */
56 #ifndef GDK_WINDOWING_QUARTZ
57 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_CONTROL_MASK
59 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_META_MASK
62 #define PSPP_SHEET_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
63 #define PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC (PSPP_SHEET_VIEW_PRIORITY_VALIDATE + 2)
64 #define PSPP_SHEET_VIEW_TIME_MS_PER_IDLE 30
65 #define SCROLL_EDGE_SIZE 15
66 #define EXPANDER_EXTRA_PADDING 4
67 #define PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT 5000
69 /* The "background" areas of all rows/cells add up to cover the entire tree.
70 * The background includes all inter-row and inter-cell spacing.
71 * The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
72 * i.e. just the cells, no spacing.
75 #define BACKGROUND_HEIGHT(tree_view) (tree_view->priv->fixed_height)
76 #define CELL_HEIGHT(tree_view, separator) ((BACKGROUND_HEIGHT (tree_view)) - (separator))
78 /* Translate from bin_window coordinates to rbtree (tree coordinates) and
81 #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->priv->dy)
82 #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->priv->dy)
84 /* This is in bin_window coordinates */
85 #define BACKGROUND_FIRST_PIXEL(tree_view,node) (RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, pspp_sheet_view_node_find_offset (tree_view, (node))))
86 #define CELL_FIRST_PIXEL(tree_view,node,separator) (BACKGROUND_FIRST_PIXEL (tree_view,node) + separator/2)
88 #define ROW_HEIGHT(tree_view) \
89 ((tree_view->priv->fixed_height > 0) ? (tree_view->priv->fixed_height) : (tree_view)->priv->expander_size)
92 typedef struct _PsppSheetViewChild PsppSheetViewChild;
93 struct _PsppSheetViewChild
103 typedef struct _TreeViewDragInfo TreeViewDragInfo;
104 struct _TreeViewDragInfo
106 GdkModifierType start_button_mask;
107 GtkTargetList *_unused_source_target_list;
108 GdkDragAction source_actions;
110 GtkTargetList *_unused_dest_target_list;
112 guint source_set : 1;
128 START_INTERACTIVE_SEARCH,
140 PROP_HEADERS_VISIBLE,
141 PROP_HEADERS_CLICKABLE,
146 PROP_HOVER_SELECTION,
148 PROP_ENABLE_GRID_LINES,
152 PROP_FIXED_HEIGHT_SET
156 static void pspp_sheet_view_finalize (GObject *object);
157 static void pspp_sheet_view_set_property (GObject *object,
161 static void pspp_sheet_view_get_property (GObject *object,
166 static void pspp_sheet_view_dispose (GObject *object);
168 /* gtkwidget signals */
169 static void pspp_sheet_view_realize (GtkWidget *widget);
170 static void pspp_sheet_view_unrealize (GtkWidget *widget);
171 static void pspp_sheet_view_map (GtkWidget *widget);
172 static void pspp_sheet_view_size_request (GtkWidget *widget,
173 GtkRequisition *requisition);
174 static void pspp_sheet_view_size_allocate (GtkWidget *widget,
175 GtkAllocation *allocation);
176 static gboolean pspp_sheet_view_draw (GtkWidget *widget,
178 static gboolean pspp_sheet_view_key_press (GtkWidget *widget,
180 static gboolean pspp_sheet_view_key_release (GtkWidget *widget,
182 static gboolean pspp_sheet_view_motion (GtkWidget *widget,
183 GdkEventMotion *event);
184 static gboolean pspp_sheet_view_enter_notify (GtkWidget *widget,
185 GdkEventCrossing *event);
186 static gboolean pspp_sheet_view_leave_notify (GtkWidget *widget,
187 GdkEventCrossing *event);
188 static gboolean pspp_sheet_view_button_press (GtkWidget *widget,
189 GdkEventButton *event);
190 static gboolean pspp_sheet_view_button_release (GtkWidget *widget,
191 GdkEventButton *event);
192 static gboolean pspp_sheet_view_grab_broken (GtkWidget *widget,
193 GdkEventGrabBroken *event);
195 static void pspp_sheet_view_set_focus_child (GtkContainer *container,
197 static gint pspp_sheet_view_focus_out (GtkWidget *widget,
198 GdkEventFocus *event);
199 static gint pspp_sheet_view_focus (GtkWidget *widget,
200 GtkDirectionType direction);
201 static void pspp_sheet_view_grab_focus (GtkWidget *widget);
202 static void pspp_sheet_view_style_set (GtkWidget *widget,
203 GtkStyle *previous_style);
204 static void pspp_sheet_view_grab_notify (GtkWidget *widget,
205 gboolean was_grabbed);
206 static void pspp_sheet_view_state_changed (GtkWidget *widget,
207 GtkStateType previous_state);
209 /* container signals */
210 static void pspp_sheet_view_remove (GtkContainer *container,
212 static void pspp_sheet_view_forall (GtkContainer *container,
213 gboolean include_internals,
214 GtkCallback callback,
215 gpointer callback_data);
217 /* Source side drag signals */
218 static void pspp_sheet_view_drag_begin (GtkWidget *widget,
219 GdkDragContext *context);
220 static void pspp_sheet_view_drag_end (GtkWidget *widget,
221 GdkDragContext *context);
222 static void pspp_sheet_view_drag_data_get (GtkWidget *widget,
223 GdkDragContext *context,
224 GtkSelectionData *selection_data,
227 static void pspp_sheet_view_drag_data_delete (GtkWidget *widget,
228 GdkDragContext *context);
230 /* Target side drag signals */
231 static void pspp_sheet_view_drag_leave (GtkWidget *widget,
232 GdkDragContext *context,
234 static gboolean pspp_sheet_view_drag_motion (GtkWidget *widget,
235 GdkDragContext *context,
239 static gboolean pspp_sheet_view_drag_drop (GtkWidget *widget,
240 GdkDragContext *context,
244 static void pspp_sheet_view_drag_data_received (GtkWidget *widget,
245 GdkDragContext *context,
248 GtkSelectionData *selection_data,
252 /* tree_model signals */
253 static void pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
255 GtkAdjustment *vadj);
256 static gboolean pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
257 GtkMovementStep step,
259 static gboolean pspp_sheet_view_real_select_all (PsppSheetView *tree_view);
260 static gboolean pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view);
261 static gboolean pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
262 gboolean start_editing,
263 PsppSheetSelectMode mode);
264 static gboolean pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view);
265 static void pspp_sheet_view_row_changed (GtkTreeModel *model,
269 static void pspp_sheet_view_row_inserted (GtkTreeModel *model,
273 static void pspp_sheet_view_row_deleted (GtkTreeModel *model,
276 static void pspp_sheet_view_rows_reordered (GtkTreeModel *model,
282 /* Incremental reflow */
283 static gint validate_row (PsppSheetView *tree_view,
287 static void validate_visible_area (PsppSheetView *tree_view);
288 static gboolean validate_rows_handler (PsppSheetView *tree_view);
289 static gboolean presize_handler_callback (gpointer data);
290 static void install_presize_handler (PsppSheetView *tree_view);
291 static void install_scroll_sync_handler (PsppSheetView *tree_view);
292 static void pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
295 static void pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view);
296 static void pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view);
297 static void invalidate_empty_focus (PsppSheetView *tree_view);
299 /* Internal functions */
300 static GtkAdjustment *pspp_sheet_view_do_get_hadjustment (PsppSheetView *);
301 static GtkAdjustment *pspp_sheet_view_do_get_vadjustment (PsppSheetView *);
302 static void pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
303 GtkAdjustment *adjustment);
304 static void pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
305 GtkAdjustment *adjustment);
306 static void pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
309 gboolean add_shifted_binding,
310 GtkMovementStep step,
312 static void pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
314 const GdkRectangle *clip_rect);
315 static gint pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
318 static void pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
319 PsppSheetView *tree_view);
320 static void pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
322 static void pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
323 PsppSheetViewColumn *column,
324 gboolean focus_to_cell);
325 static gboolean pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
326 GdkEventMotion *event);
327 static void pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view);
328 static gboolean pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
330 PsppSheetSelectMode mode);
331 static void pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
333 PsppSheetSelectMode mode);
334 static void pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
336 PsppSheetSelectMode mode);
337 static void pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
339 PsppSheetSelectMode mode);
340 static void pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
342 static void pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
344 PsppSheetSelectMode mode);
345 static void pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
347 gboolean clear_and_select,
349 PsppSheetSelectMode mode);
350 static gboolean pspp_sheet_view_has_special_cell (PsppSheetView *tree_view);
351 static void pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view);
352 static void update_prelight (PsppSheetView *tree_view,
355 static void initialize_fixed_height_mode (PsppSheetView *tree_view);
357 /* interactive search */
358 static void pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view);
359 static void pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
360 PsppSheetView *tree_view);
361 static void pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
362 GtkWidget *search_dialog,
364 static void pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
368 static void pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
369 PsppSheetView *tree_view);
371 static void pspp_sheet_view_search_activate (GtkEntry *entry,
372 PsppSheetView *tree_view);
373 static gboolean pspp_sheet_view_real_search_enable_popdown(gpointer data);
374 static void pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
376 static gboolean pspp_sheet_view_search_delete_event (GtkWidget *widget,
378 PsppSheetView *tree_view);
379 static gboolean pspp_sheet_view_search_button_press_event (GtkWidget *widget,
380 GdkEventButton *event,
381 PsppSheetView *tree_view);
382 static gboolean pspp_sheet_view_search_scroll_event (GtkWidget *entry,
383 GdkEventScroll *event,
384 PsppSheetView *tree_view);
385 static gboolean pspp_sheet_view_search_key_press_event (GtkWidget *entry,
387 PsppSheetView *tree_view);
388 static gboolean pspp_sheet_view_search_move (GtkWidget *window,
389 PsppSheetView *tree_view,
391 static gboolean pspp_sheet_view_search_equal_func (GtkTreeModel *model,
395 gpointer search_data);
396 static gboolean pspp_sheet_view_search_iter (GtkTreeModel *model,
397 PsppSheetSelection *selection,
402 static void pspp_sheet_view_search_init (GtkWidget *entry,
403 PsppSheetView *tree_view);
404 static void pspp_sheet_view_put (PsppSheetView *tree_view,
405 GtkWidget *child_widget,
410 static gboolean pspp_sheet_view_start_editing (PsppSheetView *tree_view,
411 GtkTreePath *cursor_path);
412 static gboolean pspp_sheet_view_editable_button_press_event (GtkWidget *,
415 static void pspp_sheet_view_editable_clicked (GtkButton *, PsppSheetView *);
416 static void pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
417 PsppSheetViewColumn *column,
419 GtkCellEditable *cell_editable,
420 GdkRectangle *cell_area,
423 static gboolean pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
424 gboolean keybinding);
425 static gboolean pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view);
426 static PsppSheetViewColumn *pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
427 PsppSheetViewColumn *column,
430 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
431 PsppSheetViewColumn *column,
432 const GdkRectangle *background_area,
433 gboolean subtract_focus_rect,
434 GdkRectangle *cell_area);
435 static gint pspp_sheet_view_find_offset (PsppSheetView *tree_view,
440 static void pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
444 static void pspp_sheet_view_buildable_init (GtkBuildableIface *iface);
447 static gboolean scroll_row_timeout (gpointer data);
448 static void add_scroll_timeout (PsppSheetView *tree_view);
449 static void remove_scroll_timeout (PsppSheetView *tree_view);
451 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
453 static GtkBindingSet *edit_bindings;
460 G_DEFINE_TYPE_WITH_CODE (PsppSheetView, pspp_sheet_view, GTK_TYPE_CONTAINER,
461 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
462 pspp_sheet_view_buildable_init)
463 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
466 pspp_sheet_view_get_preferred_width (GtkWidget *widget,
470 GtkRequisition requisition;
472 pspp_sheet_view_size_request (widget, &requisition);
474 *minimal_width = *natural_width = requisition.width;
478 pspp_sheet_view_get_preferred_height (GtkWidget *widget,
479 gint *minimal_height,
480 gint *natural_height)
482 GtkRequisition requisition;
484 pspp_sheet_view_size_request (widget, &requisition);
486 *minimal_height = *natural_height = requisition.height;
490 pspp_sheet_view_class_init (PsppSheetViewClass *class)
492 GObjectClass *o_class;
493 GtkWidgetClass *widget_class;
494 GtkContainerClass *container_class;
495 GtkBindingSet *binding_set[2];
498 binding_set[0] = gtk_binding_set_by_class (class);
500 binding_set[1] = gtk_binding_set_new ("PsppSheetViewEditing");
501 edit_bindings = binding_set[1];
503 o_class = (GObjectClass *) class;
504 widget_class = (GtkWidgetClass *) class;
505 container_class = (GtkContainerClass *) class;
507 /* GObject signals */
508 o_class->set_property = pspp_sheet_view_set_property;
509 o_class->get_property = pspp_sheet_view_get_property;
510 o_class->finalize = pspp_sheet_view_finalize;
511 o_class->dispose = pspp_sheet_view_dispose;
513 /* GtkWidget signals */
514 widget_class->map = pspp_sheet_view_map;
515 widget_class->realize = pspp_sheet_view_realize;
516 widget_class->unrealize = pspp_sheet_view_unrealize;
517 widget_class->get_preferred_width = pspp_sheet_view_get_preferred_width;
518 widget_class->get_preferred_height = pspp_sheet_view_get_preferred_height;
519 widget_class->size_allocate = pspp_sheet_view_size_allocate;
520 widget_class->button_press_event = pspp_sheet_view_button_press;
521 widget_class->button_release_event = pspp_sheet_view_button_release;
522 widget_class->grab_broken_event = pspp_sheet_view_grab_broken;
523 /*widget_class->configure_event = pspp_sheet_view_configure;*/
524 widget_class->motion_notify_event = pspp_sheet_view_motion;
525 widget_class->draw = pspp_sheet_view_draw;
526 widget_class->key_press_event = pspp_sheet_view_key_press;
527 widget_class->key_release_event = pspp_sheet_view_key_release;
528 widget_class->enter_notify_event = pspp_sheet_view_enter_notify;
529 widget_class->leave_notify_event = pspp_sheet_view_leave_notify;
530 widget_class->focus_out_event = pspp_sheet_view_focus_out;
531 widget_class->drag_begin = pspp_sheet_view_drag_begin;
532 widget_class->drag_end = pspp_sheet_view_drag_end;
533 widget_class->drag_data_get = pspp_sheet_view_drag_data_get;
534 widget_class->drag_data_delete = pspp_sheet_view_drag_data_delete;
535 widget_class->drag_leave = pspp_sheet_view_drag_leave;
536 widget_class->drag_motion = pspp_sheet_view_drag_motion;
537 widget_class->drag_drop = pspp_sheet_view_drag_drop;
538 widget_class->drag_data_received = pspp_sheet_view_drag_data_received;
539 widget_class->focus = pspp_sheet_view_focus;
540 widget_class->grab_focus = pspp_sheet_view_grab_focus;
541 widget_class->style_set = pspp_sheet_view_style_set;
542 widget_class->grab_notify = pspp_sheet_view_grab_notify;
543 widget_class->state_changed = pspp_sheet_view_state_changed;
545 /* GtkContainer signals */
546 container_class->remove = pspp_sheet_view_remove;
547 container_class->forall = pspp_sheet_view_forall;
548 container_class->set_focus_child = pspp_sheet_view_set_focus_child;
550 class->set_scroll_adjustments = pspp_sheet_view_set_adjustments;
551 class->move_cursor = pspp_sheet_view_real_move_cursor;
552 class->select_all = pspp_sheet_view_real_select_all;
553 class->unselect_all = pspp_sheet_view_real_unselect_all;
554 class->select_cursor_row = pspp_sheet_view_real_select_cursor_row;
555 class->toggle_cursor_row = pspp_sheet_view_real_toggle_cursor_row;
556 class->start_interactive_search = pspp_sheet_view_start_interactive_search;
560 g_object_class_install_property (o_class,
562 g_param_spec_object ("model",
563 P_("TreeView Model"),
564 P_("The model for the tree view"),
566 GTK_PARAM_READWRITE));
568 g_object_class_override_property (o_class, PROP_HADJUSTMENT, "hadjustment");
569 g_object_class_override_property (o_class, PROP_VADJUSTMENT, "vadjustment");
570 g_object_class_override_property (o_class, PROP_HSCROLL_POLICY, "hscroll-policy");
571 g_object_class_override_property (o_class, PROP_VSCROLL_POLICY, "vscroll-policy");
573 g_object_class_install_property (o_class,
574 PROP_HEADERS_VISIBLE,
575 g_param_spec_boolean ("headers-visible",
576 P_("Headers Visible"),
577 P_("Show the column header buttons"),
579 GTK_PARAM_READWRITE));
581 g_object_class_install_property (o_class,
582 PROP_HEADERS_CLICKABLE,
583 g_param_spec_boolean ("headers-clickable",
584 P_("Headers Clickable"),
585 P_("Column headers respond to click events"),
587 GTK_PARAM_READWRITE));
589 g_object_class_install_property (o_class,
591 g_param_spec_boolean ("reorderable",
593 P_("View is reorderable"),
595 GTK_PARAM_READWRITE));
597 g_object_class_install_property (o_class,
599 g_param_spec_boolean ("rules-hint",
601 P_("Set a hint to the theme engine to draw rows in alternating colors"),
603 GTK_PARAM_READWRITE));
605 g_object_class_install_property (o_class,
607 g_param_spec_boolean ("enable-search",
609 P_("View allows user to search through columns interactively"),
611 GTK_PARAM_READWRITE));
613 g_object_class_install_property (o_class,
615 g_param_spec_int ("search-column",
617 P_("Model column to search through during interactive search"),
621 GTK_PARAM_READWRITE));
624 * PsppSheetView:hover-selection:
626 * Enables of disables the hover selection mode of @tree_view.
627 * Hover selection makes the selected row follow the pointer.
628 * Currently, this works only for the selection modes
629 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
631 * This mode is primarily intended for treeviews in popups, e.g.
632 * in #GtkComboBox or #GtkEntryCompletion.
636 g_object_class_install_property (o_class,
637 PROP_HOVER_SELECTION,
638 g_param_spec_boolean ("hover-selection",
639 P_("Hover Selection"),
640 P_("Whether the selection should follow the pointer"),
642 GTK_PARAM_READWRITE));
644 g_object_class_install_property (o_class,
646 g_param_spec_boolean ("rubber-banding",
647 P_("Rubber Banding"),
648 P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
650 GTK_PARAM_READWRITE));
652 g_object_class_install_property (o_class,
653 PROP_ENABLE_GRID_LINES,
654 g_param_spec_enum ("enable-grid-lines",
655 P_("Enable Grid Lines"),
656 P_("Whether grid lines should be drawn in the tree view"),
657 PSPP_TYPE_SHEET_VIEW_GRID_LINES,
658 PSPP_SHEET_VIEW_GRID_LINES_NONE,
659 GTK_PARAM_READWRITE));
661 g_object_class_install_property (o_class,
663 g_param_spec_int ("tooltip-column",
664 P_("Tooltip Column"),
665 P_("The column in the model containing the tooltip texts for the rows"),
669 GTK_PARAM_READWRITE));
671 g_object_class_install_property (o_class,
673 g_param_spec_enum ("special-cells",
675 P_("Whether rows have special cells."),
676 PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS,
677 PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT,
678 GTK_PARAM_READWRITE));
680 g_object_class_install_property (o_class,
682 g_param_spec_int ("fixed-height",
684 P_("Height of a single row. Normally the height of a row is determined automatically. Writing this property sets fixed-height-set to true, preventing this property's value from changing."),
688 GTK_PARAM_READWRITE));
690 g_object_class_install_property (o_class,
691 PROP_FIXED_HEIGHT_SET,
692 g_param_spec_boolean ("fixed-height-set",
693 P_("Fixed Height Set"),
694 P_("Whether fixed-height was set externally."),
696 GTK_PARAM_READWRITE));
698 /* Style properties */
699 #define _TREE_VIEW_EXPANDER_SIZE 12
700 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
701 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
703 gtk_widget_class_install_style_property (widget_class,
704 g_param_spec_int ("expander-size",
706 P_("Size of the expander arrow"),
709 _TREE_VIEW_EXPANDER_SIZE,
710 GTK_PARAM_READABLE));
712 gtk_widget_class_install_style_property (widget_class,
713 g_param_spec_int ("vertical-separator",
714 P_("Vertical Separator Width"),
715 P_("Vertical space between cells. Must be an even number"),
718 _TREE_VIEW_VERTICAL_SEPARATOR,
719 GTK_PARAM_READABLE));
721 gtk_widget_class_install_style_property (widget_class,
722 g_param_spec_int ("horizontal-separator",
723 P_("Horizontal Separator Width"),
724 P_("Horizontal space between cells. Must be an even number"),
727 _TREE_VIEW_HORIZONTAL_SEPARATOR,
728 GTK_PARAM_READABLE));
730 gtk_widget_class_install_style_property (widget_class,
731 g_param_spec_boolean ("allow-rules",
733 P_("Allow drawing of alternating color rows"),
735 GTK_PARAM_READABLE));
737 gtk_widget_class_install_style_property (widget_class,
738 g_param_spec_boxed ("even-row-color",
739 P_("Even Row Color"),
740 P_("Color to use for even rows"),
742 GTK_PARAM_READABLE));
744 gtk_widget_class_install_style_property (widget_class,
745 g_param_spec_boxed ("odd-row-color",
747 P_("Color to use for odd rows"),
749 GTK_PARAM_READABLE));
751 gtk_widget_class_install_style_property (widget_class,
752 g_param_spec_boolean ("row-ending-details",
753 P_("Row Ending details"),
754 P_("Enable extended row background theming"),
756 GTK_PARAM_READABLE));
758 gtk_widget_class_install_style_property (widget_class,
759 g_param_spec_int ("grid-line-width",
760 P_("Grid line width"),
761 P_("Width, in pixels, of the tree view grid lines"),
763 GTK_PARAM_READABLE));
765 gtk_widget_class_install_style_property (widget_class,
766 g_param_spec_int ("tree-line-width",
767 P_("Tree line width"),
768 P_("Width, in pixels, of the tree view lines"),
770 GTK_PARAM_READABLE));
772 gtk_widget_class_install_style_property (widget_class,
773 g_param_spec_string ("tree-line-pattern",
774 P_("Tree line pattern"),
775 P_("Dash pattern used to draw the tree view lines"),
777 GTK_PARAM_READABLE));
782 * PsppSheetView::set-scroll-adjustments
783 * @horizontal: the horizontal #GtkAdjustment
784 * @vertical: the vertical #GtkAdjustment
786 * Set the scroll adjustments for the tree view. Usually scrolled containers
787 * like #GtkScrolledWindow will emit this signal to connect two instances
788 * of #GtkScrollbar to the scroll directions of the #PsppSheetView.
790 widget_class->set_scroll_adjustments_signal =
791 g_signal_new ("set-scroll-adjustments",
792 G_TYPE_FROM_CLASS (o_class),
793 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
794 G_STRUCT_OFFSET (PsppSheetViewClass, set_scroll_adjustments),
796 psppire_marshal_VOID__OBJECT_OBJECT,
799 GTK_TYPE_ADJUSTMENT);
803 * PsppSheetView::row-activated:
804 * @tree_view: the object on which the signal is emitted
805 * @path: the #GtkTreePath for the activated row
806 * @column: the #PsppSheetViewColumn in which the activation occurred
808 * The "row-activated" signal is emitted when the method
809 * pspp_sheet_view_row_activated() is called or the user double clicks
810 * a treeview row. It is also emitted when a non-editable row is
811 * selected and one of the keys: Space, Shift+Space, Return or
814 * For selection handling refer to the <link linkend="TreeWidget">tree
815 * widget conceptual overview</link> as well as #PsppSheetSelection.
817 tree_view_signals[ROW_ACTIVATED] =
818 g_signal_new ("row-activated",
819 G_TYPE_FROM_CLASS (o_class),
820 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
821 G_STRUCT_OFFSET (PsppSheetViewClass, row_activated),
823 psppire_marshal_VOID__BOXED_OBJECT,
826 PSPP_TYPE_SHEET_VIEW_COLUMN);
829 * PsppSheetView::columns-changed:
830 * @tree_view: the object on which the signal is emitted
832 * The number of columns of the treeview has changed.
834 tree_view_signals[COLUMNS_CHANGED] =
835 g_signal_new ("columns-changed",
836 G_TYPE_FROM_CLASS (o_class),
838 G_STRUCT_OFFSET (PsppSheetViewClass, columns_changed),
840 g_cclosure_marshal_VOID__VOID,
844 * PsppSheetView::cursor-changed:
845 * @tree_view: the object on which the signal is emitted
847 * The position of the cursor (focused cell) has changed.
849 tree_view_signals[CURSOR_CHANGED] =
850 g_signal_new ("cursor-changed",
851 G_TYPE_FROM_CLASS (o_class),
853 G_STRUCT_OFFSET (PsppSheetViewClass, cursor_changed),
855 g_cclosure_marshal_VOID__VOID,
858 tree_view_signals[MOVE_CURSOR] =
859 g_signal_new ("move-cursor",
860 G_TYPE_FROM_CLASS (o_class),
861 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
862 G_STRUCT_OFFSET (PsppSheetViewClass, move_cursor),
864 psppire_marshal_BOOLEAN__ENUM_INT,
866 GTK_TYPE_MOVEMENT_STEP,
869 tree_view_signals[SELECT_ALL] =
870 g_signal_new ("select-all",
871 G_TYPE_FROM_CLASS (o_class),
872 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
873 G_STRUCT_OFFSET (PsppSheetViewClass, select_all),
875 psppire_marshal_BOOLEAN__VOID,
878 tree_view_signals[UNSELECT_ALL] =
879 g_signal_new ("unselect-all",
880 G_TYPE_FROM_CLASS (o_class),
881 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
882 G_STRUCT_OFFSET (PsppSheetViewClass, unselect_all),
884 psppire_marshal_BOOLEAN__VOID,
887 tree_view_signals[SELECT_CURSOR_ROW] =
888 g_signal_new ("select-cursor-row",
889 G_TYPE_FROM_CLASS (o_class),
890 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
891 G_STRUCT_OFFSET (PsppSheetViewClass, select_cursor_row),
893 psppire_marshal_BOOLEAN__BOOLEAN,
895 G_TYPE_BOOLEAN, G_TYPE_INT);
897 tree_view_signals[TOGGLE_CURSOR_ROW] =
898 g_signal_new ("toggle-cursor-row",
899 G_TYPE_FROM_CLASS (o_class),
900 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
901 G_STRUCT_OFFSET (PsppSheetViewClass, toggle_cursor_row),
903 psppire_marshal_BOOLEAN__VOID,
906 tree_view_signals[START_INTERACTIVE_SEARCH] =
907 g_signal_new ("start-interactive-search",
908 G_TYPE_FROM_CLASS (o_class),
909 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
910 G_STRUCT_OFFSET (PsppSheetViewClass, start_interactive_search),
912 psppire_marshal_BOOLEAN__VOID,
916 for (i = 0; i < 2; i++)
918 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Up, 0, TRUE,
919 GTK_MOVEMENT_DISPLAY_LINES, -1);
920 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Up, 0, TRUE,
921 GTK_MOVEMENT_DISPLAY_LINES, -1);
923 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Down, 0, TRUE,
924 GTK_MOVEMENT_DISPLAY_LINES, 1);
925 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Down, 0, TRUE,
926 GTK_MOVEMENT_DISPLAY_LINES, 1);
928 pspp_sheet_view_add_move_binding (binding_set[i], GDK_p, GDK_CONTROL_MASK, FALSE,
929 GTK_MOVEMENT_DISPLAY_LINES, -1);
931 pspp_sheet_view_add_move_binding (binding_set[i], GDK_n, GDK_CONTROL_MASK, FALSE,
932 GTK_MOVEMENT_DISPLAY_LINES, 1);
934 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Home, 0, TRUE,
935 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
936 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Home, 0, TRUE,
937 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
939 pspp_sheet_view_add_move_binding (binding_set[i], GDK_End, 0, TRUE,
940 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
941 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_End, 0, TRUE,
942 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
944 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Up, 0, TRUE,
945 GTK_MOVEMENT_PAGES, -1);
946 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Up, 0, TRUE,
947 GTK_MOVEMENT_PAGES, -1);
949 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Down, 0, TRUE,
950 GTK_MOVEMENT_PAGES, 1);
951 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Down, 0, TRUE,
952 GTK_MOVEMENT_PAGES, 1);
955 gtk_binding_entry_add_signal (binding_set[i], GDK_Up, GDK_CONTROL_MASK, "move-cursor", 2,
956 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
959 gtk_binding_entry_add_signal (binding_set[i], GDK_Down, GDK_CONTROL_MASK, "move-cursor", 2,
960 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
963 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, 0, "move-cursor", 2,
964 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
967 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, 0, "move-cursor", 2,
968 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
971 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, 0, "move-cursor", 2,
972 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
975 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, GDK_SHIFT_MASK, "move-cursor", 2,
976 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
979 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, 0, "move-cursor", 2,
980 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
983 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, 0, "move-cursor", 2,
984 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
987 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, GDK_CONTROL_MASK,
989 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
992 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, GDK_CONTROL_MASK,
994 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
997 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, GDK_CONTROL_MASK,
999 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1002 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, GDK_CONTROL_MASK,
1004 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1007 gtk_binding_entry_add_signal (binding_set[i], GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
1009 gtk_binding_entry_add_signal (binding_set[i], GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
1012 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1013 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1015 gtk_binding_entry_add_signal (binding_set[0], GDK_a, GDK_CONTROL_MASK, "select-all", 0);
1016 gtk_binding_entry_add_signal (binding_set[0], GDK_slash, GDK_CONTROL_MASK, "select-all", 0);
1018 gtk_binding_entry_add_signal (binding_set[0], GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
1019 gtk_binding_entry_add_signal (binding_set[0], GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0);
1021 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1022 G_TYPE_BOOLEAN, TRUE,
1023 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1024 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1025 G_TYPE_BOOLEAN, TRUE,
1026 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1028 gtk_binding_entry_add_signal (binding_set[0], GDK_space, 0, "select-cursor-row", 1,
1029 G_TYPE_BOOLEAN, TRUE,
1031 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, 0, "select-cursor-row", 1,
1032 G_TYPE_BOOLEAN, TRUE,
1034 gtk_binding_entry_add_signal (binding_set[0], GDK_Return, 0, "select-cursor-row", 1,
1035 G_TYPE_BOOLEAN, TRUE,
1037 gtk_binding_entry_add_signal (binding_set[0], GDK_ISO_Enter, 0, "select-cursor-row", 1,
1038 G_TYPE_BOOLEAN, TRUE,
1040 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Enter, 0, "select-cursor-row", 1,
1041 G_TYPE_BOOLEAN, TRUE,
1044 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, 0, "select-cursor-parent", 0);
1045 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0);
1047 g_type_class_add_private (o_class, sizeof (PsppSheetViewPrivate));
1051 pspp_sheet_view_buildable_init (GtkBuildableIface *iface)
1053 iface->add_child = pspp_sheet_view_buildable_add_child;
1057 pspp_sheet_view_init (PsppSheetView *tree_view)
1059 tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, PSPP_TYPE_SHEET_VIEW, PsppSheetViewPrivate);
1061 gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
1062 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE);
1064 tree_view->priv->flags = PSPP_SHEET_VIEW_DRAW_KEYFOCUS
1065 | PSPP_SHEET_VIEW_HEADERS_VISIBLE;
1067 /* We need some padding */
1068 tree_view->priv->selected = range_tower_create ();
1069 tree_view->priv->dy = 0;
1070 tree_view->priv->cursor_offset = 0;
1071 tree_view->priv->n_columns = 0;
1072 tree_view->priv->header_height = 1;
1073 tree_view->priv->x_drag = 0;
1074 tree_view->priv->drag_pos = -1;
1075 tree_view->priv->header_has_focus = FALSE;
1076 tree_view->priv->pressed_button = -1;
1077 tree_view->priv->press_start_x = -1;
1078 tree_view->priv->press_start_y = -1;
1079 tree_view->priv->reorderable = FALSE;
1080 tree_view->priv->presize_handler_timer = 0;
1081 tree_view->priv->scroll_sync_timer = 0;
1082 tree_view->priv->fixed_height = -1;
1083 tree_view->priv->fixed_height_set = FALSE;
1084 pspp_sheet_view_set_adjustments (tree_view, NULL, NULL);
1085 tree_view->priv->selection = _pspp_sheet_selection_new_with_tree_view (tree_view);
1086 tree_view->priv->enable_search = TRUE;
1087 tree_view->priv->search_column = -1;
1088 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
1089 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
1090 tree_view->priv->search_custom_entry_set = FALSE;
1091 tree_view->priv->typeselect_flush_timeout = 0;
1092 tree_view->priv->init_hadjust_value = TRUE;
1093 tree_view->priv->width = 0;
1095 tree_view->priv->hover_selection = FALSE;
1097 tree_view->priv->rubber_banding_enable = FALSE;
1099 tree_view->priv->grid_lines = PSPP_SHEET_VIEW_GRID_LINES_NONE;
1101 tree_view->priv->tooltip_column = -1;
1103 tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT;
1105 tree_view->priv->post_validation_flag = FALSE;
1107 tree_view->priv->last_button_x = -1;
1108 tree_view->priv->last_button_y = -1;
1110 tree_view->priv->event_last_x = -10000;
1111 tree_view->priv->event_last_y = -10000;
1113 tree_view->priv->prelight_node = -1;
1114 tree_view->priv->rubber_band_start_node = -1;
1115 tree_view->priv->rubber_band_end_node = -1;
1117 tree_view->priv->anchor_column = NULL;
1119 tree_view->priv->button_style = NULL;
1121 tree_view->dispose_has_run = FALSE;
1123 pspp_sheet_view_do_set_vadjustment (tree_view, NULL);
1124 pspp_sheet_view_do_set_hadjustment (tree_view, NULL);
1133 pspp_sheet_view_set_property (GObject *object,
1135 const GValue *value,
1138 PsppSheetView *tree_view;
1140 tree_view = PSPP_SHEET_VIEW (object);
1145 pspp_sheet_view_set_model (tree_view, g_value_get_object (value));
1147 case PROP_HADJUSTMENT:
1148 pspp_sheet_view_do_set_hadjustment (tree_view, g_value_get_object (value));
1150 case PROP_VADJUSTMENT:
1151 pspp_sheet_view_do_set_vadjustment (tree_view, g_value_get_object (value));
1153 case PROP_HSCROLL_POLICY:
1154 tree_view->priv->hscroll_policy = g_value_get_enum (value);
1155 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1157 case PROP_VSCROLL_POLICY:
1158 tree_view->priv->vscroll_policy = g_value_get_enum (value);
1159 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1161 case PROP_HEADERS_VISIBLE:
1162 pspp_sheet_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1164 case PROP_HEADERS_CLICKABLE:
1165 pspp_sheet_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1167 case PROP_REORDERABLE:
1168 pspp_sheet_view_set_reorderable (tree_view, g_value_get_boolean (value));
1170 case PROP_RULES_HINT:
1171 pspp_sheet_view_set_rules_hint (tree_view, g_value_get_boolean (value));
1173 case PROP_ENABLE_SEARCH:
1174 pspp_sheet_view_set_enable_search (tree_view, g_value_get_boolean (value));
1176 case PROP_SEARCH_COLUMN:
1177 pspp_sheet_view_set_search_column (tree_view, g_value_get_int (value));
1179 case PROP_HOVER_SELECTION:
1180 tree_view->priv->hover_selection = g_value_get_boolean (value);
1182 case PROP_RUBBER_BANDING:
1183 tree_view->priv->rubber_banding_enable = g_value_get_boolean (value);
1185 case PROP_ENABLE_GRID_LINES:
1186 pspp_sheet_view_set_grid_lines (tree_view, g_value_get_enum (value));
1188 case PROP_TOOLTIP_COLUMN:
1189 pspp_sheet_view_set_tooltip_column (tree_view, g_value_get_int (value));
1191 case PROP_SPECIAL_CELLS:
1192 pspp_sheet_view_set_special_cells (tree_view, g_value_get_enum (value));
1194 case PROP_FIXED_HEIGHT:
1195 pspp_sheet_view_set_fixed_height (tree_view, g_value_get_int (value));
1197 case PROP_FIXED_HEIGHT_SET:
1198 if (g_value_get_boolean (value))
1200 if (!tree_view->priv->fixed_height_set
1201 && tree_view->priv->fixed_height >= 0)
1203 tree_view->priv->fixed_height_set = true;
1204 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1209 if (tree_view->priv->fixed_height_set)
1211 tree_view->priv->fixed_height_set = false;
1212 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1213 install_presize_handler (tree_view);
1218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1224 pspp_sheet_view_get_property (GObject *object,
1229 PsppSheetView *tree_view;
1231 tree_view = PSPP_SHEET_VIEW (object);
1236 g_value_set_object (value, tree_view->priv->model);
1238 case PROP_HADJUSTMENT:
1239 g_value_set_object (value, tree_view->priv->hadjustment);
1241 case PROP_VADJUSTMENT:
1242 g_value_set_object (value, tree_view->priv->vadjustment);
1244 case PROP_HSCROLL_POLICY:
1245 g_value_set_enum (value, tree_view->priv->hscroll_policy);
1247 case PROP_VSCROLL_POLICY:
1248 g_value_set_enum (value, tree_view->priv->vscroll_policy);
1250 case PROP_HEADERS_VISIBLE:
1251 g_value_set_boolean (value, pspp_sheet_view_get_headers_visible (tree_view));
1253 case PROP_HEADERS_CLICKABLE:
1254 g_value_set_boolean (value, pspp_sheet_view_get_headers_clickable (tree_view));
1256 case PROP_REORDERABLE:
1257 g_value_set_boolean (value, tree_view->priv->reorderable);
1259 case PROP_RULES_HINT:
1260 g_value_set_boolean (value, tree_view->priv->has_rules);
1262 case PROP_ENABLE_SEARCH:
1263 g_value_set_boolean (value, tree_view->priv->enable_search);
1265 case PROP_SEARCH_COLUMN:
1266 g_value_set_int (value, tree_view->priv->search_column);
1268 case PROP_HOVER_SELECTION:
1269 g_value_set_boolean (value, tree_view->priv->hover_selection);
1271 case PROP_RUBBER_BANDING:
1272 g_value_set_boolean (value, tree_view->priv->rubber_banding_enable);
1274 case PROP_ENABLE_GRID_LINES:
1275 g_value_set_enum (value, tree_view->priv->grid_lines);
1277 case PROP_TOOLTIP_COLUMN:
1278 g_value_set_int (value, tree_view->priv->tooltip_column);
1280 case PROP_SPECIAL_CELLS:
1281 g_value_set_enum (value, tree_view->priv->special_cells);
1283 case PROP_FIXED_HEIGHT:
1284 g_value_set_int (value, pspp_sheet_view_get_fixed_height (tree_view));
1286 case PROP_FIXED_HEIGHT_SET:
1287 g_value_set_boolean (value, tree_view->priv->fixed_height_set);
1290 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1296 pspp_sheet_view_dispose (GObject *object)
1298 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1300 if (tree_view->dispose_has_run)
1303 tree_view->dispose_has_run = TRUE;
1305 if (tree_view->priv->selection != NULL)
1307 _pspp_sheet_selection_set_tree_view (tree_view->priv->selection, NULL);
1308 g_object_unref (tree_view->priv->selection);
1309 tree_view->priv->selection = NULL;
1312 if (tree_view->priv->hadjustment)
1314 g_object_unref (tree_view->priv->hadjustment);
1315 tree_view->priv->hadjustment = NULL;
1317 if (tree_view->priv->vadjustment)
1319 g_object_unref (tree_view->priv->vadjustment);
1320 tree_view->priv->vadjustment = NULL;
1323 if (tree_view->priv->button_style)
1325 g_object_unref (tree_view->priv->button_style);
1326 tree_view->priv->button_style = NULL;
1330 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->dispose (object);
1336 pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
1337 GtkBuilder *builder,
1341 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), PSPP_SHEET_VIEW_COLUMN (child));
1345 pspp_sheet_view_finalize (GObject *object)
1347 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1349 pspp_sheet_view_stop_editing (tree_view, TRUE);
1351 if (tree_view->priv->selected != NULL)
1353 range_tower_destroy (tree_view->priv->selected);
1354 tree_view->priv->selected = NULL;
1358 tree_view->priv->prelight_node = -1;
1361 if (tree_view->priv->scroll_to_path != NULL)
1363 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
1364 tree_view->priv->scroll_to_path = NULL;
1367 if (tree_view->priv->drag_dest_row != NULL)
1369 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
1370 tree_view->priv->drag_dest_row = NULL;
1373 if (tree_view->priv->top_row != NULL)
1375 gtk_tree_row_reference_free (tree_view->priv->top_row);
1376 tree_view->priv->top_row = NULL;
1379 if (tree_view->priv->column_drop_func_data &&
1380 tree_view->priv->column_drop_func_data_destroy)
1382 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
1383 tree_view->priv->column_drop_func_data = NULL;
1386 if (tree_view->priv->destroy_count_destroy &&
1387 tree_view->priv->destroy_count_data)
1389 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
1390 tree_view->priv->destroy_count_data = NULL;
1393 gtk_tree_row_reference_free (tree_view->priv->cursor);
1394 tree_view->priv->cursor = NULL;
1396 gtk_tree_row_reference_free (tree_view->priv->anchor);
1397 tree_view->priv->anchor = NULL;
1399 /* destroy interactive search dialog */
1400 if (tree_view->priv->search_window)
1402 gtk_widget_destroy (tree_view->priv->search_window);
1403 tree_view->priv->search_window = NULL;
1404 tree_view->priv->search_entry = NULL;
1405 if (tree_view->priv->typeselect_flush_timeout)
1407 g_source_remove (tree_view->priv->typeselect_flush_timeout);
1408 tree_view->priv->typeselect_flush_timeout = 0;
1412 if (tree_view->priv->search_destroy && tree_view->priv->search_user_data)
1414 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
1415 tree_view->priv->search_user_data = NULL;
1418 if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data)
1420 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
1421 tree_view->priv->search_position_user_data = NULL;
1424 pspp_sheet_view_set_model (tree_view, NULL);
1427 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->finalize (object);
1432 /* GtkWidget Methods
1435 /* GtkWidget::map helper */
1437 pspp_sheet_view_map_buttons (PsppSheetView *tree_view)
1441 g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
1443 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
1445 PsppSheetViewColumn *column;
1447 for (list = tree_view->priv->columns; list; list = list->next)
1449 column = list->data;
1450 if (column->button != NULL &&
1451 gtk_widget_get_visible (column->button) &&
1452 !gtk_widget_get_mapped (column->button))
1453 gtk_widget_map (column->button);
1455 for (list = tree_view->priv->columns; list; list = list->next)
1457 column = list->data;
1458 if (column->visible == FALSE || column->window == NULL)
1460 if (column->resizable)
1462 gdk_window_raise (column->window);
1463 gdk_window_show (column->window);
1466 gdk_window_hide (column->window);
1468 gdk_window_show (tree_view->priv->header_window);
1473 pspp_sheet_view_map (GtkWidget *widget)
1475 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1478 gtk_widget_set_mapped (widget, TRUE);
1480 tmp_list = tree_view->priv->children;
1483 PsppSheetViewChild *child = tmp_list->data;
1484 tmp_list = tmp_list->next;
1486 if (gtk_widget_get_visible (child->widget))
1488 if (!gtk_widget_get_mapped (child->widget))
1489 gtk_widget_map (child->widget);
1492 gdk_window_show (tree_view->priv->bin_window);
1494 pspp_sheet_view_map_buttons (tree_view);
1496 gdk_window_show (gtk_widget_get_window (widget));
1500 pspp_sheet_view_realize (GtkWidget *widget)
1502 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1504 GdkWindowAttr attributes;
1505 gint attributes_mask;
1506 GtkAllocation allocation;
1507 GtkAllocation old_allocation;
1509 gtk_widget_set_realized (widget, TRUE);
1511 gtk_widget_get_allocation (widget, &allocation);
1512 gtk_widget_get_allocation (widget, &old_allocation);
1514 /* Make the main, clipping window */
1515 attributes.window_type = GDK_WINDOW_CHILD;
1516 attributes.x = allocation.x;
1517 attributes.y = allocation.y;
1518 attributes.width = allocation.width;
1519 attributes.height = allocation.height;
1520 attributes.wclass = GDK_INPUT_OUTPUT;
1521 attributes.visual = gtk_widget_get_visual (widget);
1522 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1524 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
1526 gtk_widget_set_window (widget,
1527 gdk_window_new (gtk_widget_get_parent_window (widget),
1528 &attributes, attributes_mask));
1529 gdk_window_set_user_data (gtk_widget_get_window (widget), widget);
1531 /* Make the window for the tree */
1533 attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
1534 attributes.width = MAX (tree_view->priv->width, old_allocation.width);
1535 attributes.height = old_allocation.height;
1536 attributes.event_mask = (GDK_EXPOSURE_MASK |
1538 GDK_POINTER_MOTION_MASK |
1539 GDK_ENTER_NOTIFY_MASK |
1540 GDK_LEAVE_NOTIFY_MASK |
1541 GDK_BUTTON_PRESS_MASK |
1542 GDK_BUTTON_RELEASE_MASK |
1543 gtk_widget_get_events (widget));
1545 tree_view->priv->bin_window = gdk_window_new (gtk_widget_get_window (widget),
1546 &attributes, attributes_mask);
1547 gdk_window_set_user_data (tree_view->priv->bin_window, widget);
1549 /* Make the column header window */
1552 attributes.width = MAX (tree_view->priv->width, old_allocation.width);
1553 attributes.height = tree_view->priv->header_height;
1554 attributes.event_mask = (GDK_EXPOSURE_MASK |
1556 GDK_BUTTON_PRESS_MASK |
1557 GDK_BUTTON_RELEASE_MASK |
1558 GDK_KEY_PRESS_MASK |
1559 GDK_KEY_RELEASE_MASK |
1560 gtk_widget_get_events (widget));
1562 tree_view->priv->header_window = gdk_window_new (gtk_widget_get_window (widget),
1563 &attributes, attributes_mask);
1564 gdk_window_set_user_data (tree_view->priv->header_window, widget);
1566 /* Add them all up. */
1567 gtk_widget_set_style (widget,
1568 gtk_style_attach (gtk_widget_get_style (widget), gtk_widget_get_window (widget)));
1569 gdk_window_set_background (tree_view->priv->bin_window, >k_widget_get_style (widget)->base[gtk_widget_get_state (widget)]);
1570 gtk_style_set_background (gtk_widget_get_style (widget), tree_view->priv->header_window, GTK_STATE_NORMAL);
1572 tmp_list = tree_view->priv->children;
1575 PsppSheetViewChild *child = tmp_list->data;
1576 tmp_list = tmp_list->next;
1578 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
1581 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
1582 _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list->data));
1584 /* Need to call those here, since they create GCs */
1585 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
1587 install_presize_handler (tree_view);
1591 pspp_sheet_view_unrealize (GtkWidget *widget)
1593 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1594 PsppSheetViewPrivate *priv = tree_view->priv;
1597 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget);
1599 if (priv->scroll_timeout != 0)
1601 g_source_remove (priv->scroll_timeout);
1602 priv->scroll_timeout = 0;
1605 if (priv->open_dest_timeout != 0)
1607 g_source_remove (priv->open_dest_timeout);
1608 priv->open_dest_timeout = 0;
1611 if (priv->presize_handler_timer != 0)
1613 g_source_remove (priv->presize_handler_timer);
1614 priv->presize_handler_timer = 0;
1617 if (priv->validate_rows_timer != 0)
1619 g_source_remove (priv->validate_rows_timer);
1620 priv->validate_rows_timer = 0;
1623 if (priv->scroll_sync_timer != 0)
1625 g_source_remove (priv->scroll_sync_timer);
1626 priv->scroll_sync_timer = 0;
1629 if (priv->typeselect_flush_timeout)
1631 g_source_remove (priv->typeselect_flush_timeout);
1632 priv->typeselect_flush_timeout = 0;
1635 for (list = priv->columns; list; list = list->next)
1636 _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list->data));
1638 gdk_window_set_user_data (priv->bin_window, NULL);
1639 gdk_window_destroy (priv->bin_window);
1640 priv->bin_window = NULL;
1642 gdk_window_set_user_data (priv->header_window, NULL);
1643 gdk_window_destroy (priv->header_window);
1644 priv->header_window = NULL;
1646 if (priv->drag_window)
1648 gdk_window_set_user_data (priv->drag_window, NULL);
1649 gdk_window_destroy (priv->drag_window);
1650 priv->drag_window = NULL;
1653 if (priv->drag_highlight_window)
1655 gdk_window_set_user_data (priv->drag_highlight_window, NULL);
1656 gdk_window_destroy (priv->drag_highlight_window);
1657 priv->drag_highlight_window = NULL;
1660 if (tree_view->priv->columns != NULL)
1662 list = tree_view->priv->columns;
1665 PsppSheetViewColumn *column;
1666 column = PSPP_SHEET_VIEW_COLUMN (list->data);
1668 pspp_sheet_view_remove_column (tree_view, column);
1670 tree_view->priv->columns = NULL;
1674 /* GtkWidget::size_request helper */
1676 pspp_sheet_view_size_request_columns (PsppSheetView *tree_view)
1680 tree_view->priv->header_height = 0;
1682 if (tree_view->priv->model)
1684 for (list = tree_view->priv->columns; list; list = list->next)
1686 GtkRequisition requisition;
1687 PsppSheetViewColumn *column = list->data;
1689 pspp_sheet_view_column_size_request (column, &requisition);
1690 column->button_request = requisition.width;
1691 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
1697 /* Called only by ::size_request */
1699 pspp_sheet_view_update_size (PsppSheetView *tree_view)
1702 PsppSheetViewColumn *column;
1705 if (tree_view->priv->model == NULL)
1707 tree_view->priv->width = 0;
1708 tree_view->priv->prev_width = 0;
1709 tree_view->priv->height = 0;
1713 tree_view->priv->prev_width = tree_view->priv->width;
1714 tree_view->priv->width = 0;
1716 /* keep this in sync with size_allocate below */
1717 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
1719 gint real_requested_width = 0;
1720 column = list->data;
1721 if (!column->visible)
1724 if (column->use_resized_width)
1726 real_requested_width = column->resized_width;
1730 real_requested_width = column->fixed_width;
1733 if (column->min_width != -1)
1734 real_requested_width = MAX (real_requested_width, column->min_width);
1735 if (column->max_width != -1)
1736 real_requested_width = MIN (real_requested_width, column->max_width);
1738 tree_view->priv->width += real_requested_width;
1741 tree_view->priv->height = tree_view->priv->fixed_height * tree_view->priv->row_count;
1745 pspp_sheet_view_size_request (GtkWidget *widget,
1746 GtkRequisition *requisition)
1748 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1751 /* we validate some rows initially just to make sure we have some size.
1752 * In practice, with a lot of static lists, this should get a good width.
1754 initialize_fixed_height_mode (tree_view);
1755 pspp_sheet_view_size_request_columns (tree_view);
1756 pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget));
1758 requisition->width = tree_view->priv->width;
1759 requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
1761 tmp_list = tree_view->priv->children;
1765 PsppSheetViewChild *child = tmp_list->data;
1766 GtkRequisition child_requisition;
1768 tmp_list = tmp_list->next;
1770 if (gtk_widget_get_visible (child->widget))
1771 gtk_widget_size_request (child->widget, &child_requisition);
1776 invalidate_column (PsppSheetView *tree_view,
1777 PsppSheetViewColumn *column)
1779 gint column_offset = 0;
1781 GtkWidget *widget = GTK_WIDGET (tree_view);
1784 if (!gtk_widget_get_realized (widget))
1787 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1788 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
1790 list = (rtl ? list->prev : list->next))
1792 PsppSheetViewColumn *tmpcolumn = list->data;
1793 if (tmpcolumn == column)
1795 GdkRectangle invalid_rect;
1796 GtkAllocation allocation;
1798 gtk_widget_get_allocation (widget, &allocation);
1799 invalid_rect.x = column_offset;
1801 invalid_rect.width = column->width;
1802 invalid_rect.height = allocation.height;
1804 gdk_window_invalidate_rect (gtk_widget_get_window (widget), &invalid_rect, TRUE);
1808 column_offset += tmpcolumn->width;
1813 invalidate_last_column (PsppSheetView *tree_view)
1818 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1820 for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
1822 last_column = (rtl ? last_column->next : last_column->prev))
1824 if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible)
1826 invalidate_column (tree_view, last_column->data);
1833 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_view,
1834 PsppSheetViewColumn *column)
1836 gint real_requested_width;
1838 if (column->use_resized_width)
1840 real_requested_width = column->resized_width;
1844 real_requested_width = column->fixed_width;
1847 if (column->min_width != -1)
1848 real_requested_width = MAX (real_requested_width, column->min_width);
1849 if (column->max_width != -1)
1850 real_requested_width = MIN (real_requested_width, column->max_width);
1852 return real_requested_width;
1856 span_intersects (int a0, int a_width,
1857 int b0, int b_width)
1859 int a1 = a0 + a_width;
1860 int b1 = b0 + b_width;
1861 return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1);
1864 /* GtkWidget::size_allocate helper */
1866 pspp_sheet_view_size_allocate_columns (GtkWidget *widget,
1867 gboolean *width_changed)
1869 PsppSheetView *tree_view;
1870 GList *list, *first_column, *last_column;
1871 PsppSheetViewColumn *column;
1872 GtkAllocation col_allocation;
1873 GtkAllocation allocation;
1875 gint extra, extra_per_column;
1876 gint full_requested_width = 0;
1877 gint number_of_expand_columns = 0;
1878 gboolean column_changed = FALSE;
1881 tree_view = PSPP_SHEET_VIEW (widget);
1883 for (last_column = g_list_last (tree_view->priv->columns);
1884 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
1885 last_column = last_column->prev)
1888 if (last_column == NULL)
1891 for (first_column = g_list_first (tree_view->priv->columns);
1892 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
1893 first_column = first_column->next)
1896 col_allocation.y = 0;
1897 col_allocation.height = tree_view->priv->header_height;
1899 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1901 /* find out how many extra space and expandable columns we have */
1902 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
1904 column = (PsppSheetViewColumn *)list->data;
1906 if (!column->visible)
1909 full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1912 number_of_expand_columns++;
1915 gtk_widget_get_allocation (widget, &allocation);
1916 extra = MAX (allocation.width - full_requested_width, 0);
1917 if (number_of_expand_columns > 0)
1918 extra_per_column = extra/number_of_expand_columns;
1920 extra_per_column = 0;
1922 for (list = (rtl ? last_column : first_column);
1923 list != (rtl ? first_column->prev : last_column->next);
1924 list = (rtl ? list->prev : list->next))
1926 gint real_requested_width = 0;
1929 column = list->data;
1930 old_width = column->width;
1932 if (!column->visible)
1935 /* We need to handle the dragged button specially.
1937 if (column == tree_view->priv->drag_column)
1939 GtkAllocation drag_allocation;
1940 drag_allocation.width = gdk_window_get_width (tree_view->priv->drag_window);
1941 drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window);
1942 drag_allocation.x = 0;
1943 drag_allocation.y = 0;
1944 pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column,
1946 width += drag_allocation.width;
1950 real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1952 col_allocation.x = width;
1953 column->width = real_requested_width;
1957 if (number_of_expand_columns == 1)
1959 /* We add the remander to the last column as
1961 column->width += extra;
1965 column->width += extra_per_column;
1966 extra -= extra_per_column;
1967 number_of_expand_columns --;
1971 if (column->width != old_width)
1972 g_object_notify (G_OBJECT (column), "width");
1974 col_allocation.width = column->width;
1975 width += column->width;
1977 if (column->width > old_width)
1978 column_changed = TRUE;
1980 pspp_sheet_view_column_size_allocate (column, &col_allocation);
1982 if (span_intersects (col_allocation.x, col_allocation.width,
1983 gtk_adjustment_get_value (tree_view->priv->hadjustment),
1985 && gtk_widget_get_realized (widget))
1986 pspp_sheet_view_column_set_need_button (column, TRUE);
1989 gdk_window_move_resize (column->window,
1990 col_allocation.x + (rtl ? 0 : col_allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
1992 TREE_VIEW_DRAG_WIDTH, col_allocation.height);
1995 /* We change the width here. The user might have been resizing columns,
1996 * so the total width of the tree view changes.
1998 tree_view->priv->width = width;
2000 *width_changed = TRUE;
2003 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2007 pspp_sheet_view_size_allocate (GtkWidget *widget,
2008 GtkAllocation *allocation)
2010 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2012 gboolean width_changed = FALSE;
2013 GtkAllocation old_allocation;
2014 gtk_widget_get_allocation (widget, &old_allocation);
2016 if (allocation->width != old_allocation.width)
2017 width_changed = TRUE;
2020 gtk_widget_set_allocation (widget, allocation);
2022 tmp_list = tree_view->priv->children;
2026 GtkAllocation allocation;
2028 PsppSheetViewChild *child = tmp_list->data;
2029 tmp_list = tmp_list->next;
2031 /* totally ignore our child's requisition */
2032 allocation.x = child->x;
2033 allocation.y = child->y;
2034 allocation.width = child->width;
2035 allocation.height = child->height;
2036 gtk_widget_size_allocate (child->widget, &allocation);
2039 /* We size-allocate the columns first because the width of the
2040 * tree view (used in updating the adjustments below) might change.
2042 pspp_sheet_view_size_allocate_columns (widget, &width_changed);
2044 gtk_adjustment_set_page_size (tree_view->priv->hadjustment, allocation->width);
2045 gtk_adjustment_set_page_increment (tree_view->priv->hadjustment, allocation->width * 0.9);
2046 gtk_adjustment_set_step_increment (tree_view->priv->hadjustment, allocation->width * 0.1);
2047 gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0);
2048 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->hadjustment), tree_view->priv->width));
2050 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2052 if (allocation->width < tree_view->priv->width)
2054 if (tree_view->priv->init_hadjust_value)
2056 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2057 tree_view->priv->init_hadjust_value = FALSE;
2059 else if (allocation->width != old_allocation.width)
2061 gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) - allocation->width + old_allocation.width, 0, tree_view->priv->width - allocation->width));
2064 gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (tree_view->priv->width - (tree_view->priv->prev_width - gtk_adjustment_get_value (tree_view->priv->hadjustment)), 0, tree_view->priv->width - allocation->width));
2068 gtk_adjustment_set_value (tree_view->priv->hadjustment, 0);
2069 tree_view->priv->init_hadjust_value = TRUE;
2073 if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + allocation->width > tree_view->priv->width)
2074 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2076 gtk_adjustment_changed (tree_view->priv->hadjustment);
2078 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2079 gtk_adjustment_set_step_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.1);
2080 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.9);
2081 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
2082 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->vadjustment), tree_view->priv->height));
2084 gtk_adjustment_changed (tree_view->priv->vadjustment);
2086 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2087 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
2088 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2089 else if (gtk_adjustment_get_value (tree_view->priv->vadjustment) + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
2090 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2091 tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
2092 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2093 pspp_sheet_view_top_row_to_dy (tree_view);
2095 pspp_sheet_view_dy_to_top_row (tree_view);
2097 if (gtk_widget_get_realized (widget))
2099 gdk_window_move_resize (gtk_widget_get_window (widget),
2100 allocation->x, allocation->y,
2101 allocation->width, allocation->height);
2102 gdk_window_move_resize (tree_view->priv->header_window,
2103 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2105 MAX (tree_view->priv->width, allocation->width),
2106 tree_view->priv->header_height);
2107 gdk_window_move_resize (tree_view->priv->bin_window,
2108 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2109 TREE_VIEW_HEADER_HEIGHT (tree_view),
2110 MAX (tree_view->priv->width, allocation->width),
2111 allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2114 if (tree_view->priv->row_count == 0)
2115 invalidate_empty_focus (tree_view);
2117 if (gtk_widget_get_realized (widget))
2119 gboolean has_expand_column = FALSE;
2120 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2122 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2124 has_expand_column = TRUE;
2129 /* This little hack only works if we have an LTR locale, and no column has the */
2132 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2133 ! has_expand_column)
2134 invalidate_last_column (tree_view);
2136 gtk_widget_queue_draw (widget);
2141 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2143 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2145 GtkWidget *widget = GTK_WIDGET (tree_view);
2147 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2148 gtk_widget_grab_focus (widget);
2149 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2153 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2156 return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2160 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2163 range_tower_set1 (tree_view->priv->selected, node, 1);
2167 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2170 range_tower_set0 (tree_view->priv->selected, node, 1);
2174 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2177 return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2181 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2184 return node > 0 ? node - 1 : -1;
2188 all_columns_selected (PsppSheetView *tree_view)
2192 for (list = tree_view->priv->columns; list; list = list->next)
2194 PsppSheetViewColumn *column = list->data;
2195 if (column->selectable && !column->selected)
2203 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2205 PsppSheetViewColumn *column,
2206 GdkEventButton *event)
2208 PsppSheetSelection *selection;
2209 PsppSheetSelectionMode mode;
2211 gboolean update_anchor;
2215 g_return_val_if_fail (tree_view != NULL, FALSE);
2216 g_return_val_if_fail (column != NULL, FALSE);
2218 selection = tree_view->priv->selection;
2219 mode = pspp_sheet_selection_get_mode (selection);
2220 if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2223 if (!column->row_head)
2228 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2229 if (event->type != GDK_BUTTON_PRESS
2230 || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2236 path = gtk_tree_path_new_from_indices (node, -1);
2239 pspp_sheet_selection_unselect_all (selection);
2240 pspp_sheet_selection_select_path (selection, path);
2241 pspp_sheet_selection_select_all_columns (selection);
2242 update_anchor = TRUE;
2245 else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2247 if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2248 || !all_columns_selected (tree_view))
2250 pspp_sheet_selection_unselect_all (selection);
2251 pspp_sheet_selection_select_path (selection, path);
2252 pspp_sheet_selection_select_all_columns (selection);
2253 update_anchor = TRUE;
2257 update_anchor = handled = FALSE;
2259 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2260 && modifiers == GDK_CONTROL_MASK)
2262 if (!all_columns_selected (tree_view))
2264 pspp_sheet_selection_unselect_all (selection);
2265 pspp_sheet_selection_select_all_columns (selection);
2268 if (pspp_sheet_selection_path_is_selected (selection, path))
2269 pspp_sheet_selection_unselect_path (selection, path);
2271 pspp_sheet_selection_select_path (selection, path);
2272 update_anchor = TRUE;
2275 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2276 && modifiers == GDK_SHIFT_MASK)
2278 GtkTreeRowReference *anchor = tree_view->priv->anchor;
2279 GtkTreePath *anchor_path;
2281 if (all_columns_selected (tree_view)
2282 && gtk_tree_row_reference_valid (anchor))
2284 update_anchor = FALSE;
2285 anchor_path = gtk_tree_row_reference_get_path (anchor);
2289 update_anchor = TRUE;
2290 anchor_path = gtk_tree_path_copy (path);
2293 pspp_sheet_selection_unselect_all (selection);
2294 pspp_sheet_selection_select_range (selection, anchor_path, path);
2295 pspp_sheet_selection_select_all_columns (selection);
2297 gtk_tree_path_free (anchor_path);
2302 update_anchor = handled = FALSE;
2306 if (tree_view->priv->anchor)
2307 gtk_tree_row_reference_free (tree_view->priv->anchor);
2308 tree_view->priv->anchor =
2309 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2310 tree_view->priv->model,
2314 gtk_tree_path_free (path);
2319 find_click (PsppSheetView *tree_view,
2322 PsppSheetViewColumn **column,
2323 GdkRectangle *background_area,
2324 GdkRectangle *cell_area)
2331 /* find the node that was clicked */
2332 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2335 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2340 background_area->y = y_offset + y;
2341 background_area->height = ROW_HEIGHT (tree_view);
2342 background_area->x = 0;
2344 /* Let the column have a chance at selecting it. */
2345 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2346 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2347 list; list = (rtl ? list->prev : list->next))
2349 PsppSheetViewColumn *candidate = list->data;
2351 if (!candidate->visible)
2354 background_area->width = candidate->width;
2355 if ((background_area->x > x) ||
2356 (background_area->x + background_area->width <= x))
2358 background_area->x += background_area->width;
2362 /* we found the focus column */
2364 pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2366 *column = candidate;
2374 pspp_sheet_view_button_press (GtkWidget *widget,
2375 GdkEventButton *event)
2377 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2379 PsppSheetViewColumn *column = NULL;
2381 GdkRectangle background_area;
2382 GdkRectangle cell_area;
2385 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2386 pspp_sheet_view_stop_editing (tree_view, FALSE);
2389 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2390 * we're done handling the button press.
2393 if (event->window == tree_view->priv->bin_window)
2398 gint pre_val, aft_val;
2399 PsppSheetViewColumn *column = NULL;
2400 GtkCellRenderer *focus_cell = NULL;
2401 gboolean row_double_click = FALSE;
2404 if (tree_view->priv->row_count == 0)
2406 grab_focus_and_unset_draw_keyfocus (tree_view);
2410 if (!find_click (tree_view, event->x, event->y, &node, &column,
2411 &background_area, &cell_area))
2413 grab_focus_and_unset_draw_keyfocus (tree_view);
2417 tree_view->priv->focus_column = column;
2419 if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2423 pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2425 path = _pspp_sheet_view_find_path (tree_view, node);
2427 /* we only handle selection modifications on the first button press
2429 if (event->type == GDK_BUTTON_PRESS)
2431 PsppSheetSelectionMode mode = 0;
2433 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2434 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
2435 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2436 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
2438 focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2440 pspp_sheet_view_column_focus_cell (column, focus_cell);
2442 if (event->state & GDK_CONTROL_MASK)
2444 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, mode);
2445 pspp_sheet_view_real_toggle_cursor_row (tree_view);
2447 else if (event->state & GDK_SHIFT_MASK)
2449 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
2450 pspp_sheet_view_real_select_cursor_row (tree_view, FALSE, mode);
2454 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
2457 if (tree_view->priv->anchor_column == NULL ||
2458 !(event->state & GDK_SHIFT_MASK))
2459 tree_view->priv->anchor_column = column;
2460 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2461 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2462 tree_view->priv->anchor_column,
2466 /* the treeview may have been scrolled because of _set_cursor,
2470 aft_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2471 dval = pre_val - aft_val;
2473 cell_area.y += dval;
2474 background_area.y += dval;
2476 /* Save press to possibly begin a drag
2478 if (!tree_view->priv->in_grab &&
2479 tree_view->priv->pressed_button < 0)
2481 tree_view->priv->pressed_button = event->button;
2482 tree_view->priv->press_start_x = event->x;
2483 tree_view->priv->press_start_y = event->y;
2484 tree_view->priv->press_start_node = node;
2486 if (tree_view->priv->rubber_banding_enable
2487 && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2488 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2490 tree_view->priv->press_start_y += tree_view->priv->dy;
2491 tree_view->priv->rubber_band_x = event->x;
2492 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2493 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2495 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2496 tree_view->priv->rubber_band_ctrl = TRUE;
2497 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2498 tree_view->priv->rubber_band_shift = TRUE;
2503 /* Test if a double click happened on the same row. */
2504 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2506 int double_click_time, double_click_distance;
2508 g_object_get (gtk_settings_get_for_screen (
2509 gtk_widget_get_screen (widget)),
2510 "gtk-double-click-time", &double_click_time,
2511 "gtk-double-click-distance", &double_click_distance,
2514 /* Same conditions as _gdk_event_button_generate */
2515 if (tree_view->priv->last_button_x != -1 &&
2516 (event->time < tree_view->priv->last_button_time + double_click_time) &&
2517 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2518 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2520 /* We do no longer compare paths of this row and the
2521 * row clicked previously. We use the double click
2522 * distance to decide whether this is a valid click,
2523 * allowing the mouse to slightly move over another row.
2525 row_double_click = TRUE;
2527 tree_view->priv->last_button_time = 0;
2528 tree_view->priv->last_button_x = -1;
2529 tree_view->priv->last_button_y = -1;
2533 tree_view->priv->last_button_time = event->time;
2534 tree_view->priv->last_button_x = event->x;
2535 tree_view->priv->last_button_y = event->y;
2539 if (row_double_click)
2541 gtk_grab_remove (widget);
2542 pspp_sheet_view_row_activated (tree_view, path, column);
2544 if (tree_view->priv->pressed_button == event->button)
2545 tree_view->priv->pressed_button = -1;
2548 gtk_tree_path_free (path);
2550 /* If we activated the row through a double click we don't want to grab
2551 * focus back, as moving focus to another widget is pretty common.
2553 if (!row_double_click)
2554 grab_focus_and_unset_draw_keyfocus (tree_view);
2559 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2561 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2563 column = list->data;
2564 if (event->window == column->window &&
2565 column->resizable &&
2570 if (gdk_pointer_grab (column->window, FALSE,
2571 GDK_POINTER_MOTION_HINT_MASK |
2572 GDK_BUTTON1_MOTION_MASK |
2573 GDK_BUTTON_RELEASE_MASK,
2574 NULL, NULL, event->time))
2577 gtk_grab_add (widget);
2578 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2579 column->resized_width = column->width;
2581 /* block attached dnd signal handler */
2582 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2584 g_signal_handlers_block_matched (widget,
2585 G_SIGNAL_MATCH_DATA,
2589 tree_view->priv->drag_pos = i;
2590 tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2592 if (!gtk_widget_has_focus (widget))
2593 gtk_widget_grab_focus (widget);
2601 /* GtkWidget::button_release_event helper */
2603 pspp_sheet_view_button_release_drag_column (GtkWidget *widget,
2604 GdkEventButton *event)
2606 PsppSheetView *tree_view;
2610 tree_view = PSPP_SHEET_VIEW (widget);
2612 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2613 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2614 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2616 /* Move the button back */
2617 g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2619 g_object_ref (tree_view->priv->drag_column->button);
2620 gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2621 gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2622 gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2623 g_object_unref (tree_view->priv->drag_column->button);
2624 gtk_widget_queue_resize (widget);
2625 if (tree_view->priv->drag_column->resizable)
2627 gdk_window_raise (tree_view->priv->drag_column->window);
2628 gdk_window_show (tree_view->priv->drag_column->window);
2631 gdk_window_hide (tree_view->priv->drag_column->window);
2633 gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2637 if (tree_view->priv->cur_reorder &&
2638 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2639 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2640 tree_view->priv->cur_reorder->right_column);
2644 if (tree_view->priv->cur_reorder &&
2645 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2646 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2647 tree_view->priv->cur_reorder->left_column);
2649 tree_view->priv->drag_column = NULL;
2650 gdk_window_hide (tree_view->priv->drag_window);
2652 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2653 g_slice_free (PsppSheetViewColumnReorder, l->data);
2654 g_list_free (tree_view->priv->column_drag_info);
2655 tree_view->priv->column_drag_info = NULL;
2656 tree_view->priv->cur_reorder = NULL;
2658 if (tree_view->priv->drag_highlight_window)
2659 gdk_window_hide (tree_view->priv->drag_highlight_window);
2661 /* Reset our flags */
2662 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2663 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2668 /* GtkWidget::button_release_event helper */
2670 pspp_sheet_view_button_release_column_resize (GtkWidget *widget,
2671 GdkEventButton *event)
2673 PsppSheetView *tree_view;
2676 tree_view = PSPP_SHEET_VIEW (widget);
2678 tree_view->priv->drag_pos = -1;
2680 /* unblock attached dnd signal handler */
2681 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2683 g_signal_handlers_unblock_matched (widget,
2684 G_SIGNAL_MATCH_DATA,
2688 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2689 gtk_grab_remove (widget);
2690 gdk_display_pointer_ungrab (gdk_window_get_display (event->window),
2696 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2697 GdkEventButton *event)
2699 GtkCellEditable *cell_editable;
2704 PsppSheetViewColumn *column;
2705 GdkRectangle background_area;
2706 GdkRectangle cell_area;
2712 if (event->window != tree_view->priv->bin_window)
2715 /* Ignore a released button, if that button wasn't depressed */
2716 if (tree_view->priv->pressed_button != event->button)
2719 if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2723 /* decide if we edit */
2724 path = _pspp_sheet_view_find_path (tree_view, node);
2725 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2726 if (event->button != 1 || modifiers)
2729 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2730 pspp_sheet_view_column_cell_set_cell_data (column,
2731 tree_view->priv->model,
2734 if (!pspp_sheet_view_column_get_quick_edit (column)
2735 && _pspp_sheet_view_column_has_editable_cell (column))
2738 flags = 0; /* FIXME: get the right flags */
2739 path_string = gtk_tree_path_to_string (path);
2741 if (!_pspp_sheet_view_column_cell_event (column,
2749 if (cell_editable == NULL)
2752 pspp_sheet_view_real_set_cursor (tree_view, path,
2753 TRUE, TRUE, 0); /* XXX mode? */
2754 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2757 _pspp_sheet_view_column_get_neighbor_sizes (
2758 column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2761 area.width -= right + left;
2763 pspp_sheet_view_real_start_editing (tree_view,
2770 g_free (path_string);
2771 gtk_tree_path_free (path);
2776 pspp_sheet_view_button_release (GtkWidget *widget,
2777 GdkEventButton *event)
2779 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2781 pspp_sheet_view_stop_editing (tree_view, FALSE);
2782 if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2783 && pspp_sheet_view_button_release_edit (tree_view, event))
2785 if (tree_view->priv->pressed_button == event->button)
2786 tree_view->priv->pressed_button = -1;
2788 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2792 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2793 return pspp_sheet_view_button_release_drag_column (widget, event);
2795 if (tree_view->priv->rubber_band_status)
2796 pspp_sheet_view_stop_rubber_band (tree_view);
2798 if (tree_view->priv->pressed_button == event->button)
2799 tree_view->priv->pressed_button = -1;
2801 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2802 return pspp_sheet_view_button_release_column_resize (widget, event);
2808 pspp_sheet_view_grab_broken (GtkWidget *widget,
2809 GdkEventGrabBroken *event)
2811 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2813 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2814 pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2816 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2817 pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2822 /* GtkWidget::motion_event function set.
2826 do_prelight (PsppSheetView *tree_view,
2828 /* these are in bin_window coords */
2832 int prev_node = tree_view->priv->prelight_node;
2834 if (prev_node != node)
2836 tree_view->priv->prelight_node = node;
2839 _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2842 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2848 prelight_or_select (PsppSheetView *tree_view,
2850 /* these are in bin_window coords */
2854 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2856 if (tree_view->priv->hover_selection &&
2857 (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2858 !(tree_view->priv->edited_column &&
2859 tree_view->priv->edited_column->editable_widget))
2863 if (!pspp_sheet_view_node_is_selected (tree_view, node))
2867 path = _pspp_sheet_view_find_path (tree_view, node);
2868 pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2869 if (pspp_sheet_view_node_is_selected (tree_view, node))
2871 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2872 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE, 0); /* XXX mode? */
2874 gtk_tree_path_free (path);
2878 else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2879 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2882 do_prelight (tree_view, node, x, y);
2886 ensure_unprelighted (PsppSheetView *tree_view)
2888 do_prelight (tree_view,
2890 -1000, -1000); /* coords not possibly over an arrow */
2892 g_assert (tree_view->priv->prelight_node < 0);
2896 update_prelight (PsppSheetView *tree_view,
2903 if (tree_view->priv->row_count == 0)
2908 ensure_unprelighted (tree_view);
2912 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2916 pspp_sheet_view_find_offset (tree_view, new_y, &node);
2919 prelight_or_select (tree_view, node, x, y);
2925 /* Our motion arrow is either a box (in the case of the original spot)
2926 * or an arrow. It is expander_size wide.
2949 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2952 PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2953 GtkWidget *widget = GTK_WIDGET (tree_view);
2954 GdkBitmap *mask = NULL;
2959 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2960 GdkWindowAttr attributes;
2961 guint attributes_mask;
2964 reorder->left_column == tree_view->priv->drag_column ||
2965 reorder->right_column == tree_view->priv->drag_column)
2966 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2967 else if (reorder->left_column || reorder->right_column)
2969 GdkRectangle visible_rect;
2970 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2971 if (reorder->left_column)
2972 x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2974 x = reorder->right_column->allocation.x;
2976 if (x < visible_rect.x)
2977 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2978 else if (x > visible_rect.x + visible_rect.width)
2979 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2981 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2984 /* We want to draw the rectangle over the initial location. */
2985 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2990 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2992 if (tree_view->priv->drag_highlight_window)
2994 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2996 gdk_window_destroy (tree_view->priv->drag_highlight_window);
2999 attributes.window_type = GDK_WINDOW_CHILD;
3000 attributes.wclass = GDK_INPUT_OUTPUT;
3001 attributes.x = tree_view->priv->drag_column_x;
3003 width = attributes.width = tree_view->priv->drag_column->allocation.width;
3004 height = attributes.height = tree_view->priv->drag_column->allocation.height;
3005 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3006 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3007 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3008 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3009 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
3010 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3012 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3013 gc = gdk_gc_new (mask);
3015 gdk_gc_set_foreground (gc, &col);
3016 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3018 gdk_gc_set_foreground(gc, &col);
3019 gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
3020 g_object_unref (gc);
3022 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3024 if (mask) g_object_unref (mask);
3025 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3028 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
3034 width = tree_view->priv->expander_size;
3036 /* Get x, y, width, height of arrow */
3037 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
3038 if (reorder->left_column)
3040 x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
3041 height = reorder->left_column->allocation.height;
3045 x += reorder->right_column->allocation.x - width/2;
3046 height = reorder->right_column->allocation.height;
3048 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3049 height += tree_view->priv->expander_size;
3051 /* Create the new window */
3052 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3054 if (tree_view->priv->drag_highlight_window)
3056 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3058 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3061 attributes.window_type = GDK_WINDOW_TEMP;
3062 attributes.wclass = GDK_INPUT_OUTPUT;
3063 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3064 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3065 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3066 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3069 attributes.width = width;
3070 attributes.height = height;
3071 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3072 &attributes, attributes_mask);
3073 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3075 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3076 gc = gdk_gc_new (mask);
3078 gdk_gc_set_foreground (gc, &col);
3079 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3081 /* Draw the 2 arrows as per above */
3083 gdk_gc_set_foreground (gc, &col);
3084 for (i = 0; i < width; i ++)
3086 if (i == (width/2 - 1))
3088 gdk_draw_line (mask, gc, i, j, i, height - j);
3089 if (i < (width/2 - 1))
3094 g_object_unref (gc);
3095 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3097 if (mask) g_object_unref (mask);
3100 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3101 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3103 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3104 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3110 width = tree_view->priv->expander_size;
3112 /* Get x, y, width, height of arrow */
3113 width = width/2; /* remember, the arrow only takes half the available width */
3114 gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
3115 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3116 x += widget->allocation.width - width;
3118 if (reorder->left_column)
3119 height = reorder->left_column->allocation.height;
3121 height = reorder->right_column->allocation.height;
3123 y -= tree_view->priv->expander_size;
3124 height += 2*tree_view->priv->expander_size;
3126 /* Create the new window */
3127 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3128 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3130 if (tree_view->priv->drag_highlight_window)
3132 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3134 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3137 attributes.window_type = GDK_WINDOW_TEMP;
3138 attributes.wclass = GDK_INPUT_OUTPUT;
3139 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3140 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3141 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3142 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3145 attributes.width = width;
3146 attributes.height = height;
3147 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3148 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3150 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3151 gc = gdk_gc_new (mask);
3153 gdk_gc_set_foreground (gc, &col);
3154 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3156 /* Draw the 2 arrows as per above */
3158 gdk_gc_set_foreground (gc, &col);
3159 j = tree_view->priv->expander_size;
3160 for (i = 0; i < width; i ++)
3163 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3167 gdk_draw_line (mask, gc, k, j, k, height - j);
3168 gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3169 gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3172 g_object_unref (gc);
3173 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3175 if (mask) g_object_unref (mask);
3178 tree_view->priv->drag_column_window_state = arrow_type;
3179 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3183 g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3184 gdk_window_hide (tree_view->priv->drag_highlight_window);
3188 gdk_window_show (tree_view->priv->drag_highlight_window);
3189 gdk_window_raise (tree_view->priv->drag_highlight_window);
3194 pspp_sheet_view_motion_resize_column (GtkWidget *widget,
3195 GdkEventMotion *event)
3199 PsppSheetViewColumn *column;
3200 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3202 column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3204 if (event->is_hint || event->window != gtk_widget_get_window (widget))
3205 gtk_widget_get_pointer (widget, &x, NULL);
3209 if (tree_view->priv->hadjustment)
3210 x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
3212 new_width = pspp_sheet_view_new_column_width (tree_view,
3213 tree_view->priv->drag_pos, &x);
3214 if (x != tree_view->priv->x_drag &&
3215 (new_width != column->fixed_width))
3217 column->use_resized_width = TRUE;
3218 column->resized_width = new_width;
3221 column->resized_width -= tree_view->priv->last_extra_space_per_column;
3223 gtk_widget_queue_resize (widget);
3231 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3233 PsppSheetViewColumnReorder *reorder = NULL;
3237 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3238 for (list = tree_view->priv->column_drag_info; list; list = list->next)
3240 reorder = (PsppSheetViewColumnReorder *) list->data;
3241 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3246 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3249 tree_view->priv->cur_reorder = reorder;
3250 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3254 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3256 GdkRectangle visible_rect;
3261 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3262 y += tree_view->priv->dy;
3264 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3266 /* see if we are near the edge. */
3267 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3270 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3275 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->vadjustment) + offset, 0.0,
3276 gtk_adjustment_get_upper (tree_view->priv->vadjustment) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
3277 gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3281 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3283 GdkRectangle visible_rect;
3288 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3290 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3292 /* See if we are near the edge. */
3293 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3296 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3302 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) + offset,
3303 0.0, gtk_adjustment_get_upper (tree_view->priv->hadjustment) - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
3304 gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3311 pspp_sheet_view_motion_drag_column (GtkWidget *widget,
3312 GdkEventMotion *event)
3314 PsppSheetView *tree_view = (PsppSheetView *) widget;
3315 PsppSheetViewColumn *column = tree_view->priv->drag_column;
3317 GtkAllocation allocation;
3320 if ((column == NULL) ||
3321 (event->window != tree_view->priv->drag_window))
3324 /* Handle moving the header */
3325 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3326 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
3327 x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3328 MAX (tree_view->priv->width, allocation.width) - column->allocation.width);
3329 gdk_window_move (tree_view->priv->drag_window, x, y);
3331 /* autoscroll, if needed */
3332 pspp_sheet_view_horizontal_autoscroll (tree_view);
3333 /* Update the current reorder position and arrow; */
3334 pspp_sheet_view_update_current_reorder (tree_view);
3340 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3342 remove_scroll_timeout (tree_view);
3343 gtk_grab_remove (GTK_WIDGET (tree_view));
3345 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3347 GtkTreePath *tmp_path;
3349 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3351 /* The anchor path should be set to the start path */
3352 tmp_path = _pspp_sheet_view_find_path (tree_view,
3353 tree_view->priv->rubber_band_start_node);
3355 if (tree_view->priv->anchor)
3356 gtk_tree_row_reference_free (tree_view->priv->anchor);
3358 tree_view->priv->anchor =
3359 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3360 tree_view->priv->model,
3363 gtk_tree_path_free (tmp_path);
3365 /* ... and the cursor to the end path */
3366 tmp_path = _pspp_sheet_view_find_path (tree_view,
3367 tree_view->priv->rubber_band_end_node);
3368 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE, 0); /* XXX mode? */
3369 gtk_tree_path_free (tmp_path);
3371 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3374 /* Clear status variables */
3375 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3376 tree_view->priv->rubber_band_shift = 0;
3377 tree_view->priv->rubber_band_ctrl = 0;
3379 tree_view->priv->rubber_band_start_node = -1;
3380 tree_view->priv->rubber_band_end_node = -1;
3384 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3388 gboolean skip_start,
3391 if (start_node == end_node)
3394 /* We skip the first node and jump inside the loop */
3400 /* Small optimization by assuming insensitive nodes are never
3405 if (tree_view->priv->rubber_band_shift)
3406 pspp_sheet_view_node_select (tree_view, start_node);
3407 else if (tree_view->priv->rubber_band_ctrl)
3409 /* Toggle the selection state */
3410 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3411 pspp_sheet_view_node_unselect (tree_view, start_node);
3413 pspp_sheet_view_node_select (tree_view, start_node);
3416 pspp_sheet_view_node_select (tree_view, start_node);
3420 /* Mirror the above */
3421 if (tree_view->priv->rubber_band_shift)
3422 pspp_sheet_view_node_unselect (tree_view, start_node);
3423 else if (tree_view->priv->rubber_band_ctrl)
3425 /* Toggle the selection state */
3426 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3427 pspp_sheet_view_node_unselect (tree_view, start_node);
3429 pspp_sheet_view_node_select (tree_view, start_node);
3432 pspp_sheet_view_node_unselect (tree_view, start_node);
3435 _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3437 if (start_node == end_node)
3442 start_node = pspp_sheet_view_node_next (tree_view, start_node);
3445 /* Ran out of tree */
3448 if (skip_end && start_node == end_node)
3455 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3458 return node * tree_view->priv->fixed_height;
3462 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3466 int fixed_height = tree_view->priv->fixed_height;
3467 if (fixed_height <= 0
3469 || height >= tree_view->priv->row_count * fixed_height)
3476 *new_node = height / fixed_height;
3477 return height % fixed_height;
3482 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3487 pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3488 pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3490 /* Handle the start area first */
3491 if (tree_view->priv->rubber_band_start_node < 0)
3493 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3500 else if (start_node < tree_view->priv->rubber_band_start_node)
3502 /* New node is above the old one; selection became bigger */
3503 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3505 tree_view->priv->rubber_band_start_node,
3510 else if (start_node > tree_view->priv->rubber_band_start_node)
3512 /* New node is below the old one; selection became smaller */
3513 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3514 tree_view->priv->rubber_band_start_node,
3521 tree_view->priv->rubber_band_start_node = start_node;
3523 /* Next, handle the end area */
3524 if (tree_view->priv->rubber_band_end_node < 0)
3526 /* In the event this happens, start_node was also -1; this case is
3530 else if (end_node < 0)
3532 /* Find the last node in the tree */
3533 pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3536 /* Selection reached end of the tree */
3537 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3538 tree_view->priv->rubber_band_end_node,
3544 else if (end_node > tree_view->priv->rubber_band_end_node)
3546 /* New node is below the old one; selection became bigger */
3547 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3548 tree_view->priv->rubber_band_end_node,
3554 else if (end_node < tree_view->priv->rubber_band_end_node)
3556 /* New node is above the old one; selection became smaller */
3557 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3559 tree_view->priv->rubber_band_end_node,
3565 tree_view->priv->rubber_band_end_node = end_node;
3568 #define GDK_RECTANGLE_PTR(X) ((GdkRectangle *)(X))
3571 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3574 cairo_rectangle_int_t old_area;
3575 cairo_rectangle_int_t new_area;
3576 cairo_rectangle_int_t common;
3577 cairo_region_t *invalid_region;
3578 PsppSheetViewColumn *column;
3580 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3581 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3582 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3583 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3585 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3588 y = MAX (y, 0) + tree_view->priv->dy;
3590 new_area.x = MIN (tree_view->priv->press_start_x, x);
3591 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3592 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3593 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3595 invalid_region = cairo_region_create_rectangle (&old_area);
3596 cairo_region_union_rectangle (invalid_region, &new_area);
3598 gdk_rectangle_intersect (GDK_RECTANGLE_PTR (&old_area),
3599 GDK_RECTANGLE_PTR (&new_area), GDK_RECTANGLE_PTR (&common));
3600 if (common.width > 2 && common.height > 2)
3602 cairo_region_t *common_region;
3604 /* make sure the border is invalidated */
3610 common_region = cairo_region_create_rectangle (&common);
3612 cairo_region_subtract (invalid_region, common_region);
3613 cairo_region_destroy (common_region);
3616 #if GTK_MAJOR_VERSION == 3
3617 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
3620 cairo_rectangle_int_t extents;
3622 cairo_region_get_extents (invalid_region, &extents);
3623 ereg = gdk_region_rectangle (GDK_RECTANGLE_PTR (&extents));
3624 gdk_window_invalidate_region (tree_view->priv->bin_window, ereg, TRUE);
3625 gdk_region_destroy (ereg);
3629 cairo_region_destroy (invalid_region);
3631 tree_view->priv->rubber_band_x = x;
3632 tree_view->priv->rubber_band_y = y;
3633 pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3635 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3636 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3637 tree_view->priv->anchor_column,
3640 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3642 pspp_sheet_view_update_rubber_band_selection (tree_view);
3647 pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view,
3652 GdkRectangle rubber_rect;
3656 rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3657 rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3658 rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3659 rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3661 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3664 cr = gdk_cairo_create (tree_view->priv->bin_window);
3665 cairo_set_line_width (cr, 1.0);
3667 style = gtk_widget_get_style (GTK_WIDGET (tree_view));
3668 cairo_set_source_rgba (cr,
3669 style->fg[GTK_STATE_NORMAL].red / 65535.,
3670 style->fg[GTK_STATE_NORMAL].green / 65535.,
3671 style->fg[GTK_STATE_NORMAL].blue / 65535.,
3674 gdk_cairo_rectangle (cr, &rect);
3678 cairo_set_source_rgb (cr,
3679 style->fg[GTK_STATE_NORMAL].red / 65535.,
3680 style->fg[GTK_STATE_NORMAL].green / 65535.,
3681 style->fg[GTK_STATE_NORMAL].blue / 65535.);
3683 cairo_rectangle (cr,
3684 rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3685 rubber_rect.width - 1, rubber_rect.height - 1);
3694 pspp_sheet_view_motion_bin_window (GtkWidget *widget,
3695 GdkEventMotion *event)
3697 PsppSheetView *tree_view;
3701 tree_view = (PsppSheetView *) widget;
3703 if (tree_view->priv->row_count == 0)
3706 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3708 GdkRectangle background_area, cell_area;
3709 PsppSheetViewColumn *column;
3711 if (find_click (tree_view, event->x, event->y, &node, &column,
3712 &background_area, &cell_area)
3713 && tree_view->priv->focus_column == column
3714 && tree_view->priv->press_start_node == node)
3717 gtk_grab_add (GTK_WIDGET (tree_view));
3718 pspp_sheet_view_update_rubber_band (tree_view);
3720 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3722 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3724 pspp_sheet_view_update_rubber_band (tree_view);
3726 add_scroll_timeout (tree_view);
3729 /* only check for an initiated drag when a button is pressed */
3730 if (tree_view->priv->pressed_button >= 0
3731 && !tree_view->priv->rubber_band_status)
3732 pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3734 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3738 pspp_sheet_view_find_offset (tree_view, new_y, &node);
3740 tree_view->priv->event_last_x = event->x;
3741 tree_view->priv->event_last_y = event->y;
3743 prelight_or_select (tree_view, node, event->x, event->y);
3749 pspp_sheet_view_motion (GtkWidget *widget,
3750 GdkEventMotion *event)
3752 PsppSheetView *tree_view;
3754 tree_view = (PsppSheetView *) widget;
3756 /* Resizing a column */
3757 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3758 return pspp_sheet_view_motion_resize_column (widget, event);
3761 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3762 return pspp_sheet_view_motion_drag_column (widget, event);
3764 /* Sanity check it */
3765 if (event->window == tree_view->priv->bin_window)
3766 return pspp_sheet_view_motion_bin_window (widget, event);
3771 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3772 * the tree is empty.
3775 invalidate_empty_focus (PsppSheetView *tree_view)
3779 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3784 area.width = gdk_window_get_width (tree_view->priv->bin_window);
3785 area.height = gdk_window_get_height (tree_view->priv->bin_window);
3786 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3789 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3793 draw_empty_focus (PsppSheetView *tree_view)
3795 GtkWidget *widget = GTK_WIDGET (tree_view);
3797 cairo_t *cr = gdk_cairo_create (tree_view->priv->bin_window);
3799 if (!gtk_widget_has_focus (widget))
3802 w = gdk_window_get_width (tree_view->priv->bin_window);
3803 h = gdk_window_get_height (tree_view->priv->bin_window);
3809 gtk_paint_focus (gtk_widget_get_style (widget),
3811 gtk_widget_get_state (widget),
3819 pspp_sheet_view_draw_vertical_grid_lines (PsppSheetView *tree_view,
3821 gint n_visible_columns,
3825 GList *list = tree_view->priv->columns;
3829 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3830 && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3833 /* Only draw the lines for visible rows and columns */
3834 for (list = tree_view->priv->columns; list; list = list->next, i++)
3836 PsppSheetViewColumn *column = list->data;
3839 if (! column->visible)
3842 current_x += column->width;
3844 /* Generally the grid lines should fit within the column, but for the
3845 last visible column we put it just past the end of the column.
3846 (Otherwise horizontal grid lines sometimes stick out by one pixel.) */
3848 if (i != n_visible_columns - 1)
3851 cairo_set_line_width (cr, 1.0);
3852 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
3853 cairo_move_to (cr, x + 0.5, min_y);
3854 cairo_line_to (cr, x + 0.5, max_y - min_y);
3859 /* Warning: Very scary function.
3860 * Modify at your own risk
3862 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3863 * FIXME: It's not...
3866 pspp_sheet_view_bin_expose (GtkWidget *widget,
3869 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3874 int drag_highlight = -1;
3877 gint y_offset, cell_offset;
3879 GdkRectangle background_area;
3880 GdkRectangle cell_area;
3882 gint bin_window_width;
3883 gint bin_window_height;
3884 GtkTreePath *cursor_path;
3885 GtkTreePath *drag_dest_path;
3886 GList *first_column, *last_column;
3887 gint vertical_separator;
3888 gint horizontal_separator;
3889 gint focus_line_width;
3890 gboolean allow_rules;
3891 gboolean has_special_cell;
3893 gint n_visible_columns;
3894 gint grid_line_width;
3895 gboolean row_ending_details;
3896 gboolean draw_vgrid_lines, draw_hgrid_lines;
3900 GtkAllocation allocation;
3901 gtk_widget_get_allocation (widget, &allocation);
3905 Zarea.height = allocation.height;
3907 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3909 gtk_widget_style_get (widget,
3910 "horizontal-separator", &horizontal_separator,
3911 "vertical-separator", &vertical_separator,
3912 "allow-rules", &allow_rules,
3913 "focus-line-width", &focus_line_width,
3914 "row-ending-details", &row_ending_details,
3917 if (tree_view->priv->row_count == 0)
3919 draw_empty_focus (tree_view);
3924 /* clip event->area to the visible area */
3925 if (Zarea.height < 0.5)
3929 validate_visible_area (tree_view);
3931 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, Zarea.y);
3935 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3937 gdk_window_get_width (tree_view->priv->bin_window);
3940 gdk_window_get_height (tree_view->priv->bin_window);
3943 if (tree_view->priv->height < bin_window_height)
3945 gtk_paint_flat_box (gtk_widget_get_style (widget),
3947 gtk_widget_get_state (widget),
3951 0, tree_view->priv->height,
3953 bin_window_height - tree_view->priv->height);
3959 /* find the path for the node */
3960 path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3961 gtk_tree_model_get_iter (tree_view->priv->model,
3964 gtk_tree_path_free (path);
3967 drag_dest_path = NULL;
3969 if (tree_view->priv->cursor)
3970 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3973 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3975 if (tree_view->priv->drag_dest_row)
3976 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3979 _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3983 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3984 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3986 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
3987 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3989 if (draw_vgrid_lines || draw_hgrid_lines)
3990 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
3992 n_visible_columns = 0;
3993 for (list = tree_view->priv->columns; list; list = list->next)
3995 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
3997 n_visible_columns ++;
4000 /* Find the last column */
4001 for (last_column = g_list_last (tree_view->priv->columns);
4002 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
4003 last_column = last_column->prev)
4007 for (first_column = g_list_first (tree_view->priv->columns);
4008 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
4009 first_column = first_column->next)
4012 /* Actually process the expose event. To do this, we want to
4013 * start at the first node of the event, and walk the tree in
4014 * order, drawing each successive node.
4021 gboolean is_first = FALSE;
4022 gboolean is_last = FALSE;
4023 gboolean done = FALSE;
4026 max_height = ROW_HEIGHT (tree_view);
4030 background_area.y = y_offset + Zarea.y;
4031 background_area.height = max_height;
4032 max_y = background_area.y + max_height;
4036 if (node == tree_view->priv->prelight_node)
4037 flags |= GTK_CELL_RENDERER_PRELIT;
4039 selected = pspp_sheet_view_node_is_selected (tree_view, node);
4043 if (tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
4045 /* we *need* to set cell data on all cells before the call
4046 * to _has_special_cell, else _has_special_cell() does not
4047 * return a correct value.
4049 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4051 list = (rtl ? list->prev : list->next))
4053 PsppSheetViewColumn *column = list->data;
4054 pspp_sheet_view_column_cell_set_cell_data (column,
4055 tree_view->priv->model,
4059 has_special_cell = pspp_sheet_view_has_special_cell (tree_view);
4062 has_special_cell = tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
4064 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4066 list = (rtl ? list->prev : list->next))
4068 PsppSheetViewColumn *column = list->data;
4069 const gchar *detail = NULL;
4070 gboolean selected_column;
4073 if (!column->visible)
4076 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
4077 selected_column = column->selected && column->selectable;
4079 selected_column = TRUE;
4082 if (cell_offset > Zarea.x + Zarea.width ||
4083 cell_offset + column->width < Zarea.x)
4085 cell_offset += column->width;
4090 if (selected && selected_column)
4091 flags |= GTK_CELL_RENDERER_SELECTED;
4093 flags &= ~GTK_CELL_RENDERER_SELECTED;
4095 if (column->show_sort_indicator)
4096 flags |= GTK_CELL_RENDERER_SORTED;
4098 flags &= ~GTK_CELL_RENDERER_SORTED;
4101 flags |= GTK_CELL_RENDERER_FOCUSED;
4103 flags &= ~GTK_CELL_RENDERER_FOCUSED;
4105 background_area.x = cell_offset;
4106 background_area.width = column->width;
4108 cell_area = background_area;
4109 cell_area.y += vertical_separator / 2;
4110 cell_area.x += horizontal_separator / 2;
4111 cell_area.height -= vertical_separator;
4112 cell_area.width -= horizontal_separator;
4114 if (draw_vgrid_lines)
4116 if (list == first_column)
4118 cell_area.width -= grid_line_width / 2;
4120 else if (list == last_column)
4122 cell_area.x += grid_line_width / 2;
4123 cell_area.width -= grid_line_width / 2;
4127 cell_area.x += grid_line_width / 2;
4128 cell_area.width -= grid_line_width;
4132 if (draw_hgrid_lines)
4134 cell_area.y += grid_line_width / 2;
4135 cell_area.height -= grid_line_width;
4139 if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4141 cell_offset += column->width;
4146 pspp_sheet_view_column_cell_set_cell_data (column,
4147 tree_view->priv->model,
4150 /* Select the detail for drawing the cell. relevant
4151 * factors are parity, sortedness, and whether to
4154 if (allow_rules && tree_view->priv->has_rules)
4156 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4157 n_visible_columns >= 3)
4160 detail = "cell_odd_ruled_sorted";
4162 detail = "cell_even_ruled_sorted";
4167 detail = "cell_odd_ruled";
4169 detail = "cell_even_ruled";
4174 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4175 n_visible_columns >= 3)
4178 detail = "cell_odd_sorted";
4180 detail = "cell_even_sorted";
4185 detail = "cell_odd";
4187 detail = "cell_even";
4193 if (gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE)
4194 state = GTK_STATE_INSENSITIVE;
4195 else if (flags & GTK_CELL_RENDERER_SELECTED)
4196 state = GTK_STATE_SELECTED;
4198 state = GTK_STATE_NORMAL;
4200 /* Draw background */
4201 if (row_ending_details)
4203 char new_detail[128];
4205 is_first = (rtl ? !list->next : !list->prev);
4206 is_last = (rtl ? !list->prev : !list->next);
4208 /* (I don't like the snprintfs either, but couldn't find a
4211 if (is_first && is_last)
4212 g_snprintf (new_detail, 127, "%s", detail);
4214 g_snprintf (new_detail, 127, "%s_start", detail);
4216 g_snprintf (new_detail, 127, "%s_end", detail);
4218 g_snprintf (new_detail, 128, "%s_middle", detail);
4220 gtk_paint_flat_box (gtk_widget_get_style (widget),
4228 background_area.width,
4229 background_area.height);
4233 gtk_paint_flat_box (gtk_widget_get_style (widget),
4241 background_area.width,
4242 background_area.height);
4245 if (draw_hgrid_lines)
4247 cairo_set_line_width (cr, 1.0);
4248 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
4250 if (background_area.y >= 0)
4253 gdk_draw_line (event->window,
4254 tree_view->priv->grid_line_gc[widget->state],
4255 background_area.x, background_area.y,
4256 background_area.x + background_area.width,
4259 cairo_move_to (cr, background_area.x, background_area.y - 0.5);
4260 cairo_line_to (cr, background_area.x + background_area.width,
4261 background_area.y - 0.5);
4265 if (y_offset + max_height <= Zarea.height - 0.5)
4268 gdk_draw_line (event->window,
4269 tree_view->priv->grid_line_gc[widget->state],
4270 background_area.x, background_area.y + max_height,
4271 background_area.x + background_area.width,
4272 background_area.y + max_height);
4275 cairo_move_to (cr, background_area.x, background_area.y + max_height - 0.5);
4276 cairo_line_to (cr, background_area.x + background_area.width,
4277 background_area.y + max_height - 0.5);
4283 _pspp_sheet_view_column_cell_render (column,
4289 if (node == cursor && has_special_cell &&
4290 ((column == tree_view->priv->focus_column &&
4291 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4292 gtk_widget_has_focus (widget)) ||
4293 (column == tree_view->priv->edited_column)))
4295 _pspp_sheet_view_column_cell_draw_focus (column,
4302 cell_offset += column->width;
4305 if (cell_offset < Zarea.x)
4307 gtk_paint_flat_box (gtk_widget_get_style (widget),
4315 Zarea.x - cell_offset,
4316 background_area.height);
4319 if (node == drag_highlight)
4321 /* Draw indicator for the drop
4323 gint highlight_y = -1;
4327 switch (tree_view->priv->drag_dest_pos)
4329 case PSPP_SHEET_VIEW_DROP_BEFORE:
4330 highlight_y = background_area.y - 1;
4331 if (highlight_y < 0)
4335 case PSPP_SHEET_VIEW_DROP_AFTER:
4336 highlight_y = background_area.y + background_area.height - 1;
4339 case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE:
4340 case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER:
4341 _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node);
4345 width = gdk_window_get_width (tree_view->priv->bin_window);
4347 if (row_ending_details)
4348 gtk_paint_focus (gtk_widget_get_style (widget),
4350 gtk_widget_get_state (widget),
4353 ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4354 : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4355 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4356 - focus_line_width / 2,
4357 width, ROW_HEIGHT (tree_view)
4358 - focus_line_width + 1);
4360 gtk_paint_focus (gtk_widget_get_style (widget),
4362 gtk_widget_get_state (widget),
4364 "treeview-drop-indicator",
4365 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4366 - focus_line_width / 2,
4367 width, ROW_HEIGHT (tree_view)
4368 - focus_line_width + 1);
4373 if (highlight_y >= 0)
4375 gdk_draw_line (event->window,
4376 widget->style->fg_gc[gtk_widget_get_state (widget)],
4379 rtl ? 0 : bin_window_width,
4385 /* draw the big row-spanning focus rectangle, if needed */
4386 if (!has_special_cell && node == cursor &&
4387 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4388 gtk_widget_has_focus (widget))
4390 gint tmp_y, tmp_height;
4392 GtkStateType focus_rect_state;
4395 flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
4396 (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
4397 (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE :
4400 width = gdk_window_get_width (tree_view->priv->bin_window);
4402 if (draw_hgrid_lines)
4404 tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node) + grid_line_width / 2;
4405 tmp_height = ROW_HEIGHT (tree_view) - grid_line_width;
4409 tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node);
4410 tmp_height = ROW_HEIGHT (tree_view);
4413 if (row_ending_details)
4414 gtk_paint_focus (gtk_widget_get_style (widget),
4419 ? (is_last ? "treeview" : "treeview-left" )
4420 : (is_last ? "treeview-right" : "treeview-middle" )),
4424 gtk_paint_focus (gtk_widget_get_style (widget),
4433 y_offset += max_height;
4437 node = pspp_sheet_view_node_next (tree_view, node);
4440 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
4444 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
4451 while (y_offset < Zarea.height);
4454 pspp_sheet_view_draw_vertical_grid_lines (tree_view, cr, n_visible_columns,
4458 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4460 GdkRectangle *rectangles;
4463 gdk_region_get_rectangles (event->region,
4467 while (n_rectangles--)
4468 pspp_sheet_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
4470 g_free (rectangles);
4475 gtk_tree_path_free (cursor_path);
4478 gtk_tree_path_free (drag_dest_path);
4485 pspp_sheet_view_draw (GtkWidget *widget,
4488 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4489 GtkAllocation allocation;
4490 gtk_widget_get_allocation (widget, &allocation);
4492 if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window))
4498 cairo_translate (cr, 0, gdk_window_get_height (tree_view->priv->header_window));
4499 retval = pspp_sheet_view_bin_expose (widget, cr);
4502 /* We can't just chain up to Container::expose as it will try to send the
4503 * event to the headers, so we handle propagating it to our children
4504 * (eg. widgets being edited) ourselves.
4506 tmp_list = tree_view->priv->children;
4509 PsppSheetViewChild *child = tmp_list->data;
4510 tmp_list = tmp_list->next;
4512 gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
4517 else if (gtk_cairo_should_draw_window (cr, tree_view->priv->header_window))
4519 gint n_visible_columns;
4522 gtk_paint_flat_box (gtk_widget_get_style (widget),
4534 for (list = tree_view->priv->columns; list != NULL; list = list->next)
4536 PsppSheetViewColumn *column = list->data;
4538 if (column == tree_view->priv->drag_column || !column->visible)
4541 if (span_intersects (column->allocation.x, column->allocation.width,
4542 allocation.x, allocation.width)
4543 && column->button != NULL)
4544 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4545 column->button, cr);
4548 n_visible_columns = 0;
4549 for (list = tree_view->priv->columns; list; list = list->next)
4551 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4553 n_visible_columns ++;
4555 pspp_sheet_view_draw_vertical_grid_lines (tree_view,
4563 else if (gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window))
4565 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4566 tree_view->priv->drag_column->button,
4583 /* returns 0x1 when no column has been found -- yes it's hackish */
4584 static PsppSheetViewColumn *
4585 pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
4586 PsppSheetViewColumn *column,
4589 PsppSheetViewColumn *left_column = NULL;
4590 PsppSheetViewColumn *cur_column = NULL;
4593 if (!column->reorderable)
4594 return (PsppSheetViewColumn *)0x1;
4596 switch (drop_position)
4599 /* find first column where we can drop */
4600 tmp_list = tree_view->priv->columns;
4601 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4602 return (PsppSheetViewColumn *)0x1;
4606 g_assert (tmp_list);
4608 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4609 tmp_list = tmp_list->next;
4611 if (left_column && left_column->visible == FALSE)
4614 if (!tree_view->priv->column_drop_func)
4617 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4619 left_column = cur_column;
4626 if (!tree_view->priv->column_drop_func)
4629 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4632 return (PsppSheetViewColumn *)0x1;
4636 /* find first column after column where we can drop */
4637 tmp_list = tree_view->priv->columns;
4639 for (; tmp_list; tmp_list = tmp_list->next)
4640 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4643 if (!tmp_list || !tmp_list->next)
4644 return (PsppSheetViewColumn *)0x1;
4646 tmp_list = tmp_list->next;
4647 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4648 tmp_list = tmp_list->next;
4652 g_assert (tmp_list);
4654 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4655 tmp_list = tmp_list->next;
4657 if (left_column && left_column->visible == FALSE)
4659 left_column = cur_column;
4661 tmp_list = tmp_list->next;
4665 if (!tree_view->priv->column_drop_func)
4668 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4670 left_column = cur_column;
4677 if (!tree_view->priv->column_drop_func)
4680 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4683 return (PsppSheetViewColumn *)0x1;
4687 /* find first column before column where we can drop */
4688 tmp_list = tree_view->priv->columns;
4690 for (; tmp_list; tmp_list = tmp_list->next)
4691 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4694 if (!tmp_list || !tmp_list->prev)
4695 return (PsppSheetViewColumn *)0x1;
4697 tmp_list = tmp_list->prev;
4698 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4699 tmp_list = tmp_list->prev;
4703 g_assert (tmp_list);
4705 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4707 if (left_column && !left_column->visible)
4709 /*if (!tmp_list->prev)
4710 return (PsppSheetViewColumn *)0x1;
4713 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4714 tmp_list = tmp_list->prev->prev;
4717 cur_column = left_column;
4719 tmp_list = tmp_list->prev;
4723 if (!tree_view->priv->column_drop_func)
4726 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4729 cur_column = left_column;
4730 tmp_list = tmp_list->prev;
4733 if (!tree_view->priv->column_drop_func)
4736 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4739 return (PsppSheetViewColumn *)0x1;
4743 /* same as DROP_HOME case, but doing it backwards */
4744 tmp_list = g_list_last (tree_view->priv->columns);
4747 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4748 return (PsppSheetViewColumn *)0x1;
4752 g_assert (tmp_list);
4754 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4756 if (left_column && !left_column->visible)
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, left_column, cur_column, tree_view->priv->column_drop_func_data))
4768 cur_column = left_column;
4769 tmp_list = tmp_list->prev;
4772 if (!tree_view->priv->column_drop_func)
4775 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4778 return (PsppSheetViewColumn *)0x1;
4782 return (PsppSheetViewColumn *)0x1;
4786 pspp_sheet_view_key_press (GtkWidget *widget,
4789 PsppSheetView *tree_view = (PsppSheetView *) widget;
4791 if (tree_view->priv->rubber_band_status)
4793 if (event->keyval == GDK_Escape)
4794 pspp_sheet_view_stop_rubber_band (tree_view);
4799 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4801 if (event->keyval == GDK_Escape)
4803 tree_view->priv->cur_reorder = NULL;
4804 pspp_sheet_view_button_release_drag_column (widget, NULL);
4809 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4811 GList *focus_column;
4814 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4816 for (focus_column = tree_view->priv->columns;
4818 focus_column = focus_column->next)
4820 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4822 if (column->button && gtk_widget_has_focus (column->button))
4827 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4828 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4829 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4831 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4833 if (!column->resizable)
4835 gtk_widget_error_bell (widget);
4839 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4840 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4842 gint old_width = column->resized_width;
4844 column->resized_width = MAX (column->resized_width,
4846 column->resized_width -= 2;
4847 if (column->resized_width < 0)
4848 column->resized_width = 0;
4850 if (column->min_width == -1)
4851 column->resized_width = MAX (column->button_request,
4852 column->resized_width);
4854 column->resized_width = MAX (column->min_width,
4855 column->resized_width);
4857 if (column->max_width != -1)
4858 column->resized_width = MIN (column->resized_width,
4861 column->use_resized_width = TRUE;
4863 if (column->resized_width != old_width)
4864 gtk_widget_queue_resize (widget);
4866 gtk_widget_error_bell (widget);
4868 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4869 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4871 gint old_width = column->resized_width;
4873 column->resized_width = MAX (column->resized_width,
4875 column->resized_width += 2;
4877 if (column->max_width != -1)
4878 column->resized_width = MIN (column->resized_width,
4881 column->use_resized_width = TRUE;
4883 if (column->resized_width != old_width)
4884 gtk_widget_queue_resize (widget);
4886 gtk_widget_error_bell (widget);
4893 (event->state & GDK_MOD1_MASK) &&
4894 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4895 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4896 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4897 || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4899 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4901 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4902 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4904 PsppSheetViewColumn *col;
4905 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
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 == (rtl ? GDK_Left : GDK_Right)
4912 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4914 PsppSheetViewColumn *col;
4915 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4916 if (col != (PsppSheetViewColumn *)0x1)
4917 pspp_sheet_view_move_column_after (tree_view, column, col);
4919 gtk_widget_error_bell (widget);
4921 else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4923 PsppSheetViewColumn *col;
4924 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4925 if (col != (PsppSheetViewColumn *)0x1)
4926 pspp_sheet_view_move_column_after (tree_view, column, col);
4928 gtk_widget_error_bell (widget);
4930 else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4932 PsppSheetViewColumn *col;
4933 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4934 if (col != (PsppSheetViewColumn *)0x1)
4935 pspp_sheet_view_move_column_after (tree_view, column, col);
4937 gtk_widget_error_bell (widget);
4944 /* Chain up to the parent class. It handles the keybindings. */
4945 if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4948 if (tree_view->priv->search_entry_avoid_unhandled_binding)
4950 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4954 /* We pass the event to the search_entry. If its text changes, then we start
4955 * the typeahead find capabilities. */
4956 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4957 && tree_view->priv->enable_search
4958 && !tree_view->priv->search_custom_entry_set)
4960 GdkEvent *new_event;
4962 const char *new_text;
4965 gboolean text_modified;
4966 gulong popup_menu_id;
4968 pspp_sheet_view_ensure_interactive_directory (tree_view);
4970 /* Make a copy of the current text */
4971 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4972 new_event = gdk_event_copy ((GdkEvent *) event);
4973 g_object_unref (((GdkEventKey *) new_event)->window);
4974 ((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window));
4975 gtk_widget_realize (tree_view->priv->search_window);
4977 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
4978 "popup-menu", G_CALLBACK (gtk_true),
4981 /* Move the entry off screen */
4982 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4983 gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4984 gdk_screen_get_width (screen) + 1,
4985 gdk_screen_get_height (screen) + 1);
4986 gtk_widget_show (tree_view->priv->search_window);
4988 /* Send the event to the window. If the preedit_changed signal is emitted
4989 * during this event, we will set priv->imcontext_changed */
4990 tree_view->priv->imcontext_changed = FALSE;
4991 retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4992 gdk_event_free (new_event);
4993 gtk_widget_hide (tree_view->priv->search_window);
4995 g_signal_handler_disconnect (tree_view->priv->search_entry,
4998 /* We check to make sure that the entry tried to handle the text, and that
4999 * the text has changed.
5001 new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
5002 text_modified = strcmp (old_text, new_text) != 0;
5004 if (tree_view->priv->imcontext_changed || /* we're in a preedit */
5005 (retval && text_modified)) /* ...or the text was modified */
5007 if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
5009 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
5014 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
5024 pspp_sheet_view_key_release (GtkWidget *widget,
5027 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
5029 if (tree_view->priv->rubber_band_status)
5032 return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
5035 /* FIXME Is this function necessary? Can I get an enter_notify event
5036 * w/o either an expose event or a mouse motion event?
5039 pspp_sheet_view_enter_notify (GtkWidget *widget,
5040 GdkEventCrossing *event)
5042 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
5046 /* Sanity check it */
5047 if (event->window != tree_view->priv->bin_window)
5050 if (tree_view->priv->row_count == 0)
5053 if (event->mode == GDK_CROSSING_GRAB ||
5054 event->mode == GDK_CROSSING_GTK_GRAB ||
5055 event->mode == GDK_CROSSING_GTK_UNGRAB ||
5056 event->mode == GDK_CROSSING_STATE_CHANGED)
5059 /* find the node internally */
5060 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
5063 pspp_sheet_view_find_offset (tree_view, new_y, &node);
5065 tree_view->priv->event_last_x = event->x;
5066 tree_view->priv->event_last_y = event->y;
5068 prelight_or_select (tree_view, node, event->x, event->y);
5074 pspp_sheet_view_leave_notify (GtkWidget *widget,
5075 GdkEventCrossing *event)
5077 PsppSheetView *tree_view;
5079 if (event->mode == GDK_CROSSING_GRAB)
5082 tree_view = PSPP_SHEET_VIEW (widget);
5084 if (tree_view->priv->prelight_node >= 0)
5085 _pspp_sheet_view_queue_draw_node (tree_view,
5086 tree_view->priv->prelight_node,
5089 tree_view->priv->event_last_x = -10000;
5090 tree_view->priv->event_last_y = -10000;
5092 prelight_or_select (tree_view,
5094 -1000, -1000); /* coords not possibly over an arrow */
5101 pspp_sheet_view_focus_out (GtkWidget *widget,
5102 GdkEventFocus *event)
5104 PsppSheetView *tree_view;
5106 tree_view = PSPP_SHEET_VIEW (widget);
5108 gtk_widget_queue_draw (widget);
5110 /* destroy interactive search dialog */
5111 if (tree_view->priv->search_window)
5112 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
5118 /* Incremental Reflow
5122 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
5125 GtkAllocation allocation;
5126 gint y = pspp_sheet_view_node_find_offset (tree_view, node)
5127 - gtk_adjustment_get_value (tree_view->priv->vadjustment)
5128 + TREE_VIEW_HEADER_HEIGHT (tree_view);
5130 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5132 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
5135 tree_view->priv->fixed_height);
5139 node_is_visible (PsppSheetView *tree_view,
5145 y = pspp_sheet_view_node_find_offset (tree_view, node);
5146 height = ROW_HEIGHT (tree_view);
5148 if (y >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5149 y + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5150 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5156 /* Returns the row height. */
5158 validate_row (PsppSheetView *tree_view,
5163 PsppSheetViewColumn *column;
5164 GList *list, *first_column, *last_column;
5166 gint horizontal_separator;
5167 gint vertical_separator;
5168 gint focus_line_width;
5169 gboolean draw_vgrid_lines, draw_hgrid_lines;
5171 gint grid_line_width;
5172 gboolean wide_separators;
5173 gint separator_height;
5175 gtk_widget_style_get (GTK_WIDGET (tree_view),
5176 "focus-padding", &focus_pad,
5177 "focus-line-width", &focus_line_width,
5178 "horizontal-separator", &horizontal_separator,
5179 "vertical-separator", &vertical_separator,
5180 "grid-line-width", &grid_line_width,
5181 "wide-separators", &wide_separators,
5182 "separator-height", &separator_height,
5186 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5187 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5189 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5190 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5192 for (last_column = g_list_last (tree_view->priv->columns);
5193 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5194 last_column = last_column->prev)
5197 for (first_column = g_list_first (tree_view->priv->columns);
5198 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5199 first_column = first_column->next)
5202 for (list = tree_view->priv->columns; list; list = list->next)
5207 column = list->data;
5209 if (! column->visible)
5212 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5213 pspp_sheet_view_column_cell_get_size (column,
5215 &tmp_width, &tmp_height);
5217 tmp_height += vertical_separator;
5218 height = MAX (height, tmp_height);
5220 tmp_width = tmp_width + horizontal_separator;
5222 if (draw_vgrid_lines)
5224 if (list->data == first_column || list->data == last_column)
5225 tmp_width += grid_line_width / 2.0;
5227 tmp_width += grid_line_width;
5230 if (tmp_width > column->requested_width)
5231 column->requested_width = tmp_width;
5234 if (draw_hgrid_lines)
5235 height += grid_line_width;
5237 tree_view->priv->post_validation_flag = TRUE;
5243 validate_visible_area (PsppSheetView *tree_view)
5245 GtkTreePath *path = NULL;
5246 GtkTreePath *above_path = NULL;
5249 gboolean size_changed = FALSE;
5251 gint area_above = 0;
5252 gint area_below = 0;
5253 GtkAllocation allocation;
5255 if (tree_view->priv->row_count == 0)
5258 if (tree_view->priv->scroll_to_path == NULL)
5261 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5263 total_height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5265 if (total_height == 0)
5268 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5271 /* we are going to scroll, and will update dy */
5272 _pspp_sheet_view_find_node (tree_view, path, &node);
5273 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5275 if (tree_view->priv->scroll_to_use_align)
5277 gint height = ROW_HEIGHT (tree_view);
5278 area_above = (total_height - height) *
5279 tree_view->priv->scroll_to_row_align;
5280 area_below = total_height - area_above - height;
5281 area_above = MAX (area_above, 0);
5282 area_below = MAX (area_below, 0);
5287 * 1) row not visible
5291 gint height = ROW_HEIGHT (tree_view);
5293 dy = pspp_sheet_view_node_find_offset (tree_view, node);
5295 if (dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5296 dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5297 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5299 /* row visible: keep the row at the same position */
5300 area_above = dy - gtk_adjustment_get_value (tree_view->priv->vadjustment);
5301 area_below = (gtk_adjustment_get_value (tree_view->priv->vadjustment) +
5302 gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5307 /* row not visible */
5309 && dy + height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5311 /* row at the beginning -- fixed */
5313 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment)
5314 - area_above - height;
5316 else if (dy >= (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5317 gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5319 /* row at the end -- fixed */
5320 area_above = dy - (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5321 gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
5322 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) -
5323 area_above - height;
5327 area_above = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - height;
5333 /* row somewhere in the middle, bring it to the top
5337 area_below = total_height - height;
5343 /* the scroll to isn't valid; ignore it.
5346 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5347 tree_view->priv->scroll_to_path = NULL;
5351 above_path = gtk_tree_path_copy (path);
5353 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5354 * backwards is much slower then forward, as there is no iter_prev function.
5355 * We go forwards first in case we run out of tree. Then we go backwards to
5358 while (node >= 0 && area_below > 0)
5360 gboolean done = FALSE;
5363 node = pspp_sheet_view_node_next (tree_view, node);
5366 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5368 gtk_tree_path_next (path);
5371 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5381 area_below -= ROW_HEIGHT (tree_view);
5383 gtk_tree_path_free (path);
5385 /* If we ran out of tree, and have extra area_below left, we need to add it
5388 area_above += area_below;
5390 _pspp_sheet_view_find_node (tree_view, above_path, &node);
5392 /* We walk backwards */
5393 while (area_above > 0)
5395 node = pspp_sheet_view_node_prev (tree_view, node);
5397 /* Always find the new path in the tree. We cannot just assume
5398 * a gtk_tree_path_prev() is enough here, as there might be children
5399 * in between this node and the previous sibling node. If this
5400 * appears to be a performance hotspot in profiles, we can look into
5401 * intrigate logic for keeping path, node and iter in sync like
5402 * we do for forward walks. (Which will be hard because of the lacking
5409 gtk_tree_path_free (above_path);
5410 above_path = _pspp_sheet_view_find_path (tree_view, node);
5412 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5414 area_above -= ROW_HEIGHT (tree_view);
5417 /* set the dy here to scroll to the path,
5418 * and sync the top row accordingly
5420 pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5421 pspp_sheet_view_top_row_to_dy (tree_view);
5423 /* update width/height and queue a resize */
5426 GtkRequisition requisition;
5428 /* We temporarily guess a size, under the assumption that it will be the
5429 * same when we get our next size_allocate. If we don't do this, we'll be
5430 * in an inconsistent state if we call top_row_to_dy. */
5432 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5433 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5434 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5435 gtk_adjustment_changed (tree_view->priv->hadjustment);
5436 gtk_adjustment_changed (tree_view->priv->vadjustment);
5437 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5440 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5441 tree_view->priv->scroll_to_path = NULL;
5444 gtk_tree_path_free (above_path);
5446 if (tree_view->priv->scroll_to_column)
5448 tree_view->priv->scroll_to_column = NULL;
5450 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5454 initialize_fixed_height_mode (PsppSheetView *tree_view)
5456 if (!tree_view->priv->row_count)
5459 if (tree_view->priv->fixed_height_set)
5462 if (tree_view->priv->fixed_height < 0)
5469 path = _pspp_sheet_view_find_path (tree_view, node);
5470 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5472 tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5474 gtk_tree_path_free (path);
5476 g_object_notify (G_OBJECT (tree_view), "fixed-height");
5480 /* Our strategy for finding nodes to validate is a little convoluted. We find
5481 * the left-most uninvalidated node. We then try walking right, validating
5482 * nodes. Once we find a valid node, we repeat the previous process of finding
5483 * the first invalid node.
5487 validate_rows_handler (PsppSheetView *tree_view)
5489 initialize_fixed_height_mode (tree_view);
5490 if (tree_view->priv->validate_rows_timer)
5492 g_source_remove (tree_view->priv->validate_rows_timer);
5493 tree_view->priv->validate_rows_timer = 0;
5500 do_presize_handler (PsppSheetView *tree_view)
5502 GtkRequisition requisition;
5504 validate_visible_area (tree_view);
5505 tree_view->priv->presize_handler_timer = 0;
5507 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5510 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5512 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5513 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5514 gtk_adjustment_changed (tree_view->priv->hadjustment);
5515 gtk_adjustment_changed (tree_view->priv->vadjustment);
5516 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5522 presize_handler_callback (gpointer data)
5524 do_presize_handler (PSPP_SHEET_VIEW (data));
5530 install_presize_handler (PsppSheetView *tree_view)
5532 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5535 if (! tree_view->priv->presize_handler_timer)
5537 tree_view->priv->presize_handler_timer =
5538 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5540 if (! tree_view->priv->validate_rows_timer)
5542 tree_view->priv->validate_rows_timer =
5543 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5548 scroll_sync_handler (PsppSheetView *tree_view)
5550 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5551 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5552 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5553 pspp_sheet_view_top_row_to_dy (tree_view);
5555 pspp_sheet_view_dy_to_top_row (tree_view);
5557 tree_view->priv->scroll_sync_timer = 0;
5563 install_scroll_sync_handler (PsppSheetView *tree_view)
5565 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5568 if (!tree_view->priv->scroll_sync_timer)
5570 tree_view->priv->scroll_sync_timer =
5571 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5576 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5580 gtk_tree_row_reference_free (tree_view->priv->top_row);
5584 tree_view->priv->top_row = NULL;
5585 tree_view->priv->top_row_dy = 0;
5589 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5590 tree_view->priv->top_row_dy = offset;
5594 /* Always call this iff dy is in the visible range. If the tree is empty, then
5595 * it's set to be NULL, and top_row_dy is 0;
5598 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5604 if (tree_view->priv->row_count == 0)
5606 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5610 offset = pspp_sheet_view_find_offset (tree_view,
5611 tree_view->priv->dy,
5616 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5620 path = _pspp_sheet_view_find_path (tree_view, node);
5621 pspp_sheet_view_set_top_row (tree_view, path, offset);
5622 gtk_tree_path_free (path);
5628 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5634 /* Avoid recursive calls */
5635 if (tree_view->priv->in_top_row_to_dy)
5638 if (tree_view->priv->top_row)
5639 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5646 _pspp_sheet_view_find_node (tree_view, path, &node);
5649 gtk_tree_path_free (path);
5653 /* keep dy and set new toprow */
5654 gtk_tree_row_reference_free (tree_view->priv->top_row);
5655 tree_view->priv->top_row = NULL;
5656 tree_view->priv->top_row_dy = 0;
5657 /* DO NOT install the idle handler */
5658 pspp_sheet_view_dy_to_top_row (tree_view);
5662 if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5664 /* new top row -- do NOT install the idle handler */
5665 pspp_sheet_view_dy_to_top_row (tree_view);
5669 new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5670 new_dy += tree_view->priv->top_row_dy;
5672 if (new_dy + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
5673 new_dy = tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
5675 new_dy = MAX (0, new_dy);
5677 tree_view->priv->in_top_row_to_dy = TRUE;
5678 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5679 tree_view->priv->in_top_row_to_dy = FALSE;
5684 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5686 install_presize_handler (tree_view);
5692 set_source_row (GdkDragContext *context,
5693 GtkTreeModel *model,
5694 GtkTreePath *source_row)
5696 g_object_set_data_full (G_OBJECT (context),
5697 "gtk-tree-view-source-row",
5698 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5699 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5703 get_source_row (GdkDragContext *context)
5705 GtkTreeRowReference *ref =
5706 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5709 return gtk_tree_row_reference_get_path (ref);
5716 GtkTreeRowReference *dest_row;
5717 guint path_down_mode : 1;
5718 guint empty_view_drop : 1;
5719 guint drop_append_mode : 1;
5724 dest_row_free (gpointer data)
5726 DestRow *dr = (DestRow *)data;
5728 gtk_tree_row_reference_free (dr->dest_row);
5729 g_slice_free (DestRow, dr);
5733 set_dest_row (GdkDragContext *context,
5734 GtkTreeModel *model,
5735 GtkTreePath *dest_row,
5736 gboolean path_down_mode,
5737 gboolean empty_view_drop,
5738 gboolean drop_append_mode)
5744 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5749 dr = g_slice_new (DestRow);
5751 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5752 dr->path_down_mode = path_down_mode != FALSE;
5753 dr->empty_view_drop = empty_view_drop != FALSE;
5754 dr->drop_append_mode = drop_append_mode != FALSE;
5756 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5757 dr, (GDestroyNotify) dest_row_free);
5761 get_dest_row (GdkDragContext *context,
5762 gboolean *path_down_mode)
5765 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5769 GtkTreePath *path = NULL;
5772 *path_down_mode = dr->path_down_mode;
5775 path = gtk_tree_row_reference_get_path (dr->dest_row);
5776 else if (dr->empty_view_drop)
5777 path = gtk_tree_path_new_from_indices (0, -1);
5781 if (path && dr->drop_append_mode)
5782 gtk_tree_path_next (path);
5790 /* Get/set whether drag_motion requested the drag data and
5791 * drag_data_received should thus not actually insert the data,
5792 * since the data doesn't result from a drop.
5795 set_status_pending (GdkDragContext *context,
5796 GdkDragAction suggested_action)
5798 g_object_set_data (G_OBJECT (context),
5799 "gtk-tree-view-status-pending",
5800 GINT_TO_POINTER (suggested_action));
5803 static GdkDragAction
5804 get_status_pending (GdkDragContext *context)
5806 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5807 "gtk-tree-view-status-pending"));
5810 static TreeViewDragInfo*
5811 get_info (PsppSheetView *tree_view)
5813 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5817 destroy_info (TreeViewDragInfo *di)
5819 g_slice_free (TreeViewDragInfo, di);
5822 static TreeViewDragInfo*
5823 ensure_info (PsppSheetView *tree_view)
5825 TreeViewDragInfo *di;
5827 di = get_info (tree_view);
5831 di = g_slice_new0 (TreeViewDragInfo);
5833 g_object_set_data_full (G_OBJECT (tree_view),
5834 "gtk-tree-view-drag-info",
5836 (GDestroyNotify) destroy_info);
5843 remove_info (PsppSheetView *tree_view)
5845 g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5850 drag_scan_timeout (gpointer data)
5852 PsppSheetView *tree_view;
5854 GdkModifierType state;
5855 GtkTreePath *path = NULL;
5856 PsppSheetViewColumn *column = NULL;
5857 GdkRectangle visible_rect;
5859 GDK_THREADS_ENTER ();
5861 tree_view = PSPP_SHEET_VIEW (data);
5863 gdk_window_get_pointer (tree_view->priv->bin_window,
5866 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5868 /* See if we are near the edge. */
5869 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5870 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5871 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5872 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5874 pspp_sheet_view_get_path_at_pos (tree_view,
5875 tree_view->priv->bin_window,
5884 pspp_sheet_view_scroll_to_cell (tree_view,
5890 gtk_tree_path_free (path);
5894 GDK_THREADS_LEAVE ();
5901 add_scroll_timeout (PsppSheetView *tree_view)
5903 if (tree_view->priv->scroll_timeout == 0)
5905 tree_view->priv->scroll_timeout =
5906 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5911 remove_scroll_timeout (PsppSheetView *tree_view)
5913 if (tree_view->priv->scroll_timeout != 0)
5915 g_source_remove (tree_view->priv->scroll_timeout);
5916 tree_view->priv->scroll_timeout = 0;
5921 check_model_dnd (GtkTreeModel *model,
5922 GType required_iface,
5923 const gchar *signal)
5925 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5927 g_warning ("You must override the default '%s' handler "
5928 "on PsppSheetView when using models that don't support "
5929 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5930 "is to connect to '%s' and call "
5931 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5932 "the default handler from running. Look at the source code "
5933 "for the default handler in gtktreeview.c to get an idea what "
5934 "your handler should do. (gtktreeview.c is in the GTK source "
5935 "code.) If you're using GTK from a language other than C, "
5936 "there may be a more natural way to override default handlers, e.g. via derivation.",
5937 signal, g_type_name (required_iface), signal);
5945 scroll_row_timeout (gpointer data)
5947 PsppSheetView *tree_view = data;
5949 pspp_sheet_view_horizontal_autoscroll (tree_view);
5950 pspp_sheet_view_vertical_autoscroll (tree_view);
5952 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5953 pspp_sheet_view_update_rubber_band (tree_view);
5958 /* Returns TRUE if event should not be propagated to parent widgets */
5960 set_destination_row (PsppSheetView *tree_view,
5961 GdkDragContext *context,
5962 /* coordinates relative to the widget */
5965 GdkDragAction *suggested_action,
5968 GtkTreePath *path = NULL;
5969 PsppSheetViewDropPosition pos;
5970 PsppSheetViewDropPosition old_pos;
5971 TreeViewDragInfo *di;
5973 GtkTreePath *old_dest_path = NULL;
5974 gboolean can_drop = FALSE;
5976 *suggested_action = 0;
5979 widget = GTK_WIDGET (tree_view);
5981 di = get_info (tree_view);
5983 if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5985 /* someone unset us as a drag dest, note that if
5986 * we return FALSE drag_leave isn't called
5989 pspp_sheet_view_set_drag_dest_row (tree_view,
5991 PSPP_SHEET_VIEW_DROP_BEFORE);
5993 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5995 return FALSE; /* no longer a drop site */
5998 *target = gtk_drag_dest_find_target (widget, context,
5999 gtk_drag_dest_get_target_list (widget));
6000 if (*target == GDK_NONE)
6005 if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
6011 GtkTreeModel *model;
6013 /* the row got dropped on empty space, let's setup a special case
6017 gtk_tree_path_free (path);
6019 model = pspp_sheet_view_get_model (tree_view);
6021 n_children = gtk_tree_model_iter_n_children (model, NULL);
6024 pos = PSPP_SHEET_VIEW_DROP_AFTER;
6025 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
6029 pos = PSPP_SHEET_VIEW_DROP_BEFORE;
6030 path = gtk_tree_path_new_from_indices (0, -1);
6040 /* If we left the current row's "open" zone, unset the timeout for
6043 pspp_sheet_view_get_drag_dest_row (tree_view,
6048 gtk_tree_path_free (old_dest_path);
6050 if (TRUE /* FIXME if the location droppable predicate */)
6058 GtkWidget *source_widget;
6060 *suggested_action = gdk_drag_context_get_suggested_action (context);
6061 source_widget = gtk_drag_get_source_widget (context);
6063 if (source_widget == widget)
6065 /* Default to MOVE, unless the user has
6066 * pressed ctrl or shift to affect available actions
6068 if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
6069 *suggested_action = GDK_ACTION_MOVE;
6072 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6077 /* can't drop here */
6078 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6080 PSPP_SHEET_VIEW_DROP_BEFORE);
6084 gtk_tree_path_free (path);
6090 get_logical_dest_row (PsppSheetView *tree_view,
6091 gboolean *path_down_mode,
6092 gboolean *drop_append_mode)
6094 /* adjust path to point to the row the drop goes in front of */
6095 GtkTreePath *path = NULL;
6096 PsppSheetViewDropPosition pos;
6098 g_return_val_if_fail (path_down_mode != NULL, NULL);
6099 g_return_val_if_fail (drop_append_mode != NULL, NULL);
6101 *path_down_mode = FALSE;
6102 *drop_append_mode = 0;
6104 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6109 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
6111 else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
6112 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
6113 *path_down_mode = TRUE;
6117 GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
6119 g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
6121 if (!gtk_tree_model_get_iter (model, &iter, path) ||
6122 !gtk_tree_model_iter_next (model, &iter))
6123 *drop_append_mode = 1;
6126 *drop_append_mode = 0;
6127 gtk_tree_path_next (path);
6135 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
6136 GdkEventMotion *event)
6138 GtkWidget *widget = GTK_WIDGET (tree_view);
6139 GdkDragContext *context;
6140 TreeViewDragInfo *di;
6141 GtkTreePath *path = NULL;
6143 gint cell_x, cell_y;
6144 GtkTreeModel *model;
6145 gboolean retval = FALSE;
6147 di = get_info (tree_view);
6149 if (di == NULL || !di->source_set)
6152 if (tree_view->priv->pressed_button < 0)
6155 if (!gtk_drag_check_threshold (widget,
6156 tree_view->priv->press_start_x,
6157 tree_view->priv->press_start_y,
6158 event->x, event->y))
6161 model = pspp_sheet_view_get_model (tree_view);
6166 button = tree_view->priv->pressed_button;
6167 tree_view->priv->pressed_button = -1;
6169 pspp_sheet_view_get_path_at_pos (tree_view,
6170 tree_view->priv->press_start_x,
6171 tree_view->priv->press_start_y,
6180 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6181 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6185 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6188 /* Now we can begin the drag */
6192 context = gtk_drag_begin (widget,
6193 gtk_drag_source_get_target_list (widget),
6198 set_source_row (context, model, path);
6202 gtk_tree_path_free (path);
6210 pspp_sheet_view_drag_begin (GtkWidget *widget,
6211 GdkDragContext *context)
6214 PsppSheetView *tree_view;
6215 GtkTreePath *path = NULL;
6216 gint cell_x, cell_y;
6218 TreeViewDragInfo *di;
6220 tree_view = PSPP_SHEET_VIEW (widget);
6222 /* if the user uses a custom DND source impl, we don't set the icon here */
6223 di = get_info (tree_view);
6225 if (di == NULL || !di->source_set)
6228 pspp_sheet_view_get_path_at_pos (tree_view,
6229 tree_view->priv->press_start_x,
6230 tree_view->priv->press_start_y,
6236 g_return_if_fail (path != NULL);
6238 row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6241 gtk_drag_set_icon_pixmap (context,
6242 gdk_drawable_get_colormap (row_pix),
6245 /* the + 1 is for the black border in the icon */
6246 tree_view->priv->press_start_x + 1,
6249 g_object_unref (row_pix);
6250 gtk_tree_path_free (path);
6256 pspp_sheet_view_drag_end (GtkWidget *widget,
6257 GdkDragContext *context)
6262 /* Default signal implementations for the drag signals */
6264 pspp_sheet_view_drag_data_get (GtkWidget *widget,
6265 GdkDragContext *context,
6266 GtkSelectionData *selection_data,
6270 PsppSheetView *tree_view;
6271 GtkTreeModel *model;
6272 TreeViewDragInfo *di;
6273 GtkTreePath *source_row;
6275 tree_view = PSPP_SHEET_VIEW (widget);
6277 model = pspp_sheet_view_get_model (tree_view);
6282 di = get_info (PSPP_SHEET_VIEW (widget));
6287 source_row = get_source_row (context);
6289 if (source_row == NULL)
6292 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6293 * any model; for DragSource models there are some other targets
6297 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6298 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6303 /* If drag_data_get does nothing, try providing row data. */
6304 if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6306 gtk_tree_set_row_drag_data (selection_data,
6312 gtk_tree_path_free (source_row);
6317 pspp_sheet_view_drag_data_delete (GtkWidget *widget,
6318 GdkDragContext *context)
6320 TreeViewDragInfo *di;
6321 GtkTreeModel *model;
6322 PsppSheetView *tree_view;
6323 GtkTreePath *source_row;
6325 tree_view = PSPP_SHEET_VIEW (widget);
6326 model = pspp_sheet_view_get_model (tree_view);
6328 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6331 di = get_info (tree_view);
6336 source_row = get_source_row (context);
6338 if (source_row == NULL)
6341 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6344 gtk_tree_path_free (source_row);
6346 set_source_row (context, NULL, NULL);
6350 pspp_sheet_view_drag_leave (GtkWidget *widget,
6351 GdkDragContext *context,
6354 /* unset any highlight row */
6355 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6357 PSPP_SHEET_VIEW_DROP_BEFORE);
6359 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6364 pspp_sheet_view_drag_motion (GtkWidget *widget,
6365 GdkDragContext *context,
6366 /* coordinates relative to the widget */
6372 GtkTreePath *path = NULL;
6373 PsppSheetViewDropPosition pos;
6374 PsppSheetView *tree_view;
6375 GdkDragAction suggested_action = 0;
6378 tree_view = PSPP_SHEET_VIEW (widget);
6380 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6383 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6385 /* we only know this *after* set_desination_row */
6386 empty = tree_view->priv->empty_view_drop;
6388 if (path == NULL && !empty)
6390 /* Can't drop here. */
6391 gdk_drag_status (context, 0, time);
6395 if (tree_view->priv->open_dest_timeout == 0 &&
6396 (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6397 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6403 add_scroll_timeout (tree_view);
6406 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6408 /* Request data so we can use the source row when
6409 * determining whether to accept the drop
6411 set_status_pending (context, suggested_action);
6412 gtk_drag_get_data (widget, context, target, time);
6416 set_status_pending (context, 0);
6417 gdk_drag_status (context, suggested_action, time);
6422 gtk_tree_path_free (path);
6429 pspp_sheet_view_drag_drop (GtkWidget *widget,
6430 GdkDragContext *context,
6431 /* coordinates relative to the widget */
6436 PsppSheetView *tree_view;
6438 GdkDragAction suggested_action = 0;
6439 GdkAtom target = GDK_NONE;
6440 TreeViewDragInfo *di;
6441 GtkTreeModel *model;
6442 gboolean path_down_mode;
6443 gboolean drop_append_mode;
6445 tree_view = PSPP_SHEET_VIEW (widget);
6447 model = pspp_sheet_view_get_model (tree_view);
6449 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6451 di = get_info (tree_view);
6456 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6459 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6462 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6464 if (target != GDK_NONE && path != NULL)
6466 /* in case a motion had requested drag data, change things so we
6467 * treat drag data receives as a drop.
6469 set_status_pending (context, 0);
6470 set_dest_row (context, model, path,
6471 path_down_mode, tree_view->priv->empty_view_drop,
6476 gtk_tree_path_free (path);
6478 /* Unset this thing */
6479 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6481 PSPP_SHEET_VIEW_DROP_BEFORE);
6483 if (target != GDK_NONE)
6485 gtk_drag_get_data (widget, context, target, time);
6493 pspp_sheet_view_drag_data_received (GtkWidget *widget,
6494 GdkDragContext *context,
6495 /* coordinates relative to the widget */
6498 GtkSelectionData *selection_data,
6503 TreeViewDragInfo *di;
6504 gboolean accepted = FALSE;
6505 GtkTreeModel *model;
6506 PsppSheetView *tree_view;
6507 GtkTreePath *dest_row;
6508 GdkDragAction suggested_action;
6509 gboolean path_down_mode;
6510 gboolean drop_append_mode;
6512 tree_view = PSPP_SHEET_VIEW (widget);
6514 model = pspp_sheet_view_get_model (tree_view);
6516 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6519 di = get_info (tree_view);
6524 suggested_action = get_status_pending (context);
6526 if (suggested_action)
6528 /* We are getting this data due to a request in drag_motion,
6529 * rather than due to a request in drag_drop, so we are just
6530 * supposed to call drag_status, not actually paste in the
6533 path = get_logical_dest_row (tree_view, &path_down_mode,
6537 suggested_action = 0;
6538 else if (path_down_mode)
6539 gtk_tree_path_down (path);
6541 if (suggested_action)
6543 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6549 path_down_mode = FALSE;
6550 gtk_tree_path_up (path);
6552 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6555 suggested_action = 0;
6558 suggested_action = 0;
6562 gdk_drag_status (context, suggested_action, time);
6565 gtk_tree_path_free (path);
6567 /* If you can't drop, remove user drop indicator until the next motion */
6568 if (suggested_action == 0)
6569 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6571 PSPP_SHEET_VIEW_DROP_BEFORE);
6576 dest_row = get_dest_row (context, &path_down_mode);
6578 if (dest_row == NULL)
6581 if (gtk_selection_data_get_length (selection_data) >= 0)
6585 gtk_tree_path_down (dest_row);
6586 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6587 dest_row, selection_data))
6588 gtk_tree_path_up (dest_row);
6592 if (gtk_selection_data_get_length (selection_data) >= 0)
6594 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6600 gtk_drag_finish (context,
6602 (gdk_drag_context_get_actions (context) == GDK_ACTION_MOVE),
6605 if (gtk_tree_path_get_depth (dest_row) == 1
6606 && gtk_tree_path_get_indices (dest_row)[0] == 0)
6608 /* special special case drag to "0", scroll to first item */
6609 if (!tree_view->priv->scroll_to_path)
6610 pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6613 gtk_tree_path_free (dest_row);
6616 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6621 /* GtkContainer Methods
6626 pspp_sheet_view_remove (GtkContainer *container,
6629 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6630 PsppSheetViewChild *child = NULL;
6633 tmp_list = tree_view->priv->children;
6636 child = tmp_list->data;
6637 if (child->widget == widget)
6639 gtk_widget_unparent (widget);
6641 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6642 g_list_free_1 (tmp_list);
6643 g_slice_free (PsppSheetViewChild, child);
6647 tmp_list = tmp_list->next;
6650 tmp_list = tree_view->priv->columns;
6654 PsppSheetViewColumn *column;
6655 column = tmp_list->data;
6656 if (column->button == widget)
6658 gtk_widget_unparent (widget);
6661 tmp_list = tmp_list->next;
6666 pspp_sheet_view_forall (GtkContainer *container,
6667 gboolean include_internals,
6668 GtkCallback callback,
6669 gpointer callback_data)
6671 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6672 PsppSheetViewChild *child = NULL;
6673 PsppSheetViewColumn *column;
6676 tmp_list = tree_view->priv->children;
6679 child = tmp_list->data;
6680 tmp_list = tmp_list->next;
6682 (* callback) (child->widget, callback_data);
6684 if (include_internals == FALSE)
6687 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6689 column = tmp_list->data;
6692 (* callback) (column->button, callback_data);
6696 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6697 * cells. If so we draw one big row-spanning focus rectangle.
6700 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6704 if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6705 return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6707 for (list = tree_view->priv->columns; list; list = list->next)
6709 if (!((PsppSheetViewColumn *)list->data)->visible)
6711 if (_pspp_sheet_view_column_count_special_cells (list->data))
6719 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6720 PsppSheetViewColumn *focus_column,
6721 gboolean clamp_column_visible)
6723 g_return_if_fail (focus_column != NULL);
6725 tree_view->priv->focus_column = focus_column;
6726 if (!focus_column->button)
6728 pspp_sheet_view_column_set_need_button (focus_column, TRUE);
6729 // g_return_if_fail (focus_column->button != NULL);
6730 if (focus_column->button == NULL)
6734 if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view)) != focus_column->button)
6735 gtk_widget_grab_focus (focus_column->button);
6737 if (clamp_column_visible)
6738 pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6741 /* Returns TRUE if the focus is within the headers, after the focus operation is
6745 pspp_sheet_view_header_focus (PsppSheetView *tree_view,
6746 GtkDirectionType dir,
6747 gboolean clamp_column_visible)
6749 GtkWidget *focus_child;
6750 PsppSheetViewColumn *focus_column;
6751 GList *last_column, *first_column;
6755 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6758 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
6760 first_column = tree_view->priv->columns;
6761 while (first_column)
6763 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6765 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6767 first_column = first_column->next;
6770 /* No headers are visible, or are focusable. We can't focus in or out.
6772 if (first_column == NULL)
6775 last_column = g_list_last (tree_view->priv->columns);
6778 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6780 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6782 last_column = last_column->prev;
6786 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6790 case GTK_DIR_TAB_BACKWARD:
6791 case GTK_DIR_TAB_FORWARD:
6794 if (focus_child == NULL)
6796 if (tree_view->priv->focus_column != NULL &&
6797 pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6798 focus_column = tree_view->priv->focus_column;
6800 focus_column = first_column->data;
6801 pspp_sheet_view_focus_column (tree_view, focus_column,
6802 clamp_column_visible);
6809 if (focus_child == NULL)
6811 if (tree_view->priv->focus_column != NULL)
6812 focus_column = tree_view->priv->focus_column;
6813 else if (dir == GTK_DIR_LEFT)
6814 focus_column = last_column->data;
6816 focus_column = first_column->data;
6817 pspp_sheet_view_focus_column (tree_view, focus_column,
6818 clamp_column_visible);
6822 if (gtk_widget_child_focus (focus_child, dir))
6824 /* The focus moves inside the button. */
6825 /* This is probably a great example of bad UI */
6826 if (clamp_column_visible)
6827 pspp_sheet_view_clamp_column_visible (tree_view,
6828 tree_view->priv->focus_column,
6833 /* We need to move the focus among the row of buttons. */
6834 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6835 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6838 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6839 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6841 gtk_widget_error_bell (GTK_WIDGET (tree_view));
6847 PsppSheetViewColumn *column;
6849 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6850 tmp_list = tmp_list->next;
6852 tmp_list = tmp_list->prev;
6854 if (tmp_list == NULL)
6856 g_warning ("Internal button not found");
6859 column = tmp_list->data;
6860 if (column->visible &&
6861 pspp_sheet_view_column_can_focus (column))
6863 pspp_sheet_view_column_set_need_button (column, TRUE);
6866 pspp_sheet_view_focus_column (tree_view, column,
6867 clamp_column_visible);
6875 g_assert_not_reached ();
6882 /* This function returns in 'path' the first focusable path, if the given path
6883 * is already focusable, it's the returned one.
6887 search_first_focusable_path (PsppSheetView *tree_view,
6889 gboolean search_forward,
6892 /* XXX this function is trivial given that the sheetview doesn't support
6896 if (!path || !*path)
6899 _pspp_sheet_view_find_node (tree_view, *path, &node);
6907 return (*path != NULL);
6911 pspp_sheet_view_focus (GtkWidget *widget,
6912 GtkDirectionType direction)
6914 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6915 GtkContainer *container = GTK_CONTAINER (widget);
6916 GtkWidget *focus_child;
6918 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6921 focus_child = gtk_container_get_focus_child (container);
6923 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6924 /* Case 1. Headers currently have focus. */
6931 pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6933 case GTK_DIR_TAB_BACKWARD:
6936 case GTK_DIR_TAB_FORWARD:
6938 gtk_widget_grab_focus (widget);
6941 g_assert_not_reached ();
6946 /* Case 2. We don't have focus at all. */
6947 if (!gtk_widget_has_focus (widget))
6949 if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6950 gtk_widget_grab_focus (widget);
6954 /* Case 3. We have focus already. */
6955 if (direction == GTK_DIR_TAB_BACKWARD)
6956 return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6957 else if (direction == GTK_DIR_TAB_FORWARD)
6960 /* Other directions caught by the keybindings */
6961 gtk_widget_grab_focus (widget);
6966 pspp_sheet_view_grab_focus (GtkWidget *widget)
6968 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6970 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6974 pspp_sheet_view_style_set (GtkWidget *widget,
6975 GtkStyle *previous_style)
6977 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6979 PsppSheetViewColumn *column;
6981 if (gtk_widget_get_realized (widget))
6983 gdk_window_set_background (tree_view->priv->bin_window, >k_widget_get_style (widget)->base[gtk_widget_get_state (widget)]);
6984 gtk_style_set_background (gtk_widget_get_style (widget), tree_view->priv->header_window, GTK_STATE_NORMAL);
6985 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6988 gtk_widget_style_get (widget,
6989 "expander-size", &tree_view->priv->expander_size,
6991 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6993 for (list = tree_view->priv->columns; list; list = list->next)
6995 column = list->data;
6996 _pspp_sheet_view_column_cell_set_dirty (column);
6999 tree_view->priv->fixed_height = -1;
7001 /* Invalidate cached button style. */
7002 if (tree_view->priv->button_style)
7004 g_object_unref (tree_view->priv->button_style);
7005 tree_view->priv->button_style = NULL;
7008 gtk_widget_queue_resize (widget);
7013 pspp_sheet_view_set_focus_child (GtkContainer *container,
7016 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
7019 for (list = tree_view->priv->columns; list; list = list->next)
7021 if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
7023 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7028 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
7032 pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
7033 GtkAdjustment *hadj,
7034 GtkAdjustment *vadj)
7036 gboolean need_adjust = FALSE;
7038 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7041 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
7043 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7045 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
7047 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7049 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
7051 g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
7052 pspp_sheet_view_adjustment_changed,
7054 g_object_unref (tree_view->priv->hadjustment);
7057 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
7059 g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
7060 pspp_sheet_view_adjustment_changed,
7062 g_object_unref (tree_view->priv->vadjustment);
7065 if (tree_view->priv->hadjustment != hadj)
7067 tree_view->priv->hadjustment = hadj;
7068 g_object_ref_sink (tree_view->priv->hadjustment);
7070 g_signal_connect (tree_view->priv->hadjustment, "value-changed",
7071 G_CALLBACK (pspp_sheet_view_adjustment_changed),
7076 if (tree_view->priv->vadjustment != vadj)
7078 tree_view->priv->vadjustment = vadj;
7079 g_object_ref_sink (tree_view->priv->vadjustment);
7081 g_signal_connect (tree_view->priv->vadjustment, "value-changed",
7082 G_CALLBACK (pspp_sheet_view_adjustment_changed),
7088 pspp_sheet_view_adjustment_changed (NULL, tree_view);
7093 pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
7094 GtkMovementStep step,
7097 PsppSheetSelectMode mode;
7098 GdkModifierType state;
7100 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
7101 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
7102 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
7103 step == GTK_MOVEMENT_DISPLAY_LINES ||
7104 step == GTK_MOVEMENT_PAGES ||
7105 step == GTK_MOVEMENT_BUFFER_ENDS ||
7106 step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
7108 if (tree_view->priv->row_count == 0)
7110 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7113 pspp_sheet_view_stop_editing (tree_view, FALSE);
7114 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7115 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7118 if (gtk_get_current_event_state (&state))
7120 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7121 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
7122 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
7123 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
7125 /* else we assume not pressed */
7129 case GTK_MOVEMENT_LOGICAL_POSITIONS:
7130 pspp_sheet_view_move_cursor_tab (tree_view, count);
7132 case GTK_MOVEMENT_VISUAL_POSITIONS:
7133 pspp_sheet_view_move_cursor_left_right (tree_view, count, mode);
7135 case GTK_MOVEMENT_DISPLAY_LINES:
7136 pspp_sheet_view_move_cursor_up_down (tree_view, count, mode);
7138 case GTK_MOVEMENT_PAGES:
7139 pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode);
7141 case GTK_MOVEMENT_BUFFER_ENDS:
7142 pspp_sheet_view_move_cursor_start_end (tree_view, count, mode);
7144 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7145 pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode);
7148 g_assert_not_reached ();
7155 pspp_sheet_view_put (PsppSheetView *tree_view,
7156 GtkWidget *child_widget,
7157 /* in bin_window coordinates */
7163 PsppSheetViewChild *child;
7165 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7166 g_return_if_fail (GTK_IS_WIDGET (child_widget));
7168 child = g_slice_new (PsppSheetViewChild);
7170 child->widget = child_widget;
7173 child->width = width;
7174 child->height = height;
7176 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7178 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7179 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7181 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7185 _pspp_sheet_view_child_move_resize (PsppSheetView *tree_view,
7187 /* in tree coordinates */
7193 PsppSheetViewChild *child = NULL;
7195 GdkRectangle allocation;
7197 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7198 g_return_if_fail (GTK_IS_WIDGET (widget));
7200 for (list = tree_view->priv->children; list; list = list->next)
7202 if (((PsppSheetViewChild *)list->data)->widget == widget)
7211 allocation.x = child->x = x;
7212 allocation.y = child->y = y;
7213 allocation.width = child->width = width;
7214 allocation.height = child->height = height;
7216 if (gtk_widget_get_realized (widget))
7217 gtk_widget_size_allocate (widget, &allocation);
7221 /* TreeModel Callbacks
7225 pspp_sheet_view_row_changed (GtkTreeModel *model,
7230 PsppSheetView *tree_view = (PsppSheetView *)data;
7232 gboolean free_path = FALSE;
7233 GtkTreePath *cursor_path;
7235 g_return_if_fail (path != NULL || iter != NULL);
7237 if (tree_view->priv->cursor != NULL)
7238 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7242 if (tree_view->priv->edited_column &&
7243 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7244 pspp_sheet_view_stop_editing (tree_view, TRUE);
7246 if (cursor_path != NULL)
7247 gtk_tree_path_free (cursor_path);
7251 path = gtk_tree_model_get_path (model, iter);
7254 else if (iter == NULL)
7255 gtk_tree_model_get_iter (model, iter, path);
7257 _pspp_sheet_view_find_node (tree_view,
7263 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7264 pspp_sheet_view_node_queue_redraw (tree_view, node);
7268 gtk_tree_path_free (path);
7272 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7277 PsppSheetView *tree_view = (PsppSheetView *) data;
7280 gint height = tree_view->priv->fixed_height;
7281 gboolean free_path = FALSE;
7282 gboolean node_visible = TRUE;
7284 g_return_if_fail (path != NULL || iter != NULL);
7288 path = gtk_tree_model_get_path (model, iter);
7291 else if (iter == NULL)
7292 gtk_tree_model_get_iter (model, iter, path);
7294 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7296 /* Update all row-references */
7297 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7298 indices = gtk_tree_path_get_indices (path);
7299 tmpnode = indices[0];
7301 range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7305 if (node_visible && node_is_visible (tree_view, tmpnode))
7306 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7308 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7311 install_presize_handler (tree_view);
7313 gtk_tree_path_free (path);
7317 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7321 PsppSheetView *tree_view = (PsppSheetView *)data;
7324 g_return_if_fail (path != NULL);
7326 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7328 _pspp_sheet_view_find_node (tree_view, path, &node);
7333 range_tower_delete (tree_view->priv->selected, node, 1);
7335 /* Ensure we don't have a dangling pointer to a dead node */
7336 ensure_unprelighted (tree_view);
7338 /* Cancel editting if we've started */
7339 pspp_sheet_view_stop_editing (tree_view, TRUE);
7341 if (tree_view->priv->destroy_count_func)
7343 gint child_count = 0;
7344 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7347 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7349 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7351 gtk_tree_row_reference_free (tree_view->priv->top_row);
7352 tree_view->priv->top_row = NULL;
7355 install_scroll_sync_handler (tree_view);
7357 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7360 if (helper_data.changed)
7361 g_signal_emit_by_name (tree_view->priv->selection, "changed");
7366 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7367 GtkTreePath *parent,
7372 PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7375 /* XXX need to adjust selection */
7376 len = gtk_tree_model_iter_n_children (model, iter);
7381 gtk_tree_row_reference_reordered (G_OBJECT (data),
7386 if (gtk_tree_path_get_depth (parent) != 0)
7389 if (tree_view->priv->edited_column)
7390 pspp_sheet_view_stop_editing (tree_view, TRUE);
7392 /* we need to be unprelighted */
7393 ensure_unprelighted (tree_view);
7395 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7397 pspp_sheet_view_dy_to_top_row (tree_view);
7401 /* Internal tree functions
7406 pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view,
7407 PsppSheetViewColumn *column,
7411 PsppSheetViewColumn *tmp_column = NULL;
7422 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7425 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7427 list = (rtl ? list->prev : list->next))
7429 tmp_column = list->data;
7431 if (tmp_column == column)
7434 if (tmp_column->visible)
7435 total_width += tmp_column->width;
7438 if (tmp_column != column)
7440 g_warning (G_STRLOC": passed-in column isn't in the tree");
7449 if (column->visible)
7450 *x2 = total_width + column->width;
7452 *x2 = total_width; /* width of 0 */
7456 /* Make sure the node is visible vertically */
7458 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7461 gint node_dy, height;
7462 GtkTreePath *path = NULL;
7464 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7467 /* just return if the node is visible, avoiding a costly expose */
7468 node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7469 height = ROW_HEIGHT (tree_view);
7470 if (node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment)
7471 && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
7472 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
7475 path = _pspp_sheet_view_find_path (tree_view, node);
7478 /* We process updates because we want to clear old selected items when we scroll.
7479 * if this is removed, we get a "selection streak" at the bottom. */
7480 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7481 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7482 gtk_tree_path_free (path);
7487 pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
7488 PsppSheetViewColumn *column,
7489 gboolean focus_to_cell)
7496 x = column->allocation.x;
7497 width = column->allocation.width;
7499 if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7501 /* The column is larger than the horizontal page size. If the
7502 * column has cells which can be focussed individually, then we make
7503 * sure the cell which gets focus is fully visible (if even the
7504 * focus cell is bigger than the page size, we make sure the
7505 * left-hand side of the cell is visible).
7507 * If the column does not have those so-called special cells, we
7508 * make sure the left-hand side of the column is visible.
7511 if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7513 GtkTreePath *cursor_path;
7514 GdkRectangle background_area, cell_area, focus_area;
7516 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7518 pspp_sheet_view_get_cell_area (tree_view,
7519 cursor_path, column, &cell_area);
7520 pspp_sheet_view_get_background_area (tree_view,
7521 cursor_path, column,
7524 gtk_tree_path_free (cursor_path);
7526 _pspp_sheet_view_column_get_focus_area (column,
7532 width = focus_area.width;
7534 if (width < gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7536 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7537 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7538 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7539 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7540 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7544 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7546 gtk_adjustment_get_lower (tree_view->priv->hadjustment),
7547 gtk_adjustment_get_upper (tree_view->priv->hadjustment)
7548 - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)));
7552 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7553 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7554 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7555 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7556 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7561 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7566 path = gtk_tree_path_new ();
7568 gtk_tree_path_append_index (path, node);
7573 _pspp_sheet_view_find_node (PsppSheetView *tree_view,
7577 gint *indices = gtk_tree_path_get_indices (path);
7578 gint depth = gtk_tree_path_get_depth (path);
7581 if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7587 pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
7590 gboolean add_shifted_binding,
7591 GtkMovementStep step,
7595 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7600 if (add_shifted_binding)
7601 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7606 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7609 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7614 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7621 pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view,
7622 PsppSheetViewColumn *column)
7624 PsppSheetViewColumn *left_column;
7625 PsppSheetViewColumn *cur_column = NULL;
7626 PsppSheetViewColumnReorder *reorder;
7631 /* We want to precalculate the motion list such that we know what column slots
7635 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7637 /* First, identify all possible drop spots */
7639 tmp_list = g_list_last (tree_view->priv->columns);
7641 tmp_list = g_list_first (tree_view->priv->columns);
7645 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7646 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7648 if (cur_column->visible == FALSE)
7651 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7652 if (left_column != column && cur_column != column &&
7653 tree_view->priv->column_drop_func &&
7654 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7656 left_column = cur_column;
7659 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7660 reorder->left_column = left_column;
7661 left_column = reorder->right_column = cur_column;
7663 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7666 /* Add the last one */
7667 if (tree_view->priv->column_drop_func == NULL ||
7668 ((left_column != column) &&
7669 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7671 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7672 reorder->left_column = left_column;
7673 reorder->right_column = NULL;
7674 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7677 /* We quickly check to see if it even makes sense to reorder columns. */
7678 /* If there is nothing that can be moved, then we return */
7680 if (tree_view->priv->column_drag_info == NULL)
7683 /* We know there are always 2 slots possbile, as you can always return column. */
7684 /* If that's all there is, return */
7685 if (tree_view->priv->column_drag_info->next == NULL ||
7686 (tree_view->priv->column_drag_info->next->next == NULL &&
7687 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7688 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7690 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7691 g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7692 g_list_free (tree_view->priv->column_drag_info);
7693 tree_view->priv->column_drag_info = NULL;
7696 /* We fill in the ranges for the columns, now that we've isolated them */
7697 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7699 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7701 reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7703 reorder->left_align = left;
7704 if (tmp_list->next != NULL)
7706 g_assert (tmp_list->next->data);
7707 left = reorder->right_align = (reorder->right_column->allocation.x +
7708 reorder->right_column->allocation.width +
7709 ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7713 gint width = gdk_window_get_width (tree_view->priv->header_window);
7714 reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7720 _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view,
7721 PsppSheetViewColumn *column)
7723 GdkEvent *send_event;
7724 GtkAllocation allocation;
7726 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7727 GdkDisplay *display = gdk_screen_get_display (screen);
7729 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7730 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7731 g_return_if_fail (column->button);
7733 pspp_sheet_view_set_column_drag_info (tree_view, column);
7735 if (tree_view->priv->column_drag_info == NULL)
7738 if (tree_view->priv->drag_window == NULL)
7740 GdkWindowAttr attributes;
7741 guint attributes_mask;
7743 attributes.window_type = GDK_WINDOW_CHILD;
7744 attributes.wclass = GDK_INPUT_OUTPUT;
7745 attributes.x = column->allocation.x;
7747 attributes.width = column->allocation.width;
7748 attributes.height = column->allocation.height;
7749 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7750 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7751 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL ;
7753 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7756 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7759 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7760 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7762 gtk_grab_remove (column->button);
7764 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7765 send_event->crossing.send_event = TRUE;
7766 send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button)));
7767 send_event->crossing.subwindow = NULL;
7768 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7769 send_event->crossing.time = GDK_CURRENT_TIME;
7771 gtk_propagate_event (column->button, send_event);
7772 gdk_event_free (send_event);
7774 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7775 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7776 send_event->button.send_event = TRUE;
7777 send_event->button.time = GDK_CURRENT_TIME;
7778 send_event->button.x = -1;
7779 send_event->button.y = -1;
7780 send_event->button.axes = NULL;
7781 send_event->button.state = 0;
7782 send_event->button.button = 1;
7783 send_event->button.device =
7784 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
7786 send_event->button.x_root = 0;
7787 send_event->button.y_root = 0;
7789 gtk_propagate_event (column->button, send_event);
7790 gdk_event_free (send_event);
7792 /* Kids, don't try this at home */
7793 g_object_ref (column->button);
7794 gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7795 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7796 gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7797 g_object_unref (column->button);
7799 tree_view->priv->drag_column_x = column->allocation.x;
7800 allocation = column->allocation;
7802 gtk_widget_size_allocate (column->button, &allocation);
7803 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7805 tree_view->priv->drag_column = column;
7806 gdk_window_show (tree_view->priv->drag_window);
7808 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7810 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7811 while (gtk_events_pending ())
7812 gtk_main_iteration ();
7814 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7815 gdk_pointer_grab (tree_view->priv->drag_window,
7817 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7818 NULL, NULL, GDK_CURRENT_TIME);
7819 gdk_keyboard_grab (tree_view->priv->drag_window,
7825 _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view,
7827 const GdkRectangle *clip_rect)
7830 GtkAllocation allocation;
7832 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7835 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
7837 rect.width = MAX (tree_view->priv->width, allocation.width);
7839 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7840 rect.height = ROW_HEIGHT (tree_view);
7844 GdkRectangle new_rect;
7846 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7848 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7852 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7857 pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
7859 const GdkRectangle *clip_rect)
7863 _pspp_sheet_view_find_node (tree_view, path, &node);
7866 _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7870 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7873 GtkTreePath *cursor_path;
7875 if ((tree_view->priv->row_count == 0) ||
7876 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7880 if (tree_view->priv->cursor)
7881 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7883 if (cursor_path == NULL)
7885 /* There's no cursor. Move the cursor to the first selected row, if any
7886 * are selected, otherwise to the first row in the sheetview.
7888 GList *selected_rows;
7889 GtkTreeModel *model;
7890 PsppSheetSelection *selection;
7892 selection = pspp_sheet_view_get_selection (tree_view);
7893 selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7897 /* XXX we could avoid doing O(n) work to get this result */
7898 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7899 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7900 g_list_free (selected_rows);
7904 cursor_path = gtk_tree_path_new_first ();
7905 search_first_focusable_path (tree_view, &cursor_path,
7909 gtk_tree_row_reference_free (tree_view->priv->cursor);
7910 tree_view->priv->cursor = NULL;
7914 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7915 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7916 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0);
7918 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0);
7924 /* Now find a column for the cursor. */
7925 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7927 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7928 gtk_tree_path_free (cursor_path);
7930 if (tree_view->priv->focus_column == NULL)
7933 for (list = tree_view->priv->columns; list; list = list->next)
7935 if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7937 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7938 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7939 pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7949 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7951 PsppSheetSelectMode mode)
7953 gint selection_count;
7954 int cursor_node = -1;
7955 int new_cursor_node = -1;
7956 GtkTreePath *cursor_path = NULL;
7957 gboolean grab_focus = TRUE;
7959 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7963 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7964 /* FIXME: we lost the cursor; should we get the first? */
7967 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7968 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7970 if (cursor_node < 0)
7971 /* FIXME: we lost the cursor; should we get the first? */
7974 selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7976 if (selection_count == 0
7977 && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7978 && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
7980 /* Don't move the cursor, but just select the current node */
7981 new_cursor_node = cursor_node;
7986 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7988 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7991 gtk_tree_path_free (cursor_path);
7993 if (new_cursor_node)
7995 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7997 search_first_focusable_path (tree_view, &cursor_path,
8002 gtk_tree_path_free (cursor_path);
8006 * If the list has only one item and multi-selection is set then select
8007 * the row (if not yet selected).
8009 if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
8010 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
8011 new_cursor_node < 0)
8014 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
8016 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
8018 if (new_cursor_node < 0
8019 && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
8021 new_cursor_node = cursor_node;
8025 new_cursor_node = -1;
8029 if (new_cursor_node >= 0)
8031 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
8032 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode);
8033 gtk_tree_path_free (cursor_path);
8037 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8039 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8041 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
8043 GTK_DIR_UP : GTK_DIR_DOWN))
8045 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8048 gtk_widget_child_focus (toplevel,
8050 GTK_DIR_TAB_BACKWARD :
8051 GTK_DIR_TAB_FORWARD);
8058 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8063 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8065 return new_cursor_node >= 0;
8069 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
8071 PsppSheetSelectMode mode)
8073 int cursor_node = -1;
8074 GtkTreePath *old_cursor_path = NULL;
8075 GtkTreePath *cursor_path = NULL;
8076 int start_cursor_node = -1;
8079 gint vertical_separator;
8081 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8084 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8085 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8087 /* This is sorta weird. Focus in should give us a cursor */
8090 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
8091 _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
8093 if (cursor_node < 0)
8095 /* FIXME: we lost the cursor. Should we try to get one? */
8096 gtk_tree_path_free (old_cursor_path);
8100 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8101 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
8102 y += tree_view->priv->cursor_offset;
8103 y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment);
8104 y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment), (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator);
8106 if (y >= tree_view->priv->height)
8107 y = tree_view->priv->height - 1;
8109 tree_view->priv->cursor_offset =
8110 pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
8112 if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
8114 cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
8115 tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
8118 y -= tree_view->priv->cursor_offset;
8119 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8121 start_cursor_node = cursor_node;
8123 if (! search_first_focusable_path (tree_view, &cursor_path,
8127 /* It looks like we reached the end of the view without finding
8128 * a focusable row. We will step backwards to find the last
8131 cursor_node = start_cursor_node;
8132 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8134 search_first_focusable_path (tree_view, &cursor_path,
8143 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8145 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode);
8148 pspp_sheet_view_scroll_to_point (tree_view, -1, y);
8149 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8150 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8152 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
8153 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8155 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8158 gtk_tree_path_free (old_cursor_path);
8159 gtk_tree_path_free (cursor_path);
8163 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8165 PsppSheetSelectMode mode)
8167 int cursor_node = -1;
8168 GtkTreePath *cursor_path = NULL;
8169 PsppSheetViewColumn *column;
8172 gboolean found_column = FALSE;
8175 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8177 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8180 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8181 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8185 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8186 if (cursor_node < 0)
8188 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8190 gtk_tree_path_free (cursor_path);
8193 gtk_tree_path_free (cursor_path);
8195 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8196 if (tree_view->priv->focus_column)
8198 for (; list; list = (rtl ? list->prev : list->next))
8200 if (list->data == tree_view->priv->focus_column)
8207 gboolean left, right;
8209 column = list->data;
8210 if (column->visible == FALSE || column->row_head)
8213 pspp_sheet_view_column_cell_set_cell_data (column,
8214 tree_view->priv->model,
8219 right = list->prev ? TRUE : FALSE;
8220 left = list->next ? TRUE : FALSE;
8224 left = list->prev ? TRUE : FALSE;
8225 right = list->next ? TRUE : FALSE;
8228 if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8230 tree_view->priv->focus_column = column;
8231 found_column = TRUE;
8236 list = rtl ? list->prev : list->next;
8238 list = rtl ? list->next : list->prev;
8243 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8244 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8245 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8249 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8252 pspp_sheet_view_clamp_column_visible (tree_view,
8253 tree_view->priv->focus_column, TRUE);
8257 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8259 PsppSheetSelectMode mode)
8261 int cursor_node = -1;
8262 GtkTreePath *cursor_path = NULL;
8263 PsppSheetViewColumn *column;
8264 PsppSheetViewColumn *found_column;
8269 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8271 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8274 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8275 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8279 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8280 if (cursor_node < 0)
8282 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8284 gtk_tree_path_free (cursor_path);
8287 gtk_tree_path_free (cursor_path);
8289 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8290 if (tree_view->priv->focus_column)
8292 for (; list; list = (rtl ? list->prev : list->next))
8294 if (list->data == tree_view->priv->focus_column)
8299 found_column = NULL;
8302 gboolean left, right;
8304 column = list->data;
8305 if (column->visible == FALSE || column->row_head)
8308 pspp_sheet_view_column_cell_set_cell_data (column,
8309 tree_view->priv->model,
8314 right = list->prev ? TRUE : FALSE;
8315 left = list->next ? TRUE : FALSE;
8319 left = list->prev ? TRUE : FALSE;
8320 right = list->next ? TRUE : FALSE;
8323 if (column->tabbable
8324 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8325 found_column = column;
8329 list = rtl ? list->prev : list->next;
8331 list = rtl ? list->next : list->prev;
8336 tree_view->priv->focus_column = found_column;
8337 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8338 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8339 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8342 pspp_sheet_view_clamp_column_visible (tree_view,
8343 tree_view->priv->focus_column, TRUE);
8347 try_move_cursor_tab (PsppSheetView *tree_view,
8348 gboolean start_at_focus_column,
8351 PsppSheetViewColumn *column;
8353 int cursor_node = -1;
8354 GtkTreePath *cursor_path = NULL;
8358 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8359 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8363 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8364 if (cursor_node < 0)
8366 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8368 gtk_tree_path_free (cursor_path);
8371 gtk_tree_path_free (cursor_path);
8373 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8374 if (start_at_focus_column)
8377 ? g_list_last (tree_view->priv->columns)
8378 : g_list_first (tree_view->priv->columns));
8379 if (tree_view->priv->focus_column)
8381 for (; list; list = (rtl ? list->prev : list->next))
8383 if (list->data == tree_view->priv->focus_column)
8390 list = (rtl ^ (count == 1)
8391 ? g_list_first (tree_view->priv->columns)
8392 : g_list_last (tree_view->priv->columns));
8397 gboolean left, right;
8399 column = list->data;
8400 if (column->visible == FALSE || !column->tabbable)
8403 pspp_sheet_view_column_cell_set_cell_data (column,
8404 tree_view->priv->model,
8409 right = list->prev ? TRUE : FALSE;
8410 left = list->next ? TRUE : FALSE;
8414 left = list->prev ? TRUE : FALSE;
8415 right = list->next ? TRUE : FALSE;
8418 if (column->tabbable
8419 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8421 tree_view->priv->focus_column = column;
8422 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8423 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8424 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8429 list = rtl ? list->prev : list->next;
8431 list = rtl ? list->next : list->prev;
8438 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8441 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8444 if (!try_move_cursor_tab (tree_view, TRUE, count))
8446 if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0)
8447 && !try_move_cursor_tab (tree_view, FALSE, count))
8448 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8451 pspp_sheet_view_clamp_column_visible (tree_view,
8452 tree_view->priv->focus_column, TRUE);
8456 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8458 PsppSheetSelectMode mode)
8462 GtkTreePath *old_path;
8464 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8467 g_return_if_fail (tree_view->priv->row_count > 0);
8469 pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8473 /* Now go forward to find the first focusable row. */
8474 path = _pspp_sheet_view_find_path (tree_view, 0);
8475 search_first_focusable_path (tree_view, &path,
8476 TRUE, &cursor_node);
8480 /* Now go backwards to find last focusable row. */
8481 path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8482 search_first_focusable_path (tree_view, &path,
8483 FALSE, &cursor_node);
8489 if (gtk_tree_path_compare (old_path, path))
8491 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
8492 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8496 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8500 gtk_tree_path_free (old_path);
8501 gtk_tree_path_free (path);
8505 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8507 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8510 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8511 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8514 pspp_sheet_selection_select_all (tree_view->priv->selection);
8520 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8522 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8525 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8526 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8529 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8535 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8536 gboolean start_editing,
8537 PsppSheetSelectMode mode)
8540 int cursor_node = -1;
8541 GtkTreePath *cursor_path = NULL;
8543 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8546 if (tree_view->priv->cursor)
8547 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8549 if (cursor_path == NULL)
8552 _pspp_sheet_view_find_node (tree_view, cursor_path,
8555 if (cursor_node < 0)
8557 gtk_tree_path_free (cursor_path);
8561 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing &&
8562 tree_view->priv->focus_column)
8564 if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8566 gtk_tree_path_free (cursor_path);
8571 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8577 /* We bail out if the original (tree, node) don't exist anymore after
8578 * handling the selection-changed callback. We do return TRUE because
8579 * the key press has been handled at this point.
8581 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8583 if (cursor_node != new_node)
8586 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8588 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8589 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8591 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8592 pspp_sheet_view_row_activated (tree_view, cursor_path,
8593 tree_view->priv->focus_column);
8595 gtk_tree_path_free (cursor_path);
8601 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8604 int cursor_node = -1;
8605 GtkTreePath *cursor_path = NULL;
8607 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8611 if (tree_view->priv->cursor)
8612 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8614 if (cursor_path == NULL)
8617 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8618 if (cursor_node < 0)
8620 gtk_tree_path_free (cursor_path);
8624 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8627 PSPP_SHEET_SELECT_MODE_TOGGLE,
8630 /* We bail out if the original (tree, node) don't exist anymore after
8631 * handling the selection-changed callback. We do return TRUE because
8632 * the key press has been handled at this point.
8634 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8636 if (cursor_node != new_node)
8639 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8641 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8642 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8643 gtk_tree_path_free (cursor_path);
8649 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8651 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8652 tree_view->priv->typeselect_flush_timeout = 0;
8657 /* Cut and paste from gtkwindow.c */
8659 send_focus_change (GtkWidget *widget,
8662 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8664 fevent->focus_change.type = GDK_FOCUS_CHANGE;
8665 fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
8666 fevent->focus_change.in = in;
8668 gtk_widget_send_focus_change (widget, fevent);
8669 gdk_event_free (fevent);
8673 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8675 GtkWidget *frame, *vbox, *toplevel;
8678 if (tree_view->priv->search_custom_entry_set)
8681 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8682 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8684 if (tree_view->priv->search_window != NULL)
8686 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8687 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8688 GTK_WINDOW (tree_view->priv->search_window));
8689 else if (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)))
8690 gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
8691 GTK_WINDOW (tree_view->priv->search_window));
8692 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8696 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8697 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8699 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8700 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8701 GTK_WINDOW (tree_view->priv->search_window));
8703 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8704 GDK_WINDOW_TYPE_HINT_UTILITY);
8705 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8706 g_signal_connect (tree_view->priv->search_window, "delete-event",
8707 G_CALLBACK (pspp_sheet_view_search_delete_event),
8709 g_signal_connect (tree_view->priv->search_window, "key-press-event",
8710 G_CALLBACK (pspp_sheet_view_search_key_press_event),
8712 g_signal_connect (tree_view->priv->search_window, "button-press-event",
8713 G_CALLBACK (pspp_sheet_view_search_button_press_event),
8715 g_signal_connect (tree_view->priv->search_window, "scroll-event",
8716 G_CALLBACK (pspp_sheet_view_search_scroll_event),
8719 frame = gtk_frame_new (NULL);
8720 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8721 gtk_widget_show (frame);
8722 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8724 vbox = gtk_vbox_new (FALSE, 0);
8725 gtk_widget_show (vbox);
8726 gtk_container_add (GTK_CONTAINER (frame), vbox);
8727 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8730 tree_view->priv->search_entry = gtk_entry_new ();
8731 gtk_widget_show (tree_view->priv->search_entry);
8732 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8733 G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8735 g_signal_connect (tree_view->priv->search_entry,
8736 "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8740 g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8742 G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8746 gtk_container_add (GTK_CONTAINER (vbox),
8747 tree_view->priv->search_entry);
8749 gtk_widget_realize (tree_view->priv->search_entry);
8752 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8753 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8756 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8757 gboolean keybinding)
8759 /* We only start interactive search if we have focus or the columns
8760 * have focus. If one of our children have focus, we don't want to
8764 gboolean found_focus = FALSE;
8765 GtkWidgetClass *entry_parent_class;
8767 if (!tree_view->priv->enable_search && !keybinding)
8770 if (tree_view->priv->search_custom_entry_set)
8773 if (tree_view->priv->search_window != NULL &&
8774 gtk_widget_get_visible (tree_view->priv->search_window))
8777 for (list = tree_view->priv->columns; list; list = list->next)
8779 PsppSheetViewColumn *column;
8781 column = list->data;
8782 if (! column->visible)
8785 if (column->button && gtk_widget_has_focus (column->button))
8792 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8798 if (tree_view->priv->search_column < 0)
8801 pspp_sheet_view_ensure_interactive_directory (tree_view);
8804 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8807 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8808 gtk_widget_show (tree_view->priv->search_window);
8809 if (tree_view->priv->search_entry_changed_id == 0)
8811 tree_view->priv->search_entry_changed_id =
8812 g_signal_connect (tree_view->priv->search_entry, "changed",
8813 G_CALLBACK (pspp_sheet_view_search_init),
8817 tree_view->priv->typeselect_flush_timeout =
8818 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8819 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8822 /* Grab focus will select all the text. We don't want that to happen, so we
8823 * call the parent instance and bypass the selection change. This is probably
8824 * really non-kosher. */
8825 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8826 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8828 /* send focus-in event */
8829 send_focus_change (tree_view->priv->search_entry, TRUE);
8831 /* search first matching iter */
8832 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8838 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8840 return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8843 /* this function returns the new width of the column being resized given
8844 * the column and x position of the cursor; the x cursor position is passed
8845 * in as a pointer and automagicly corrected if it's beyond min/max limits
8848 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8852 PsppSheetViewColumn *column;
8856 /* first translate the x position from gtk_widget_get_window (widget)
8857 * to clist->clist_window
8859 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8860 column = g_list_nth (tree_view->priv->columns, i)->data;
8861 width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8863 /* Clamp down the value */
8864 if (column->min_width == -1)
8865 width = MAX (column->button_request, width);
8867 width = MAX (column->min_width, width);
8868 if (column->max_width != -1)
8869 width = MIN (width, column->max_width);
8871 *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8877 /* FIXME this adjust_allocation is a big cut-and-paste from
8878 * GtkCList, needs to be some "official" way to do this
8888 /* The window to which gtk_widget_get_window (widget) is relative */
8889 #define ALLOCATION_WINDOW(widget) \
8890 (!gtk_widget_get_has_window (widget) ? \
8891 gtk_widget_get_window (widget) : \
8892 gdk_window_get_parent (gtk_widget_get_window (widget)))
8895 adjust_allocation_recurse (GtkWidget *widget,
8898 ScrollData *scroll_data = data;
8899 GtkAllocation allocation;
8900 gtk_widget_get_allocation (widget, &allocation);
8901 /* Need to really size allocate instead of just poking
8902 * into widget->allocation if the widget is not realized.
8903 * FIXME someone figure out why this was.
8905 if (!gtk_widget_get_realized (widget))
8907 if (gtk_widget_get_visible (widget))
8909 GdkRectangle tmp_rectangle = allocation;
8910 tmp_rectangle.x += scroll_data->dx;
8911 tmp_rectangle.y += scroll_data->dy;
8913 gtk_widget_size_allocate (widget, &tmp_rectangle);
8918 if (ALLOCATION_WINDOW (widget) == scroll_data->window)
8920 allocation.x += scroll_data->dx;
8921 allocation.y += scroll_data->dy;
8923 if (GTK_IS_CONTAINER (widget))
8924 gtk_container_forall (GTK_CONTAINER (widget),
8925 adjust_allocation_recurse,
8932 adjust_allocation (GtkWidget *widget,
8936 ScrollData scroll_data;
8938 if (gtk_widget_get_realized (widget))
8939 scroll_data.window = ALLOCATION_WINDOW (widget);
8941 scroll_data.window = NULL;
8943 scroll_data.dx = dx;
8944 scroll_data.dy = dy;
8946 adjust_allocation_recurse (widget, &scroll_data);
8950 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8954 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8955 PsppSheetView *tree_view)
8957 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8962 gdk_window_move (tree_view->priv->bin_window,
8963 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8964 TREE_VIEW_HEADER_HEIGHT (tree_view));
8965 gdk_window_move (tree_view->priv->header_window,
8966 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8968 dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8971 update_prelight (tree_view,
8972 tree_view->priv->event_last_x,
8973 tree_view->priv->event_last_y - dy);
8975 if (tree_view->priv->edited_column &&
8976 GTK_IS_WIDGET (tree_view->priv->edited_column->editable_widget))
8980 PsppSheetViewChild *child = NULL;
8982 widget = GTK_WIDGET (tree_view->priv->edited_column->editable_widget);
8983 adjust_allocation (widget, 0, dy);
8985 for (list = tree_view->priv->children; list; list = list->next)
8987 child = (PsppSheetViewChild *)list->data;
8988 if (child->widget == widget)
8996 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8998 if (tree_view->priv->dy != (int) gtk_adjustment_get_value (tree_view->priv->vadjustment))
9000 /* update our dy and top_row */
9001 tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
9003 if (!tree_view->priv->in_top_row_to_dy)
9004 pspp_sheet_view_dy_to_top_row (tree_view);
9007 for (list = tree_view->priv->columns; list; list = list->next)
9009 PsppSheetViewColumn *column = list->data;
9010 GtkAllocation *col_allocation = &column->allocation;
9011 GtkAllocation widget_allocation;
9012 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &widget_allocation);
9014 if (span_intersects (col_allocation->x, col_allocation->width,
9015 gtk_adjustment_get_value (tree_view->priv->hadjustment),
9016 widget_allocation.width))
9018 pspp_sheet_view_column_set_need_button (column, TRUE);
9019 if (!column->button)
9020 pspp_sheet_view_column_update_button (column);
9032 * pspp_sheet_view_new:
9034 * Creates a new #PsppSheetView widget.
9036 * Return value: A newly created #PsppSheetView widget.
9039 pspp_sheet_view_new (void)
9041 return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
9045 * pspp_sheet_view_new_with_model:
9046 * @model: the model.
9048 * Creates a new #PsppSheetView widget with the model initialized to @model.
9050 * Return value: A newly created #PsppSheetView widget.
9053 pspp_sheet_view_new_with_model (GtkTreeModel *model)
9055 return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
9062 * pspp_sheet_view_get_model:
9063 * @tree_view: a #PsppSheetView
9065 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
9068 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
9071 pspp_sheet_view_get_model (PsppSheetView *tree_view)
9073 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9075 return tree_view->priv->model;
9079 * pspp_sheet_view_set_model:
9080 * @tree_view: A #GtkTreeNode.
9081 * @model: (allow-none): The model.
9083 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
9084 * set, it will remove it before setting the new model. If @model is %NULL,
9085 * then it will unset the old model.
9088 pspp_sheet_view_set_model (PsppSheetView *tree_view,
9089 GtkTreeModel *model)
9091 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9092 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
9094 if (model == tree_view->priv->model)
9097 if (tree_view->priv->scroll_to_path)
9099 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9100 tree_view->priv->scroll_to_path = NULL;
9103 if (tree_view->priv->model)
9105 GList *tmplist = tree_view->priv->columns;
9107 if (tree_view->priv->selected)
9108 range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
9109 pspp_sheet_view_stop_editing (tree_view, TRUE);
9111 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9112 pspp_sheet_view_row_changed,
9114 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9115 pspp_sheet_view_row_inserted,
9117 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9118 pspp_sheet_view_row_deleted,
9120 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9121 pspp_sheet_view_rows_reordered,
9124 for (; tmplist; tmplist = tmplist->next)
9125 _pspp_sheet_view_column_unset_model (tmplist->data,
9126 tree_view->priv->model);
9128 tree_view->priv->prelight_node = -1;
9130 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
9131 tree_view->priv->drag_dest_row = NULL;
9132 gtk_tree_row_reference_free (tree_view->priv->cursor);
9133 tree_view->priv->cursor = NULL;
9134 gtk_tree_row_reference_free (tree_view->priv->anchor);
9135 tree_view->priv->anchor = NULL;
9136 gtk_tree_row_reference_free (tree_view->priv->top_row);
9137 tree_view->priv->top_row = NULL;
9138 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9139 tree_view->priv->scroll_to_path = NULL;
9141 tree_view->priv->scroll_to_column = NULL;
9143 g_object_unref (tree_view->priv->model);
9145 tree_view->priv->search_column = -1;
9146 tree_view->priv->fixed_height = -1;
9147 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
9148 tree_view->priv->last_button_x = -1;
9149 tree_view->priv->last_button_y = -1;
9152 tree_view->priv->model = model;
9154 if (tree_view->priv->model)
9158 if (tree_view->priv->search_column == -1)
9160 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
9162 GType type = gtk_tree_model_get_column_type (model, i);
9164 if (g_value_type_transformable (type, G_TYPE_STRING))
9166 tree_view->priv->search_column = i;
9172 g_object_ref (tree_view->priv->model);
9173 g_signal_connect (tree_view->priv->model,
9175 G_CALLBACK (pspp_sheet_view_row_changed),
9177 g_signal_connect (tree_view->priv->model,
9179 G_CALLBACK (pspp_sheet_view_row_inserted),
9181 g_signal_connect (tree_view->priv->model,
9183 G_CALLBACK (pspp_sheet_view_row_deleted),
9185 g_signal_connect (tree_view->priv->model,
9187 G_CALLBACK (pspp_sheet_view_rows_reordered),
9190 tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
9192 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
9193 install_presize_handler (tree_view);
9196 g_object_notify (G_OBJECT (tree_view), "model");
9198 if (tree_view->priv->selection)
9199 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
9201 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9202 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9206 * pspp_sheet_view_get_selection:
9207 * @tree_view: A #PsppSheetView.
9209 * Gets the #PsppSheetSelection associated with @tree_view.
9211 * Return value: A #PsppSheetSelection object.
9213 PsppSheetSelection *
9214 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
9216 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9218 return tree_view->priv->selection;
9222 * pspp_sheet_view_get_hadjustment:
9223 * @tree_view: A #PsppSheetView
9225 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
9227 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9231 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
9233 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9235 return pspp_sheet_view_do_get_hadjustment (tree_view);
9238 static GtkAdjustment *
9239 pspp_sheet_view_do_get_hadjustment (PsppSheetView *tree_view)
9241 return tree_view->priv->hadjustment;
9245 * pspp_sheet_view_set_hadjustment:
9246 * @tree_view: A #PsppSheetView
9247 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9249 * Sets the #GtkAdjustment for the current horizontal aspect.
9252 pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view,
9253 GtkAdjustment *adjustment)
9255 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9257 pspp_sheet_view_set_adjustments (tree_view,
9259 tree_view->priv->vadjustment);
9261 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9265 pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
9266 GtkAdjustment *adjustment)
9268 PsppSheetViewPrivate *priv = tree_view->priv;
9270 if (adjustment && priv->hadjustment == adjustment)
9273 if (priv->hadjustment != NULL)
9275 g_signal_handlers_disconnect_by_func (priv->hadjustment,
9276 pspp_sheet_view_adjustment_changed,
9278 g_object_unref (priv->hadjustment);
9281 if (adjustment == NULL)
9282 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9285 g_signal_connect (adjustment, "value-changed",
9286 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9287 priv->hadjustment = g_object_ref_sink (adjustment);
9288 /* FIXME: Adjustment should probably be populated here with fresh values, but
9289 * internal details are too complicated for me to decipher right now.
9291 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9293 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9297 * pspp_sheet_view_get_vadjustment:
9298 * @tree_view: A #PsppSheetView
9300 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9302 * Return value: (transfer none): A #GtkAdjustment object, or %NULL
9303 * if none is currently being used.
9305 * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
9308 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9310 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9312 return pspp_sheet_view_do_get_vadjustment (tree_view);
9315 static GtkAdjustment *
9316 pspp_sheet_view_do_get_vadjustment (PsppSheetView *tree_view)
9318 return tree_view->priv->vadjustment;
9322 * pspp_sheet_view_set_vadjustment:
9323 * @tree_view: A #PsppSheetView
9324 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9326 * Sets the #GtkAdjustment for the current vertical aspect.
9328 * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
9331 pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view,
9332 GtkAdjustment *adjustment)
9334 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9335 g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
9337 pspp_sheet_view_do_set_vadjustment (tree_view, adjustment);
9341 pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
9342 GtkAdjustment *adjustment)
9344 PsppSheetViewPrivate *priv = tree_view->priv;
9346 if (adjustment && priv->vadjustment == adjustment)
9349 if (priv->vadjustment != NULL)
9351 g_signal_handlers_disconnect_by_func (priv->vadjustment,
9352 pspp_sheet_view_adjustment_changed,
9354 g_object_unref (priv->vadjustment);
9357 if (adjustment == NULL)
9358 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9361 g_signal_connect (adjustment, "value-changed",
9362 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9363 priv->vadjustment = g_object_ref_sink (adjustment);
9364 /* FIXME: Adjustment should probably be populated here with fresh values, but
9365 * internal details are too complicated for me to decipher right now.
9367 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9368 g_object_notify (G_OBJECT (tree_view), "vadjustment");
9371 /* Column and header operations */
9374 * pspp_sheet_view_get_headers_visible:
9375 * @tree_view: A #PsppSheetView.
9377 * Returns %TRUE if the headers on the @tree_view are visible.
9379 * Return value: Whether the headers are visible or not.
9382 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9384 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9386 return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9390 * pspp_sheet_view_set_headers_visible:
9391 * @tree_view: A #PsppSheetView.
9392 * @headers_visible: %TRUE if the headers are visible
9394 * Sets the visibility state of the headers.
9397 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9398 gboolean headers_visible)
9402 PsppSheetViewColumn *column;
9403 GtkAllocation allocation;
9405 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9407 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9409 headers_visible = !! headers_visible;
9411 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9414 if (headers_visible)
9415 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9417 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9419 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9421 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9422 if (headers_visible)
9424 gdk_window_move_resize (tree_view->priv->bin_window, x, y + TREE_VIEW_HEADER_HEIGHT (tree_view),
9425 tree_view->priv->width, allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view));
9427 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9428 pspp_sheet_view_map_buttons (tree_view);
9432 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9434 for (list = tree_view->priv->columns; list; list = list->next)
9436 column = list->data;
9438 gtk_widget_unmap (column->button);
9440 gdk_window_hide (tree_view->priv->header_window);
9444 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view));
9445 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2);
9446 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
9447 gtk_adjustment_set_upper (tree_view->priv->vadjustment, tree_view->priv->height);
9448 gtk_adjustment_changed (tree_view->priv->vadjustment);
9450 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9452 g_object_notify (G_OBJECT (tree_view), "headers-visible");
9456 * pspp_sheet_view_columns_autosize:
9457 * @tree_view: A #PsppSheetView.
9459 * Resizes all columns to their optimal width. Only works after the
9460 * treeview has been realized.
9463 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9465 gboolean dirty = FALSE;
9467 PsppSheetViewColumn *column;
9469 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9471 for (list = tree_view->priv->columns; list; list = list->next)
9473 column = list->data;
9474 _pspp_sheet_view_column_cell_set_dirty (column);
9479 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9483 * pspp_sheet_view_set_headers_clickable:
9484 * @tree_view: A #PsppSheetView.
9485 * @setting: %TRUE if the columns are clickable.
9487 * Allow the column title buttons to be clicked.
9490 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9495 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9497 for (list = tree_view->priv->columns; list; list = list->next)
9498 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9500 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9505 * pspp_sheet_view_get_headers_clickable:
9506 * @tree_view: A #PsppSheetView.
9508 * Returns whether all header columns are clickable.
9510 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9515 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9519 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9521 for (list = tree_view->priv->columns; list; list = list->next)
9522 if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9529 * pspp_sheet_view_set_rules_hint
9530 * @tree_view: a #PsppSheetView
9531 * @setting: %TRUE if the tree requires reading across rows
9533 * This function tells GTK+ that the user interface for your
9534 * application requires users to read across tree rows and associate
9535 * cells with one another. By default, GTK+ will then render the tree
9536 * with alternating row colors. Do <emphasis>not</emphasis> use it
9537 * just because you prefer the appearance of the ruled tree; that's a
9538 * question for the theme. Some themes will draw tree rows in
9539 * alternating colors even when rules are turned off, and users who
9540 * prefer that appearance all the time can choose those themes. You
9541 * should call this function only as a <emphasis>semantic</emphasis>
9542 * hint to the theme engine that your tree makes alternating colors
9543 * useful from a functional standpoint (since it has lots of columns,
9548 pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view,
9551 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9553 setting = setting != FALSE;
9555 if (tree_view->priv->has_rules != setting)
9557 tree_view->priv->has_rules = setting;
9558 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9561 g_object_notify (G_OBJECT (tree_view), "rules-hint");
9565 * pspp_sheet_view_get_rules_hint
9566 * @tree_view: a #PsppSheetView
9568 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9570 * Return value: %TRUE if rules are useful for the user of this tree
9573 pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view)
9575 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9577 return tree_view->priv->has_rules;
9580 /* Public Column functions
9584 * pspp_sheet_view_append_column:
9585 * @tree_view: A #PsppSheetView.
9586 * @column: The #PsppSheetViewColumn to add.
9588 * Appends @column to the list of columns.
9590 * Return value: The number of columns in @tree_view after appending.
9593 pspp_sheet_view_append_column (PsppSheetView *tree_view,
9594 PsppSheetViewColumn *column)
9596 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9597 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9598 g_return_val_if_fail (column->tree_view == NULL, -1);
9600 return pspp_sheet_view_insert_column (tree_view, column, -1);
9605 * pspp_sheet_view_remove_column:
9606 * @tree_view: A #PsppSheetView.
9607 * @column: The #PsppSheetViewColumn to remove.
9609 * Removes @column from @tree_view.
9611 * Return value: The number of columns in @tree_view after removing.
9614 pspp_sheet_view_remove_column (PsppSheetView *tree_view,
9615 PsppSheetViewColumn *column)
9617 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9618 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9619 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9621 if (tree_view->priv->focus_column == column)
9622 tree_view->priv->focus_column = NULL;
9624 if (tree_view->priv->edited_column == column)
9626 pspp_sheet_view_stop_editing (tree_view, TRUE);
9628 /* no need to, but just to be sure ... */
9629 tree_view->priv->edited_column = NULL;
9632 _pspp_sheet_view_column_unset_tree_view (column);
9634 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9635 tree_view->priv->n_columns--;
9637 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9641 _pspp_sheet_view_column_unrealize_button (column);
9642 for (list = tree_view->priv->columns; list; list = list->next)
9644 PsppSheetViewColumn *tmp_column;
9646 tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9647 if (tmp_column->visible)
9648 _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9651 if (tree_view->priv->n_columns == 0 &&
9652 pspp_sheet_view_get_headers_visible (tree_view) &&
9653 tree_view->priv->header_window)
9654 gdk_window_hide (tree_view->priv->header_window);
9656 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9659 g_object_unref (column);
9660 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9662 return tree_view->priv->n_columns;
9666 * pspp_sheet_view_insert_column:
9667 * @tree_view: A #PsppSheetView.
9668 * @column: The #PsppSheetViewColumn to be inserted.
9669 * @position: The position to insert @column in.
9671 * This inserts the @column into the @tree_view at @position. If @position is
9672 * -1, then the column is inserted at the end.
9674 * Return value: The number of columns in @tree_view after insertion.
9677 pspp_sheet_view_insert_column (PsppSheetView *tree_view,
9678 PsppSheetViewColumn *column,
9681 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9682 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9683 g_return_val_if_fail (column->tree_view == NULL, -1);
9685 g_object_ref_sink (column);
9687 if (tree_view->priv->n_columns == 0 &&
9688 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9689 pspp_sheet_view_get_headers_visible (tree_view))
9691 gdk_window_show (tree_view->priv->header_window);
9694 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9696 tree_view->priv->n_columns++;
9698 _pspp_sheet_view_column_set_tree_view (column, tree_view);
9700 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9704 _pspp_sheet_view_column_realize_button (column);
9706 for (list = tree_view->priv->columns; list; list = list->next)
9708 column = PSPP_SHEET_VIEW_COLUMN (list->data);
9709 if (column->visible)
9710 _pspp_sheet_view_column_cell_set_dirty (column);
9712 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9715 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9717 return tree_view->priv->n_columns;
9721 * pspp_sheet_view_insert_column_with_attributes:
9722 * @tree_view: A #PsppSheetView
9723 * @position: The position to insert the new column in.
9724 * @title: The title to set the header to.
9725 * @cell: The #GtkCellRenderer.
9726 * @Varargs: A %NULL-terminated list of attributes.
9728 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9729 * @position. If @position is -1, then the newly created column is inserted at
9730 * the end. The column is initialized with the attributes given.
9732 * Return value: The number of columns in @tree_view after insertion.
9735 pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view,
9738 GtkCellRenderer *cell,
9741 PsppSheetViewColumn *column;
9746 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9748 column = pspp_sheet_view_column_new ();
9749 pspp_sheet_view_column_set_title (column, title);
9750 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9752 va_start (args, cell);
9754 attribute = va_arg (args, gchar *);
9756 while (attribute != NULL)
9758 column_id = va_arg (args, gint);
9759 pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9760 attribute = va_arg (args, gchar *);
9765 pspp_sheet_view_insert_column (tree_view, column, position);
9767 return tree_view->priv->n_columns;
9771 * pspp_sheet_view_insert_column_with_data_func:
9772 * @tree_view: a #PsppSheetView
9773 * @position: Position to insert, -1 for append
9774 * @title: column title
9775 * @cell: cell renderer for column
9776 * @func: function to set attributes of cell renderer
9777 * @data: data for @func
9778 * @dnotify: destroy notifier for @data
9780 * Convenience function that inserts a new column into the #PsppSheetView
9781 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9782 * attributes (normally using data from the model). See also
9783 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9785 * Return value: number of columns in the tree view post-insert
9788 pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view,
9791 GtkCellRenderer *cell,
9792 PsppSheetCellDataFunc func,
9794 GDestroyNotify dnotify)
9796 PsppSheetViewColumn *column;
9798 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9800 column = pspp_sheet_view_column_new ();
9801 pspp_sheet_view_column_set_title (column, title);
9802 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9803 pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9805 pspp_sheet_view_insert_column (tree_view, column, position);
9807 return tree_view->priv->n_columns;
9811 * pspp_sheet_view_get_column:
9812 * @tree_view: A #PsppSheetView.
9813 * @n: The position of the column, counting from 0.
9815 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9817 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9820 PsppSheetViewColumn *
9821 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9824 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9826 if (n < 0 || n >= tree_view->priv->n_columns)
9829 if (tree_view->priv->columns == NULL)
9832 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9836 * pspp_sheet_view_get_columns:
9837 * @tree_view: A #PsppSheetView
9839 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9840 * The returned list must be freed with g_list_free ().
9842 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9845 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9847 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9849 return g_list_copy (tree_view->priv->columns);
9853 * pspp_sheet_view_move_column_after:
9854 * @tree_view: A #PsppSheetView
9855 * @column: The #PsppSheetViewColumn to be moved.
9856 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9858 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9859 * @column is placed in the first position.
9862 pspp_sheet_view_move_column_after (PsppSheetView *tree_view,
9863 PsppSheetViewColumn *column,
9864 PsppSheetViewColumn *base_column)
9866 GList *column_list_el, *base_el = NULL;
9868 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9870 column_list_el = g_list_find (tree_view->priv->columns, column);
9871 g_return_if_fail (column_list_el != NULL);
9875 base_el = g_list_find (tree_view->priv->columns, base_column);
9876 g_return_if_fail (base_el != NULL);
9879 if (column_list_el->prev == base_el)
9882 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9883 if (base_el == NULL)
9885 column_list_el->prev = NULL;
9886 column_list_el->next = tree_view->priv->columns;
9887 if (column_list_el->next)
9888 column_list_el->next->prev = column_list_el;
9889 tree_view->priv->columns = column_list_el;
9893 column_list_el->prev = base_el;
9894 column_list_el->next = base_el->next;
9895 if (column_list_el->next)
9896 column_list_el->next->prev = column_list_el;
9897 base_el->next = column_list_el;
9900 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9902 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9903 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9906 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9910 * pspp_sheet_view_set_column_drag_function:
9911 * @tree_view: A #PsppSheetView.
9912 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9913 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9914 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9916 * Sets a user function for determining where a column may be dropped when
9917 * dragged. This function is called on every column pair in turn at the
9918 * beginning of a column drag to determine where a drop can take place. The
9919 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9920 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9921 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9922 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9923 * @tree_view reverts to the default behavior of allowing all columns to be
9924 * dropped everywhere.
9927 pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view,
9928 PsppSheetViewColumnDropFunc func,
9930 GDestroyNotify destroy)
9932 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9934 if (tree_view->priv->column_drop_func_data_destroy)
9935 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9937 tree_view->priv->column_drop_func = func;
9938 tree_view->priv->column_drop_func_data = user_data;
9939 tree_view->priv->column_drop_func_data_destroy = destroy;
9943 * pspp_sheet_view_scroll_to_point:
9944 * @tree_view: a #PsppSheetView
9945 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9946 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9948 * Scrolls the tree view such that the top-left corner of the visible
9949 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9950 * in tree coordinates. The @tree_view must be realized before
9951 * this function is called. If it isn't, you probably want to be
9952 * using pspp_sheet_view_scroll_to_cell().
9954 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9957 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9961 GtkAdjustment *hadj;
9962 GtkAdjustment *vadj;
9964 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9965 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9967 hadj = tree_view->priv->hadjustment;
9968 vadj = tree_view->priv->vadjustment;
9971 gtk_adjustment_set_value (hadj, CLAMP (tree_x, gtk_adjustment_get_lower (hadj), gtk_adjustment_get_upper (hadj) - gtk_adjustment_get_page_size (hadj)));
9973 gtk_adjustment_set_value (vadj, CLAMP (tree_y, gtk_adjustment_get_lower (vadj), gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj)));
9977 * pspp_sheet_view_scroll_to_cell:
9978 * @tree_view: A #PsppSheetView.
9979 * @path: (allow-none): The path of the row to move to, or %NULL.
9980 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9981 * @use_align: whether to use alignment arguments, or %FALSE.
9982 * @row_align: The vertical alignment of the row specified by @path.
9983 * @col_align: The horizontal alignment of the column specified by @column.
9985 * Moves the alignments of @tree_view to the position specified by @column and
9986 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9987 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9988 * or @path need to be non-%NULL. @row_align determines where the row is
9989 * placed, and @col_align determines where @column is placed. Both are expected
9990 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9991 * right/bottom alignment, 0.5 means center.
9993 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9994 * tree does the minimum amount of work to scroll the cell onto the screen.
9995 * This means that the cell will be scrolled to the edge closest to its current
9996 * position. If the cell is currently visible on the screen, nothing is done.
9998 * This function only works if the model is set, and @path is a valid row on the
9999 * model. If the model changes before the @tree_view is realized, the centered
10000 * path will be modified to reflect this change.
10003 pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view,
10005 PsppSheetViewColumn *column,
10006 gboolean use_align,
10010 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10011 g_return_if_fail (tree_view->priv->model != NULL);
10012 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
10013 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
10014 g_return_if_fail (path != NULL || column != NULL);
10017 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
10018 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
10020 row_align = CLAMP (row_align, 0.0, 1.0);
10021 col_align = CLAMP (col_align, 0.0, 1.0);
10024 /* Note: Despite the benefits that come from having one code path for the
10025 * scrolling code, we short-circuit validate_visible_area's immplementation as
10026 * it is much slower than just going to the point.
10028 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
10029 !gtk_widget_get_realized (GTK_WIDGET (tree_view))
10030 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
10032 if (tree_view->priv->scroll_to_path)
10033 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
10035 tree_view->priv->scroll_to_path = NULL;
10036 tree_view->priv->scroll_to_column = NULL;
10039 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
10041 tree_view->priv->scroll_to_column = column;
10042 tree_view->priv->scroll_to_use_align = use_align;
10043 tree_view->priv->scroll_to_row_align = row_align;
10044 tree_view->priv->scroll_to_col_align = col_align;
10046 install_presize_handler (tree_view);
10050 GdkRectangle cell_rect;
10051 GdkRectangle vis_rect;
10052 gint dest_x, dest_y;
10054 pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
10055 pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
10057 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
10059 dest_x = vis_rect.x;
10060 dest_y = vis_rect.y;
10066 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
10070 if (cell_rect.x < vis_rect.x)
10071 dest_x = cell_rect.x;
10072 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
10073 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
10081 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
10082 dest_y = MAX (dest_y, 0);
10086 if (cell_rect.y < vis_rect.y)
10087 dest_y = cell_rect.y;
10088 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
10089 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
10093 pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
10098 * pspp_sheet_view_row_activated:
10099 * @tree_view: A #PsppSheetView
10100 * @path: The #GtkTreePath to be activated.
10101 * @column: The #PsppSheetViewColumn to be activated.
10103 * Activates the cell determined by @path and @column.
10106 pspp_sheet_view_row_activated (PsppSheetView *tree_view,
10108 PsppSheetViewColumn *column)
10110 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10112 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
10117 * pspp_sheet_view_get_reorderable:
10118 * @tree_view: a #PsppSheetView
10120 * Retrieves whether the user can reorder the tree via drag-and-drop. See
10121 * pspp_sheet_view_set_reorderable().
10123 * Return value: %TRUE if the tree can be reordered.
10126 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
10128 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10130 return tree_view->priv->reorderable;
10134 * pspp_sheet_view_set_reorderable:
10135 * @tree_view: A #PsppSheetView.
10136 * @reorderable: %TRUE, if the tree can be reordered.
10138 * This function is a convenience function to allow you to reorder
10139 * models that support the #GtkDragSourceIface and the
10140 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
10141 * these. If @reorderable is %TRUE, then the user can reorder the
10142 * model by dragging and dropping rows. The developer can listen to
10143 * these changes by connecting to the model's row_inserted and
10144 * row_deleted signals. The reordering is implemented by setting up
10145 * the tree view as a drag source and destination. Therefore, drag and
10146 * drop can not be used in a reorderable view for any other purpose.
10148 * This function does not give you any degree of control over the order -- any
10149 * reordering is allowed. If more control is needed, you should probably
10150 * handle drag and drop manually.
10153 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
10154 gboolean reorderable)
10156 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10158 reorderable = reorderable != FALSE;
10160 if (tree_view->priv->reorderable == reorderable)
10165 const GtkTargetEntry row_targets[] = {
10166 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
10169 pspp_sheet_view_enable_model_drag_source (tree_view,
10172 G_N_ELEMENTS (row_targets),
10174 pspp_sheet_view_enable_model_drag_dest (tree_view,
10176 G_N_ELEMENTS (row_targets),
10181 pspp_sheet_view_unset_rows_drag_source (tree_view);
10182 pspp_sheet_view_unset_rows_drag_dest (tree_view);
10185 tree_view->priv->reorderable = reorderable;
10187 g_object_notify (G_OBJECT (tree_view), "reorderable");
10190 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
10191 is pressed, other rows will be unselected.
10193 If CLAMP_NODE is true, then the sheetview will scroll to make the row
10196 pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
10198 gboolean clear_and_select,
10199 gboolean clamp_node,
10200 PsppSheetSelectMode mode)
10204 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10206 GtkTreePath *cursor_path;
10207 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10208 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
10209 gtk_tree_path_free (cursor_path);
10212 gtk_tree_row_reference_free (tree_view->priv->cursor);
10213 tree_view->priv->cursor = NULL;
10215 _pspp_sheet_view_find_node (tree_view, path, &node);
10216 tree_view->priv->cursor =
10217 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
10218 tree_view->priv->model,
10221 if (tree_view->priv->row_count > 0)
10225 if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
10226 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
10230 /* We have to re-find tree and node here again, somebody might have
10231 * cleared the node or the whole tree in the PsppSheetSelection::changed
10232 * callback. If the nodes differ we bail out here.
10234 _pspp_sheet_view_find_node (tree_view, path, &new_node);
10236 if (node != new_node)
10241 pspp_sheet_view_clamp_node_visible (tree_view, node);
10242 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
10246 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
10250 * pspp_sheet_view_get_cursor:
10251 * @tree_view: A #PsppSheetView
10252 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
10253 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
10255 * Fills in @path and @focus_column with the current path and focus column. If
10256 * the cursor isn't currently set, then *@path will be %NULL. If no column
10257 * currently has focus, then *@focus_column will be %NULL.
10259 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
10260 * you are done with it.
10263 pspp_sheet_view_get_cursor (PsppSheetView *tree_view,
10264 GtkTreePath **path,
10265 PsppSheetViewColumn **focus_column)
10267 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10271 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10272 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10279 *focus_column = tree_view->priv->focus_column;
10284 * pspp_sheet_view_set_cursor:
10285 * @tree_view: A #PsppSheetView
10286 * @path: A #GtkTreePath
10287 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10288 * @start_editing: %TRUE if the specified cell should start being edited.
10290 * Sets the current keyboard focus to be at @path, and selects it. This is
10291 * useful when you want to focus the user's attention on a particular row. If
10292 * @focus_column is not %NULL, then focus is given to the column specified by
10293 * it. Additionally, if @focus_column is specified, and @start_editing is
10294 * %TRUE, then editing should be started in the specified cell.
10295 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10296 * in order to give keyboard focus to the widget. Please note that editing
10297 * can only happen when the widget is realized.
10299 * If @path is invalid for @model, the current cursor (if any) will be unset
10300 * and the function will return without failing.
10303 pspp_sheet_view_set_cursor (PsppSheetView *tree_view,
10305 PsppSheetViewColumn *focus_column,
10306 gboolean start_editing)
10308 pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10309 NULL, start_editing);
10313 * pspp_sheet_view_set_cursor_on_cell:
10314 * @tree_view: A #PsppSheetView
10315 * @path: A #GtkTreePath
10316 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10317 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10318 * @start_editing: %TRUE if the specified cell should start being edited.
10320 * Sets the current keyboard focus to be at @path, and selects it. This is
10321 * useful when you want to focus the user's attention on a particular row. If
10322 * @focus_column is not %NULL, then focus is given to the column specified by
10323 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10324 * contains 2 or more editable or activatable cells, then focus is given to
10325 * the cell specified by @focus_cell. Additionally, if @focus_column is
10326 * specified, and @start_editing is %TRUE, then editing should be started in
10327 * the specified cell. This function is often followed by
10328 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10329 * widget. Please note that editing can only happen when the widget is
10332 * If @path is invalid for @model, the current cursor (if any) will be unset
10333 * and the function will return without failing.
10338 pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view,
10340 PsppSheetViewColumn *focus_column,
10341 GtkCellRenderer *focus_cell,
10342 gboolean start_editing)
10344 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10345 g_return_if_fail (path != NULL);
10346 g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10348 if (!tree_view->priv->model)
10353 g_return_if_fail (focus_column);
10354 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10357 /* cancel the current editing, if it exists */
10358 if (tree_view->priv->edited_column &&
10359 tree_view->priv->edited_column->editable_widget)
10360 pspp_sheet_view_stop_editing (tree_view, TRUE);
10362 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
10364 if (focus_column && focus_column->visible)
10367 gboolean column_in_tree = FALSE;
10369 for (list = tree_view->priv->columns; list; list = list->next)
10370 if (list->data == focus_column)
10372 column_in_tree = TRUE;
10375 g_return_if_fail (column_in_tree);
10376 tree_view->priv->focus_column = focus_column;
10378 pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10380 pspp_sheet_view_start_editing (tree_view, path);
10382 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10383 pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10389 * pspp_sheet_view_get_bin_window:
10390 * @tree_view: A #PsppSheetView
10392 * Returns the window that @tree_view renders to. This is used primarily to
10393 * compare to <literal>event->window</literal> to confirm that the event on
10394 * @tree_view is on the right window.
10396 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10399 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10401 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10403 return tree_view->priv->bin_window;
10407 * pspp_sheet_view_get_path_at_pos:
10408 * @tree_view: A #PsppSheetView.
10409 * @x: The x position to be identified (relative to bin_window).
10410 * @y: The y position to be identified (relative to bin_window).
10411 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10412 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10413 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10414 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10416 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10417 * (please see pspp_sheet_view_get_bin_window()).
10418 * That is, @x and @y are relative to an events coordinates. @x and @y must
10419 * come from an event on the @tree_view only where <literal>event->window ==
10420 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10421 * things like popup menus. If @path is non-%NULL, then it will be filled
10422 * with the #GtkTreePath at that point. This path should be freed with
10423 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10424 * with the column at that point. @cell_x and @cell_y return the coordinates
10425 * relative to the cell background (i.e. the @background_area passed to
10426 * gtk_cell_renderer_render()). This function is only meaningful if
10427 * @tree_view is realized. Therefore this function will always return %FALSE
10428 * if @tree_view is not realized or does not have a model.
10430 * For converting widget coordinates (eg. the ones you get from
10431 * GtkWidget::query-tooltip), please see
10432 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10434 * Return value: %TRUE if a row exists at that coordinate.
10437 pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view,
10440 GtkTreePath **path,
10441 PsppSheetViewColumn **column,
10448 g_return_val_if_fail (tree_view != NULL, FALSE);
10455 if (tree_view->priv->bin_window == NULL)
10458 if (tree_view->priv->row_count == 0)
10461 if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment))
10464 if (x < 0 || y < 0)
10467 if (column || cell_x)
10469 PsppSheetViewColumn *tmp_column;
10470 PsppSheetViewColumn *last_column = NULL;
10472 gint remaining_x = x;
10473 gboolean found = FALSE;
10476 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10477 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10479 list = (rtl ? list->prev : list->next))
10481 tmp_column = list->data;
10483 if (tmp_column->visible == FALSE)
10486 last_column = tmp_column;
10487 if (remaining_x <= tmp_column->width)
10492 *column = tmp_column;
10495 *cell_x = remaining_x;
10499 remaining_x -= tmp_column->width;
10502 /* If found is FALSE and there is a last_column, then it the remainder
10503 * space is in that area
10510 *column = last_column;
10513 *cell_x = last_column->width + remaining_x;
10522 y_offset = pspp_sheet_view_find_offset (tree_view,
10523 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10530 *cell_y = y_offset;
10533 *path = _pspp_sheet_view_find_path (tree_view, node);
10538 /* Computes 'cell_area' from 'background_area', which must be the background
10539 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10540 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10541 the cell area as passed to _pspp_sheet_view_column_cell_render().
10543 'column' is required to properly adjust 'cell_area->x' and
10544 'cell_area->width'. It may be set to NULL if these values are not of
10545 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10548 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
10549 PsppSheetViewColumn *column,
10550 const GdkRectangle *background_area,
10551 gboolean subtract_focus_rect,
10552 GdkRectangle *cell_area)
10554 gint vertical_separator;
10555 gint horizontal_separator;
10557 *cell_area = *background_area;
10559 gtk_widget_style_get (GTK_WIDGET (tree_view),
10560 "vertical-separator", &vertical_separator,
10561 "horizontal-separator", &horizontal_separator,
10563 cell_area->x += horizontal_separator / 2;
10564 cell_area->y += vertical_separator / 2;
10565 cell_area->width -= horizontal_separator;
10566 cell_area->height -= vertical_separator;
10568 if (subtract_focus_rect)
10570 int focus_line_width;
10572 gtk_widget_style_get (GTK_WIDGET (tree_view),
10573 "focus-line-width", &focus_line_width,
10575 cell_area->x += focus_line_width;
10576 cell_area->y += focus_line_width;
10577 cell_area->width -= 2 * focus_line_width;
10578 cell_area->height -= 2 * focus_line_width;
10581 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10583 gint grid_line_width;
10584 gtk_widget_style_get (GTK_WIDGET (tree_view),
10585 "grid-line-width", &grid_line_width,
10588 if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10589 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10592 PsppSheetViewColumn *first_column, *last_column;
10595 /* Find the last visible column. */
10596 last_column = NULL;
10597 for (list = g_list_last (tree_view->priv->columns);
10601 PsppSheetViewColumn *c = list->data;
10609 /* Find the first visible column. */
10610 first_column = NULL;
10611 for (list = g_list_first (tree_view->priv->columns);
10615 PsppSheetViewColumn *c = list->data;
10623 if (column == first_column)
10625 cell_area->width -= grid_line_width / 2;
10627 else if (column == last_column)
10629 cell_area->x += grid_line_width / 2;
10630 cell_area->width -= grid_line_width / 2;
10634 cell_area->x += grid_line_width / 2;
10635 cell_area->width -= grid_line_width;
10639 if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10640 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10642 cell_area->y += grid_line_width / 2;
10643 cell_area->height -= grid_line_width;
10647 if (column == NULL)
10650 cell_area->width = 0;
10655 * pspp_sheet_view_get_cell_area:
10656 * @tree_view: a #PsppSheetView
10657 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10658 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10659 * @rect: rectangle to fill with cell rect
10661 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10662 * row specified by @path and the column specified by @column. If @path is
10663 * %NULL, or points to a path not currently displayed, the @y and @height fields
10664 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10665 * fields will be filled with 0. The sum of all cell rects does not cover the
10666 * entire tree; there are extra pixels in between rows, for example. The
10667 * returned rectangle is equivalent to the @cell_area passed to
10668 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10672 pspp_sheet_view_get_cell_area (PsppSheetView *tree_view,
10674 PsppSheetViewColumn *column,
10675 GdkRectangle *rect)
10677 GdkRectangle background_area;
10679 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10680 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10681 g_return_if_fail (rect != NULL);
10682 g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10683 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10685 pspp_sheet_view_get_background_area (tree_view, path, column,
10687 pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10692 * pspp_sheet_view_get_background_area:
10693 * @tree_view: a #PsppSheetView
10694 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10695 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10696 * @rect: rectangle to fill with cell background rect
10698 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10699 * row specified by @path and the column specified by @column. If @path is
10700 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10701 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10702 * fields will be filled with 0. The returned rectangle is equivalent to the
10703 * @background_area passed to gtk_cell_renderer_render(). These background
10704 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10705 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10706 * itself, excluding surrounding borders.
10710 pspp_sheet_view_get_background_area (PsppSheetView *tree_view,
10712 PsppSheetViewColumn *column,
10713 GdkRectangle *rect)
10717 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10718 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10719 g_return_if_fail (rect != NULL);
10728 /* Get vertical coords */
10730 _pspp_sheet_view_find_node (tree_view, path, &node);
10734 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10736 rect->height = ROW_HEIGHT (tree_view);
10743 pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10744 rect->width = x2 - rect->x;
10749 * pspp_sheet_view_get_visible_rect:
10750 * @tree_view: a #PsppSheetView
10751 * @visible_rect: rectangle to fill
10753 * Fills @visible_rect with the currently-visible region of the
10754 * buffer, in tree coordinates. Convert to bin_window coordinates with
10755 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10756 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10757 * scrollable area of the tree.
10760 pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view,
10761 GdkRectangle *visible_rect)
10765 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10767 widget = GTK_WIDGET (tree_view);
10771 GtkAllocation allocation;
10772 gtk_widget_get_allocation (widget, &allocation);
10773 visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
10774 visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
10775 visible_rect->width = allocation.width;
10776 visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10781 * pspp_sheet_view_widget_to_tree_coords:
10782 * @tree_view: a #PsppSheetView
10783 * @wx: X coordinate relative to bin_window
10784 * @wy: Y coordinate relative to bin_window
10785 * @tx: return location for tree X coordinate
10786 * @ty: return location for tree Y coordinate
10788 * Converts bin_window coordinates to coordinates for the
10789 * tree (the full scrollable area of the tree).
10791 * Deprecated: 2.12: Due to historial reasons the name of this function is
10792 * incorrect. For converting coordinates relative to the widget to
10793 * bin_window coordinates, please see
10794 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10798 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10804 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10807 *tx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10809 *ty = wy + tree_view->priv->dy;
10813 * pspp_sheet_view_tree_to_widget_coords:
10814 * @tree_view: a #PsppSheetView
10815 * @tx: tree X coordinate
10816 * @ty: tree Y coordinate
10817 * @wx: return location for X coordinate relative to bin_window
10818 * @wy: return location for Y coordinate relative to bin_window
10820 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10821 * to bin_window coordinates.
10823 * Deprecated: 2.12: Due to historial reasons the name of this function is
10824 * incorrect. For converting bin_window coordinates to coordinates relative
10825 * to bin_window, please see
10826 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10830 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10836 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10839 *wx = tx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10841 *wy = ty - tree_view->priv->dy;
10846 * pspp_sheet_view_convert_widget_to_tree_coords:
10847 * @tree_view: a #PsppSheetView
10848 * @wx: X coordinate relative to the widget
10849 * @wy: Y coordinate relative to the widget
10850 * @tx: return location for tree X coordinate
10851 * @ty: return location for tree Y coordinate
10853 * Converts widget coordinates to coordinates for the
10854 * tree (the full scrollable area of the tree).
10859 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10867 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10869 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10872 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10878 * pspp_sheet_view_convert_tree_to_widget_coords:
10879 * @tree_view: a #PsppSheetView
10880 * @tx: X coordinate relative to the tree
10881 * @ty: Y coordinate relative to the tree
10882 * @wx: return location for widget X coordinate
10883 * @wy: return location for widget Y coordinate
10885 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10886 * to widget coordinates.
10891 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10899 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10901 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10904 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10910 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10911 * @tree_view: a #PsppSheetView
10912 * @wx: X coordinate relative to the widget
10913 * @wy: Y coordinate relative to the widget
10914 * @bx: return location for bin_window X coordinate
10915 * @by: return location for bin_window Y coordinate
10917 * Converts widget coordinates to coordinates for the bin_window
10918 * (see pspp_sheet_view_get_bin_window()).
10923 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10929 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10932 *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10934 *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10938 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10939 * @tree_view: a #PsppSheetView
10940 * @bx: bin_window X coordinate
10941 * @by: bin_window Y coordinate
10942 * @wx: return location for widget X coordinate
10943 * @wy: return location for widget Y coordinate
10945 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10946 * to widget relative coordinates.
10951 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10957 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10960 *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10962 *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10966 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10967 * @tree_view: a #PsppSheetView
10968 * @tx: tree X coordinate
10969 * @ty: tree Y coordinate
10970 * @bx: return location for X coordinate relative to bin_window
10971 * @by: return location for Y coordinate relative to bin_window
10973 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10974 * to bin_window coordinates.
10979 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10985 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10990 *by = ty - tree_view->priv->dy;
10994 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10995 * @tree_view: a #PsppSheetView
10996 * @bx: X coordinate relative to bin_window
10997 * @by: Y coordinate relative to bin_window
10998 * @tx: return location for tree X coordinate
10999 * @ty: return location for tree Y coordinate
11001 * Converts bin_window coordinates to coordinates for the
11002 * tree (the full scrollable area of the tree).
11007 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
11013 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11018 *ty = by + tree_view->priv->dy;
11024 * pspp_sheet_view_get_visible_range:
11025 * @tree_view: A #PsppSheetView
11026 * @start_path: (allow-none): Return location for start of region, or %NULL.
11027 * @end_path: (allow-none): Return location for end of region, or %NULL.
11029 * Sets @start_path and @end_path to be the first and last visible path.
11030 * Note that there may be invisible paths in between.
11032 * The paths should be freed with gtk_tree_path_free() after use.
11034 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
11039 pspp_sheet_view_get_visible_range (PsppSheetView *tree_view,
11040 GtkTreePath **start_path,
11041 GtkTreePath **end_path)
11046 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11048 if (!tree_view->priv->row_count)
11055 pspp_sheet_view_find_offset (tree_view,
11056 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
11059 *start_path = _pspp_sheet_view_find_path (tree_view, node);
11068 if (tree_view->priv->height < gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
11069 y = tree_view->priv->height - 1;
11071 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1;
11073 pspp_sheet_view_find_offset (tree_view, y, &node);
11075 *end_path = _pspp_sheet_view_find_path (tree_view, node);
11084 unset_reorderable (PsppSheetView *tree_view)
11086 if (tree_view->priv->reorderable)
11088 tree_view->priv->reorderable = FALSE;
11089 g_object_notify (G_OBJECT (tree_view), "reorderable");
11094 * pspp_sheet_view_enable_model_drag_source:
11095 * @tree_view: a #PsppSheetView
11096 * @start_button_mask: Mask of allowed buttons to start drag
11097 * @targets: the table of targets that the drag will support
11098 * @n_targets: the number of items in @targets
11099 * @actions: the bitmask of possible actions for a drag from this
11102 * Turns @tree_view into a drag source for automatic DND. Calling this
11103 * method sets #PsppSheetView:reorderable to %FALSE.
11106 pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view,
11107 GdkModifierType start_button_mask,
11108 const GtkTargetEntry *targets,
11110 GdkDragAction actions)
11112 TreeViewDragInfo *di;
11114 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11116 gtk_drag_source_set (GTK_WIDGET (tree_view),
11122 di = ensure_info (tree_view);
11124 di->start_button_mask = start_button_mask;
11125 di->source_actions = actions;
11126 di->source_set = TRUE;
11128 unset_reorderable (tree_view);
11132 * pspp_sheet_view_enable_model_drag_dest:
11133 * @tree_view: a #PsppSheetView
11134 * @targets: the table of targets that the drag will support
11135 * @n_targets: the number of items in @targets
11136 * @actions: the bitmask of possible actions for a drag from this
11139 * Turns @tree_view into a drop destination for automatic DND. Calling
11140 * this method sets #PsppSheetView:reorderable to %FALSE.
11143 pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view,
11144 const GtkTargetEntry *targets,
11146 GdkDragAction actions)
11148 TreeViewDragInfo *di;
11150 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11152 gtk_drag_dest_set (GTK_WIDGET (tree_view),
11158 di = ensure_info (tree_view);
11159 di->dest_set = TRUE;
11161 unset_reorderable (tree_view);
11165 * pspp_sheet_view_unset_rows_drag_source:
11166 * @tree_view: a #PsppSheetView
11168 * Undoes the effect of
11169 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
11170 * #PsppSheetView:reorderable to %FALSE.
11173 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
11175 TreeViewDragInfo *di;
11177 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11179 di = get_info (tree_view);
11183 if (di->source_set)
11185 gtk_drag_source_unset (GTK_WIDGET (tree_view));
11186 di->source_set = FALSE;
11189 if (!di->dest_set && !di->source_set)
11190 remove_info (tree_view);
11193 unset_reorderable (tree_view);
11197 * pspp_sheet_view_unset_rows_drag_dest:
11198 * @tree_view: a #PsppSheetView
11200 * Undoes the effect of
11201 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
11202 * #PsppSheetView:reorderable to %FALSE.
11205 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
11207 TreeViewDragInfo *di;
11209 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11211 di = get_info (tree_view);
11217 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
11218 di->dest_set = FALSE;
11221 if (!di->dest_set && !di->source_set)
11222 remove_info (tree_view);
11225 unset_reorderable (tree_view);
11229 * pspp_sheet_view_set_drag_dest_row:
11230 * @tree_view: a #PsppSheetView
11231 * @path: (allow-none): The path of the row to highlight, or %NULL.
11232 * @pos: Specifies whether to drop before, after or into the row
11234 * Sets the row that is highlighted for feedback.
11237 pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view,
11239 PsppSheetViewDropPosition pos)
11241 GtkTreePath *current_dest;
11243 /* Note; this function is exported to allow a custom DND
11244 * implementation, so it can't touch TreeViewDragInfo
11247 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11249 current_dest = NULL;
11251 if (tree_view->priv->drag_dest_row)
11253 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11254 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
11257 /* special case a drop on an empty model */
11258 tree_view->priv->empty_view_drop = 0;
11260 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
11261 && gtk_tree_path_get_depth (path) == 1
11262 && gtk_tree_path_get_indices (path)[0] == 0)
11266 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
11270 tree_view->priv->empty_view_drop = 1;
11273 tree_view->priv->drag_dest_pos = pos;
11277 tree_view->priv->drag_dest_row =
11278 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
11279 pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
11282 tree_view->priv->drag_dest_row = NULL;
11286 int node, new_node;
11288 _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11289 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11293 new_node = pspp_sheet_view_node_next (tree_view, node);
11295 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11297 new_node = pspp_sheet_view_node_prev (tree_view, node);
11299 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11301 gtk_tree_path_free (current_dest);
11306 * pspp_sheet_view_get_drag_dest_row:
11307 * @tree_view: a #PsppSheetView
11308 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11309 * @pos: (allow-none): Return location for the drop position, or %NULL
11311 * Gets information about the row that is highlighted for feedback.
11314 pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view,
11315 GtkTreePath **path,
11316 PsppSheetViewDropPosition *pos)
11318 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11322 if (tree_view->priv->drag_dest_row)
11323 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11326 if (tree_view->priv->empty_view_drop)
11327 *path = gtk_tree_path_new_from_indices (0, -1);
11334 *pos = tree_view->priv->drag_dest_pos;
11338 * pspp_sheet_view_get_dest_row_at_pos:
11339 * @tree_view: a #PsppSheetView
11340 * @drag_x: the position to determine the destination row for
11341 * @drag_y: the position to determine the destination row for
11342 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11343 * @pos: (allow-none): Return location for the drop position, or %NULL
11345 * Determines the destination row for a given position. @drag_x and
11346 * @drag_y are expected to be in widget coordinates. This function is only
11347 * meaningful if @tree_view is realized. Therefore this function will always
11348 * return %FALSE if @tree_view is not realized or does not have a model.
11350 * Return value: whether there is a row at the given position, %TRUE if this
11351 * is indeed the case.
11354 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view,
11357 GtkTreePath **path,
11358 PsppSheetViewDropPosition *pos)
11362 gdouble offset_into_row;
11365 PsppSheetViewColumn *column = NULL;
11366 GtkTreePath *tmp_path = NULL;
11368 /* Note; this function is exported to allow a custom DND
11369 * implementation, so it can't touch TreeViewDragInfo
11372 g_return_val_if_fail (tree_view != NULL, FALSE);
11373 g_return_val_if_fail (drag_x >= 0, FALSE);
11374 g_return_val_if_fail (drag_y >= 0, FALSE);
11379 if (tree_view->priv->bin_window == NULL)
11382 if (tree_view->priv->row_count == 0)
11385 /* If in the top third of a row, we drop before that row; if
11386 * in the bottom third, drop after that row; if in the middle,
11387 * and the row has children, drop into the row.
11389 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11392 if (!pspp_sheet_view_get_path_at_pos (tree_view,
11401 pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11404 offset_into_row = cell_y;
11409 gtk_tree_path_free (tmp_path);
11413 third = cell.height / 3.0;
11417 if (offset_into_row < third)
11419 *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11421 else if (offset_into_row < (cell.height / 2.0))
11423 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11425 else if (offset_into_row < third * 2.0)
11427 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11431 *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11439 #if GTK3_TRANSITION
11440 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11442 * pspp_sheet_view_create_row_drag_icon:
11443 * @tree_view: a #PsppSheetView
11444 * @path: a #GtkTreePath in @tree_view
11446 * Creates a #GdkPixmap representation of the row at @path.
11447 * This image is used for a drag icon.
11449 * Return value: a newly-allocated pixmap of the drag icon.
11452 pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view,
11459 GdkRectangle background_area;
11460 GdkRectangle expose_area;
11462 /* start drawing inside the black outline */
11464 GdkDrawable *drawable;
11465 gint bin_window_width;
11468 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11469 g_return_val_if_fail (path != NULL, NULL);
11471 widget = GTK_WIDGET (tree_view);
11473 if (!gtk_widget_get_realized (widget))
11476 _pspp_sheet_view_find_node (tree_view,
11483 if (!gtk_tree_model_get_iter (tree_view->priv->model,
11490 background_area.y = y;
11491 background_area.height = ROW_HEIGHT (tree_view);
11493 bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
11495 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11496 bin_window_width + 2,
11497 background_area.height + 2,
11502 expose_area.width = bin_window_width + 2;
11503 expose_area.height = background_area.height + 2;
11505 #if GTK3_TRANSITION
11506 gdk_draw_rectangle (drawable,
11507 widget->style->base_gc [gtk_widget_get_state (widget)],
11510 bin_window_width + 2,
11511 background_area.height + 2);
11514 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11516 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11518 list = (rtl ? list->prev : list->next))
11520 PsppSheetViewColumn *column = list->data;
11521 GdkRectangle cell_area;
11522 gint vertical_separator;
11524 if (!column->visible)
11527 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11529 background_area.x = cell_offset;
11530 background_area.width = column->width;
11532 gtk_widget_style_get (widget,
11533 "vertical-separator", &vertical_separator,
11536 cell_area = background_area;
11538 cell_area.y += vertical_separator / 2;
11539 cell_area.height -= vertical_separator;
11541 if (pspp_sheet_view_column_cell_is_visible (column))
11542 _pspp_sheet_view_column_cell_render (column,
11548 cell_offset += column->width;
11551 #if GTK3_TRANSITION
11552 gdk_draw_rectangle (drawable,
11553 widget->style->black_gc,
11556 bin_window_width + 1,
11557 background_area.height + 1);
11565 * pspp_sheet_view_set_destroy_count_func:
11566 * @tree_view: A #PsppSheetView
11567 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11568 * @data: (allow-none): User data to be passed to @func, or %NULL
11569 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11571 * This function should almost never be used. It is meant for private use by
11572 * ATK for determining the number of visible children that are removed when a row is deleted.
11575 pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view,
11576 PsppSheetDestroyCountFunc func,
11578 GDestroyNotify destroy)
11580 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11582 if (tree_view->priv->destroy_count_destroy)
11583 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11585 tree_view->priv->destroy_count_func = func;
11586 tree_view->priv->destroy_count_data = data;
11587 tree_view->priv->destroy_count_destroy = destroy;
11592 * Interactive search
11596 * pspp_sheet_view_set_enable_search:
11597 * @tree_view: A #PsppSheetView
11598 * @enable_search: %TRUE, if the user can search interactively
11600 * If @enable_search is set, then the user can type in text to search through
11601 * the tree interactively (this is sometimes called "typeahead find").
11603 * Note that even if this is %FALSE, the user can still initiate a search
11604 * using the "start-interactive-search" key binding.
11607 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11608 gboolean enable_search)
11610 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11612 enable_search = !!enable_search;
11614 if (tree_view->priv->enable_search != enable_search)
11616 tree_view->priv->enable_search = enable_search;
11617 g_object_notify (G_OBJECT (tree_view), "enable-search");
11622 * pspp_sheet_view_get_enable_search:
11623 * @tree_view: A #PsppSheetView
11625 * Returns whether or not the tree allows to start interactive searching
11626 * by typing in text.
11628 * Return value: whether or not to let the user search interactively
11631 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11633 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11635 return tree_view->priv->enable_search;
11640 * pspp_sheet_view_get_search_column:
11641 * @tree_view: A #PsppSheetView
11643 * Gets the column searched on by the interactive search code.
11645 * Return value: the column the interactive search code searches in.
11648 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11650 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11652 return (tree_view->priv->search_column);
11656 * pspp_sheet_view_set_search_column:
11657 * @tree_view: A #PsppSheetView
11658 * @column: the column of the model to search in, or -1 to disable searching
11660 * Sets @column as the column where the interactive search code should
11661 * search in for the current model.
11663 * If the search column is set, users can use the "start-interactive-search"
11664 * key binding to bring up search popup. The enable-search property controls
11665 * whether simply typing text will also start an interactive search.
11667 * Note that @column refers to a column of the current model. The search
11668 * column is reset to -1 when the model is changed.
11671 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11674 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11675 g_return_if_fail (column >= -1);
11677 if (tree_view->priv->search_column == column)
11680 tree_view->priv->search_column = column;
11681 g_object_notify (G_OBJECT (tree_view), "search-column");
11685 * pspp_sheet_view_get_search_equal_func:
11686 * @tree_view: A #PsppSheetView
11688 * Returns the compare function currently in use.
11690 * Return value: the currently used compare function for the search code.
11693 PsppSheetViewSearchEqualFunc
11694 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11696 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11698 return tree_view->priv->search_equal_func;
11702 * pspp_sheet_view_set_search_equal_func:
11703 * @tree_view: A #PsppSheetView
11704 * @search_equal_func: the compare function to use during the search
11705 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11706 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11708 * Sets the compare function for the interactive search capabilities; note
11709 * that somewhat like strcmp() returning 0 for equality
11710 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11713 pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view,
11714 PsppSheetViewSearchEqualFunc search_equal_func,
11715 gpointer search_user_data,
11716 GDestroyNotify search_destroy)
11718 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11719 g_return_if_fail (search_equal_func != NULL);
11721 if (tree_view->priv->search_destroy)
11722 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11724 tree_view->priv->search_equal_func = search_equal_func;
11725 tree_view->priv->search_user_data = search_user_data;
11726 tree_view->priv->search_destroy = search_destroy;
11727 if (tree_view->priv->search_equal_func == NULL)
11728 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11732 * pspp_sheet_view_get_search_entry:
11733 * @tree_view: A #PsppSheetView
11735 * Returns the #GtkEntry which is currently in use as interactive search
11736 * entry for @tree_view. In case the built-in entry is being used, %NULL
11737 * will be returned.
11739 * Return value: the entry currently in use as search entry.
11744 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11746 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11748 if (tree_view->priv->search_custom_entry_set)
11749 return GTK_ENTRY (tree_view->priv->search_entry);
11755 * pspp_sheet_view_set_search_entry:
11756 * @tree_view: A #PsppSheetView
11757 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11759 * Sets the entry which the interactive search code will use for this
11760 * @tree_view. This is useful when you want to provide a search entry
11761 * in our interface at all time at a fixed position. Passing %NULL for
11762 * @entry will make the interactive search code use the built-in popup
11768 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11771 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11772 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11774 if (tree_view->priv->search_custom_entry_set)
11776 if (tree_view->priv->search_entry_changed_id)
11778 g_signal_handler_disconnect (tree_view->priv->search_entry,
11779 tree_view->priv->search_entry_changed_id);
11780 tree_view->priv->search_entry_changed_id = 0;
11782 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11783 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11786 g_object_unref (tree_view->priv->search_entry);
11788 else if (tree_view->priv->search_window)
11790 gtk_widget_destroy (tree_view->priv->search_window);
11792 tree_view->priv->search_window = NULL;
11797 tree_view->priv->search_entry = g_object_ref (entry);
11798 tree_view->priv->search_custom_entry_set = TRUE;
11800 if (tree_view->priv->search_entry_changed_id == 0)
11802 tree_view->priv->search_entry_changed_id =
11803 g_signal_connect (tree_view->priv->search_entry, "changed",
11804 G_CALLBACK (pspp_sheet_view_search_init),
11808 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11809 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11812 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11816 tree_view->priv->search_entry = NULL;
11817 tree_view->priv->search_custom_entry_set = FALSE;
11822 * pspp_sheet_view_set_search_position_func:
11823 * @tree_view: A #PsppSheetView
11824 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11825 * to use the default search position function
11826 * @data: (allow-none): user data to pass to @func, or %NULL
11827 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11829 * Sets the function to use when positioning the search dialog.
11834 pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view,
11835 PsppSheetViewSearchPositionFunc func,
11836 gpointer user_data,
11837 GDestroyNotify destroy)
11839 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11841 if (tree_view->priv->search_position_destroy)
11842 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11844 tree_view->priv->search_position_func = func;
11845 tree_view->priv->search_position_user_data = user_data;
11846 tree_view->priv->search_position_destroy = destroy;
11847 if (tree_view->priv->search_position_func == NULL)
11848 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11852 * pspp_sheet_view_get_search_position_func:
11853 * @tree_view: A #PsppSheetView
11855 * Returns the positioning function currently in use.
11857 * Return value: the currently used function for positioning the search dialog.
11861 PsppSheetViewSearchPositionFunc
11862 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11864 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11866 return tree_view->priv->search_position_func;
11871 pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
11872 PsppSheetView *tree_view)
11874 if (tree_view->priv->disable_popdown)
11877 if (tree_view->priv->search_entry_changed_id)
11879 g_signal_handler_disconnect (tree_view->priv->search_entry,
11880 tree_view->priv->search_entry_changed_id);
11881 tree_view->priv->search_entry_changed_id = 0;
11883 if (tree_view->priv->typeselect_flush_timeout)
11885 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11886 tree_view->priv->typeselect_flush_timeout = 0;
11889 if (gtk_widget_get_visible (search_dialog))
11891 /* send focus-in event */
11892 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11893 gtk_widget_hide (search_dialog);
11894 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11895 send_focus_change (GTK_WIDGET (tree_view), TRUE);
11900 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11901 GtkWidget *search_dialog,
11902 gpointer user_data)
11905 gint tree_x, tree_y;
11906 gint tree_width, tree_height;
11907 GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
11908 GdkScreen *screen = gdk_window_get_screen (tree_window);
11909 GtkRequisition requisition;
11911 GdkRectangle monitor;
11913 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11914 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11916 gtk_widget_realize (search_dialog);
11918 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11919 tree_width = gdk_window_get_width (tree_window);
11920 tree_height = gdk_window_get_height (tree_window);
11922 gtk_widget_size_request (search_dialog, &requisition);
11924 if (tree_x + tree_width > gdk_screen_get_width (screen))
11925 x = gdk_screen_get_width (screen) - requisition.width;
11926 else if (tree_x + tree_width - requisition.width < 0)
11929 x = tree_x + tree_width - requisition.width;
11931 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11932 y = gdk_screen_get_height (screen) - requisition.height;
11933 else if (tree_y + tree_height < 0) /* isn't really possible ... */
11936 y = tree_y + tree_height;
11938 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11942 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11946 PsppSheetView *tree_view = (PsppSheetView *)data;
11948 tree_view->priv->disable_popdown = 1;
11949 g_signal_connect (menu, "hide",
11950 G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11953 #if GTK3_TRANSITION
11954 /* Because we're visible but offscreen, we just set a flag in the preedit
11958 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11959 PsppSheetView *tree_view)
11961 tree_view->priv->imcontext_changed = 1;
11962 if (tree_view->priv->typeselect_flush_timeout)
11964 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11965 tree_view->priv->typeselect_flush_timeout =
11966 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11967 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11975 pspp_sheet_view_search_activate (GtkEntry *entry,
11976 PsppSheetView *tree_view)
11981 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11984 /* If we have a row selected and it's the cursor row, we activate
11986 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11988 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11990 _pspp_sheet_view_find_node (tree_view, path, &node);
11992 if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11993 pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11995 gtk_tree_path_free (path);
12000 pspp_sheet_view_real_search_enable_popdown (gpointer data)
12002 PsppSheetView *tree_view = (PsppSheetView *)data;
12004 tree_view->priv->disable_popdown = 0;
12010 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
12013 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
12017 pspp_sheet_view_search_delete_event (GtkWidget *widget,
12018 GdkEventAny *event,
12019 PsppSheetView *tree_view)
12021 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
12023 pspp_sheet_view_search_dialog_hide (widget, tree_view);
12029 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
12030 GdkEventButton *event,
12031 PsppSheetView *tree_view)
12033 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
12035 pspp_sheet_view_search_dialog_hide (widget, tree_view);
12037 if (event->window == tree_view->priv->bin_window)
12038 pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
12044 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
12045 GdkEventScroll *event,
12046 PsppSheetView *tree_view)
12048 gboolean retval = FALSE;
12050 if (event->direction == GDK_SCROLL_UP)
12052 pspp_sheet_view_search_move (widget, tree_view, TRUE);
12055 else if (event->direction == GDK_SCROLL_DOWN)
12057 pspp_sheet_view_search_move (widget, tree_view, FALSE);
12061 /* renew the flush timeout */
12062 if (retval && tree_view->priv->typeselect_flush_timeout
12063 && !tree_view->priv->search_custom_entry_set)
12065 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12066 tree_view->priv->typeselect_flush_timeout =
12067 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12068 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12076 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
12077 GdkEventKey *event,
12078 PsppSheetView *tree_view)
12080 gboolean retval = FALSE;
12082 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
12083 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12085 /* close window and cancel the search */
12086 if (!tree_view->priv->search_custom_entry_set
12087 && (event->keyval == GDK_Escape ||
12088 event->keyval == GDK_Tab ||
12089 event->keyval == GDK_KP_Tab ||
12090 event->keyval == GDK_ISO_Left_Tab))
12092 pspp_sheet_view_search_dialog_hide (widget, tree_view);
12096 /* select previous matching iter */
12097 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
12099 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
12100 gtk_widget_error_bell (widget);
12105 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
12106 && (event->keyval == GDK_g || event->keyval == GDK_G))
12108 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
12109 gtk_widget_error_bell (widget);
12114 /* select next matching iter */
12115 if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
12117 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
12118 gtk_widget_error_bell (widget);
12123 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
12124 && (event->keyval == GDK_g || event->keyval == GDK_G))
12126 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
12127 gtk_widget_error_bell (widget);
12132 /* renew the flush timeout */
12133 if (retval && tree_view->priv->typeselect_flush_timeout
12134 && !tree_view->priv->search_custom_entry_set)
12136 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12137 tree_view->priv->typeselect_flush_timeout =
12138 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12139 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12146 /* this function returns FALSE if there is a search string but
12147 * nothing was found, and TRUE otherwise.
12150 pspp_sheet_view_search_move (GtkWidget *window,
12151 PsppSheetView *tree_view,
12159 GtkTreeModel *model;
12160 PsppSheetSelection *selection;
12162 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
12164 g_return_val_if_fail (text != NULL, FALSE);
12166 len = strlen (text);
12168 if (up && tree_view->priv->selected_iter == 1)
12169 return strlen (text) < 1;
12171 len = strlen (text);
12176 model = pspp_sheet_view_get_model (tree_view);
12177 selection = pspp_sheet_view_get_selection (tree_view);
12180 pspp_sheet_selection_unselect_all (selection);
12181 if (!gtk_tree_model_get_iter_first (model, &iter))
12184 ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
12185 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
12190 tree_view->priv->selected_iter += up?(-1):(1);
12195 /* return to old iter */
12197 gtk_tree_model_get_iter_first (model, &iter);
12198 pspp_sheet_view_search_iter (model, selection,
12200 &count, tree_view->priv->selected_iter);
12206 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
12210 gpointer search_data)
12212 gboolean retval = TRUE;
12214 gchar *normalized_string;
12215 gchar *normalized_key;
12216 gchar *case_normalized_string = NULL;
12217 gchar *case_normalized_key = NULL;
12218 GValue value = {0,};
12219 GValue transformed = {0,};
12221 gtk_tree_model_get_value (model, iter, column, &value);
12223 g_value_init (&transformed, G_TYPE_STRING);
12225 if (!g_value_transform (&value, &transformed))
12227 g_value_unset (&value);
12231 g_value_unset (&value);
12233 str = g_value_get_string (&transformed);
12236 g_value_unset (&transformed);
12240 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
12241 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
12243 if (normalized_string && normalized_key)
12245 case_normalized_string = g_utf8_casefold (normalized_string, -1);
12246 case_normalized_key = g_utf8_casefold (normalized_key, -1);
12248 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
12252 g_value_unset (&transformed);
12253 g_free (normalized_key);
12254 g_free (normalized_string);
12255 g_free (case_normalized_key);
12256 g_free (case_normalized_string);
12262 pspp_sheet_view_search_iter (GtkTreeModel *model,
12263 PsppSheetSelection *selection,
12272 PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
12274 path = gtk_tree_model_get_path (model, iter);
12275 _pspp_sheet_view_find_node (tree_view, path, &node);
12279 gboolean done = FALSE;
12281 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
12286 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
12288 pspp_sheet_selection_select_iter (selection, iter);
12289 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12292 gtk_tree_path_free (path);
12301 node = pspp_sheet_view_node_next (tree_view, node);
12307 has_next = gtk_tree_model_iter_next (model, iter);
12310 gtk_tree_path_next (path);
12313 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12318 gtk_tree_path_free (path);
12320 /* we've run out of tree, done with this func */
12332 pspp_sheet_view_search_init (GtkWidget *entry,
12333 PsppSheetView *tree_view)
12339 GtkTreeModel *model;
12340 PsppSheetSelection *selection;
12342 g_return_if_fail (GTK_IS_ENTRY (entry));
12343 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12345 text = gtk_entry_get_text (GTK_ENTRY (entry));
12347 model = pspp_sheet_view_get_model (tree_view);
12348 selection = pspp_sheet_view_get_selection (tree_view);
12351 pspp_sheet_selection_unselect_all (selection);
12352 if (tree_view->priv->typeselect_flush_timeout
12353 && !tree_view->priv->search_custom_entry_set)
12355 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12356 tree_view->priv->typeselect_flush_timeout =
12357 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12358 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12365 if (!gtk_tree_model_get_iter_first (model, &iter))
12368 ret = pspp_sheet_view_search_iter (model, selection,
12373 tree_view->priv->selected_iter = 1;
12377 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12378 PsppSheetView *tree_view)
12380 if (tree_view->priv->edited_column == NULL)
12383 _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12384 tree_view->priv->edited_column = NULL;
12386 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12387 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12389 g_signal_handlers_disconnect_by_func (cell_editable,
12390 pspp_sheet_view_remove_widget,
12392 g_signal_handlers_disconnect_by_func (cell_editable,
12393 pspp_sheet_view_editable_button_press_event,
12395 g_signal_handlers_disconnect_by_func (cell_editable,
12396 pspp_sheet_view_editable_clicked,
12399 gtk_container_remove (GTK_CONTAINER (tree_view),
12400 GTK_WIDGET (cell_editable));
12402 /* FIXME should only redraw a single node */
12403 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12407 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12408 GtkTreePath *cursor_path)
12411 GdkRectangle background_area;
12412 GdkRectangle cell_area;
12413 GtkCellEditable *editable_widget = NULL;
12414 gchar *path_string;
12415 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12416 gint retval = FALSE;
12419 g_assert (tree_view->priv->focus_column);
12421 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12424 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12425 if (cursor_node < 0)
12428 path_string = gtk_tree_path_to_string (cursor_path);
12429 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12431 pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12432 tree_view->priv->model,
12434 pspp_sheet_view_get_background_area (tree_view,
12436 tree_view->priv->focus_column,
12438 pspp_sheet_view_get_cell_area (tree_view,
12440 tree_view->priv->focus_column,
12443 if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12452 if (editable_widget != NULL)
12456 GtkCellRenderer *cell;
12459 cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12461 _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12464 area.width -= right + left;
12466 pspp_sheet_view_real_start_editing (tree_view,
12467 tree_view->priv->focus_column,
12476 g_free (path_string);
12481 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12482 GdkEventButton *event,
12483 PsppSheetView *sheet_view)
12487 node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12488 "pspp-sheet-view-node"));
12489 return pspp_sheet_view_row_head_clicked (sheet_view,
12491 sheet_view->priv->edited_column,
12496 pspp_sheet_view_editable_clicked (GtkButton *button,
12497 PsppSheetView *sheet_view)
12499 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12504 is_all_selected (GtkWidget *widget)
12506 GtkEntryBuffer *buffer;
12507 gint start_pos, end_pos;
12509 if (!GTK_IS_ENTRY (widget))
12512 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12513 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12514 &start_pos, &end_pos)
12516 && end_pos == gtk_entry_buffer_get_length (buffer));
12520 is_at_left (GtkWidget *widget)
12522 return (GTK_IS_ENTRY (widget)
12523 && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12527 is_at_right (GtkWidget *widget)
12529 GtkEntryBuffer *buffer;
12532 if (!GTK_IS_ENTRY (widget))
12535 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12536 length = gtk_entry_buffer_get_length (buffer);
12537 return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12541 pspp_sheet_view_event (GtkWidget *widget,
12542 GdkEventKey *event,
12543 PsppSheetView *tree_view)
12545 PsppSheetViewColumn *column;
12552 /* Intercept only key press events.
12553 It would make sense to use "key-press-event" instead of "event", but
12554 GtkEntry attaches its own signal handler to "key-press-event" that runs
12555 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12557 if (event->type != GDK_KEY_PRESS)
12560 keyval = event->keyval;
12562 switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12565 switch (event->keyval)
12567 case GDK_Left: case GDK_KP_Left:
12568 case GDK_Home: case GDK_KP_Home:
12569 if (!is_all_selected (widget) && !is_at_left (widget))
12573 case GDK_Right: case GDK_KP_Right:
12574 case GDK_End: case GDK_KP_End:
12575 if (!is_all_selected (widget) && !is_at_right (widget))
12579 case GDK_Up: case GDK_KP_Up:
12580 case GDK_Down: case GDK_KP_Down:
12583 case GDK_Page_Up: case GDK_KP_Page_Up:
12584 case GDK_Page_Down: case GDK_KP_Page_Down:
12595 case GDK_Tab: case GDK_KP_Tab:
12596 case GDK_ISO_Left_Tab:
12605 case GDK_SHIFT_MASK:
12606 switch (event->keyval)
12609 case GDK_ISO_Left_Tab:
12618 case GDK_CONTROL_MASK:
12619 switch (event->keyval)
12621 case GDK_Left: case GDK_KP_Left:
12622 if (!is_all_selected (widget) && !is_at_left (widget))
12626 case GDK_Right: case GDK_KP_Right:
12627 if (!is_all_selected (widget) && !is_at_right (widget))
12631 case GDK_Up: case GDK_KP_Up:
12632 case GDK_Down: case GDK_KP_Down:
12644 row = tree_view->priv->edited_row;
12645 column = tree_view->priv->edited_column;
12646 path = gtk_tree_path_new_from_indices (row, -1);
12648 pspp_sheet_view_stop_editing (tree_view, cancel);
12649 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12651 pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12652 gtk_tree_path_free (path);
12654 handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12655 G_OBJECT (tree_view));
12657 g_signal_stop_emission_by_name (widget, "event");
12659 pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12660 pspp_sheet_view_start_editing (tree_view, path);
12661 gtk_tree_path_free (path);
12667 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12670 PsppSheetView *sheet_view = data;
12672 g_signal_connect (widget, "event",
12673 G_CALLBACK (pspp_sheet_view_event),
12676 if (GTK_IS_CONTAINER (widget))
12677 gtk_container_foreach (GTK_CONTAINER (widget),
12678 pspp_sheet_view_override_cell_keypresses,
12683 pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
12684 PsppSheetViewColumn *column,
12686 GtkCellEditable *cell_editable,
12687 GdkRectangle *cell_area,
12691 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12692 gint pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
12693 GtkRequisition requisition;
12696 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12698 tree_view->priv->edited_column = column;
12699 _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12701 row = gtk_tree_path_get_indices (path)[0];
12702 tree_view->priv->edited_row = row;
12703 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12704 cell_area->y += pre_val - (int)gtk_adjustment_get_value (tree_view->priv->vadjustment);
12706 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12707 pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12708 tree_view->priv->anchor_column = column;
12710 gtk_widget_size_request (GTK_WIDGET (cell_editable), &requisition);
12712 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12714 if (requisition.height < cell_area->height)
12716 gint diff = cell_area->height - requisition.height;
12717 pspp_sheet_view_put (tree_view,
12718 GTK_WIDGET (cell_editable),
12719 cell_area->x, cell_area->y + diff/2,
12720 cell_area->width, requisition.height);
12724 pspp_sheet_view_put (tree_view,
12725 GTK_WIDGET (cell_editable),
12726 cell_area->x, cell_area->y,
12727 cell_area->width, cell_area->height);
12730 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12731 (GdkEvent *)event);
12733 gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12734 g_signal_connect (cell_editable, "remove-widget",
12735 G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12736 if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12737 GTK_IS_BUTTON (cell_editable))
12739 g_signal_connect (cell_editable, "button-press-event",
12740 G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12742 g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12743 GINT_TO_POINTER (row));
12744 g_signal_connect (cell_editable, "clicked",
12745 G_CALLBACK (pspp_sheet_view_editable_clicked),
12749 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12754 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12755 gboolean cancel_editing)
12757 PsppSheetViewColumn *column;
12758 GtkCellRenderer *cell;
12760 if (tree_view->priv->edited_column == NULL)
12764 * This is very evil. We need to do this, because
12765 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12766 * later on. If pspp_sheet_view_row_changed notices
12767 * tree_view->priv->edited_column != NULL, it'll call
12768 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12770 * Please read that again if you intend to modify anything here.
12773 column = tree_view->priv->edited_column;
12774 tree_view->priv->edited_column = NULL;
12776 cell = _pspp_sheet_view_column_get_edited_cell (column);
12777 gtk_cell_renderer_stop_editing (cell, cancel_editing);
12779 if (!cancel_editing)
12780 gtk_cell_editable_editing_done (column->editable_widget);
12782 tree_view->priv->edited_column = column;
12784 gtk_cell_editable_remove_widget (column->editable_widget);
12789 * pspp_sheet_view_set_hover_selection:
12790 * @tree_view: a #PsppSheetView
12791 * @hover: %TRUE to enable hover selection mode
12793 * Enables of disables the hover selection mode of @tree_view.
12794 * Hover selection makes the selected row follow the pointer.
12795 * Currently, this works only for the selection modes
12796 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12801 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12804 hover = hover != FALSE;
12806 if (hover != tree_view->priv->hover_selection)
12808 tree_view->priv->hover_selection = hover;
12810 g_object_notify (G_OBJECT (tree_view), "hover-selection");
12815 * pspp_sheet_view_get_hover_selection:
12816 * @tree_view: a #PsppSheetView
12818 * Returns whether hover selection mode is turned on for @tree_view.
12820 * Return value: %TRUE if @tree_view is in hover selection mode
12825 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12827 return tree_view->priv->hover_selection;
12831 * pspp_sheet_view_set_rubber_banding:
12832 * @tree_view: a #PsppSheetView
12833 * @enable: %TRUE to enable rubber banding
12835 * Enables or disables rubber banding in @tree_view. If the selection mode is
12836 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12837 * banding will allow the user to select multiple rows by dragging the mouse.
12842 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12845 enable = enable != FALSE;
12847 if (enable != tree_view->priv->rubber_banding_enable)
12849 tree_view->priv->rubber_banding_enable = enable;
12851 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12856 * pspp_sheet_view_get_rubber_banding:
12857 * @tree_view: a #PsppSheetView
12859 * Returns whether rubber banding is turned on for @tree_view. If the
12860 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12861 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12862 * select multiple rows by dragging the mouse.
12864 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12869 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12871 return tree_view->priv->rubber_banding_enable;
12875 * pspp_sheet_view_is_rubber_banding_active:
12876 * @tree_view: a #PsppSheetView
12878 * Returns whether a rubber banding operation is currently being done
12881 * Return value: %TRUE if a rubber banding operation is currently being
12882 * done in @tree_view.
12887 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12889 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12891 if (tree_view->priv->rubber_banding_enable
12892 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12899 pspp_sheet_view_grab_notify (GtkWidget *widget,
12900 gboolean was_grabbed)
12902 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12904 tree_view->priv->in_grab = !was_grabbed;
12908 tree_view->priv->pressed_button = -1;
12910 if (tree_view->priv->rubber_band_status)
12911 pspp_sheet_view_stop_rubber_band (tree_view);
12916 pspp_sheet_view_state_changed (GtkWidget *widget,
12917 GtkStateType previous_state)
12919 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12921 if (gtk_widget_get_realized (widget))
12923 GtkStyle *style = gtk_widget_get_style (widget);
12924 gdk_window_set_background (tree_view->priv->bin_window, &style->base[gtk_widget_get_state (widget)]);
12927 gtk_widget_queue_draw (widget);
12931 * pspp_sheet_view_get_grid_lines:
12932 * @tree_view: a #PsppSheetView
12934 * Returns which grid lines are enabled in @tree_view.
12936 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12941 PsppSheetViewGridLines
12942 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12944 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12946 return tree_view->priv->grid_lines;
12950 * pspp_sheet_view_set_grid_lines:
12951 * @tree_view: a #PsppSheetView
12952 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12955 * Sets which grid lines to draw in @tree_view.
12960 pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view,
12961 PsppSheetViewGridLines grid_lines)
12963 PsppSheetViewPrivate *priv;
12964 PsppSheetViewGridLines old_grid_lines;
12966 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12968 priv = tree_view->priv;
12970 old_grid_lines = priv->grid_lines;
12971 priv->grid_lines = grid_lines;
12973 if (old_grid_lines != grid_lines)
12975 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12977 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12982 * pspp_sheet_view_get_special_cells:
12983 * @tree_view: a #PsppSheetView
12985 * Returns which grid lines are enabled in @tree_view.
12987 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12988 * the sheet view contain special cells.
12990 PsppSheetViewSpecialCells
12991 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12993 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12995 return tree_view->priv->special_cells;
12999 * pspp_sheet_view_set_special_cells:
13000 * @tree_view: a #PsppSheetView
13001 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
13002 * the sheet view contain special cells.
13004 * Sets whether rows in the sheet view contain special cells, controlling the
13005 * rendering of row selections.
13008 pspp_sheet_view_set_special_cells (PsppSheetView *tree_view,
13009 PsppSheetViewSpecialCells special_cells)
13011 PsppSheetViewPrivate *priv;
13013 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13015 priv = tree_view->priv;
13017 if (priv->special_cells != special_cells)
13019 priv->special_cells = special_cells;
13020 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
13021 g_object_notify (G_OBJECT (tree_view), "special-cells");
13026 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
13028 /* XXX (re)calculate fixed_height if necessary */
13029 return tree_view->priv->fixed_height;
13033 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
13036 g_return_if_fail (fixed_height > 0);
13038 if (tree_view->priv->fixed_height != fixed_height)
13040 tree_view->priv->fixed_height = fixed_height;
13041 g_object_notify (G_OBJECT (tree_view), "fixed-height");
13043 if (!tree_view->priv->fixed_height_set)
13045 tree_view->priv->fixed_height_set = TRUE;
13046 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
13051 * pspp_sheet_view_set_tooltip_row:
13052 * @tree_view: a #PsppSheetView
13053 * @tooltip: a #GtkTooltip
13054 * @path: a #GtkTreePath
13056 * Sets the tip area of @tooltip to be the area covered by the row at @path.
13057 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13058 * See also gtk_tooltip_set_tip_area().
13063 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
13064 GtkTooltip *tooltip,
13067 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13068 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
13070 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
13074 * pspp_sheet_view_set_tooltip_cell:
13075 * @tree_view: a #PsppSheetView
13076 * @tooltip: a #GtkTooltip
13077 * @path: (allow-none): a #GtkTreePath or %NULL
13078 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
13079 * @cell: (allow-none): a #GtkCellRenderer or %NULL
13081 * Sets the tip area of @tooltip to the area @path, @column and @cell have
13082 * in common. For example if @path is %NULL and @column is set, the tip
13083 * area will be set to the full area covered by @column. See also
13084 * gtk_tooltip_set_tip_area().
13086 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13091 pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view,
13092 GtkTooltip *tooltip,
13094 PsppSheetViewColumn *column,
13095 GtkCellRenderer *cell)
13099 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13100 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
13101 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
13102 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
13104 /* Determine x values. */
13105 if (column && cell)
13110 pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
13111 pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
13113 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13116 rect.width = width;
13122 pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
13123 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13126 rect.width = tmp.width;
13130 GtkAllocation allocation;
13131 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
13133 rect.width = allocation.width;
13136 /* Determine y values. */
13141 pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
13142 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13145 rect.height = tmp.height;
13150 rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
13153 gtk_tooltip_set_tip_area (tooltip, &rect);
13157 * pspp_sheet_view_get_tooltip_context:
13158 * @tree_view: a #PsppSheetView
13159 * @x: the x coordinate (relative to widget coordinates)
13160 * @y: the y coordinate (relative to widget coordinates)
13161 * @keyboard_tip: whether this is a keyboard tooltip or not
13162 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
13163 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
13164 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
13166 * This function is supposed to be used in a #GtkWidget::query-tooltip
13167 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
13168 * which are received in the signal handler, should be passed to this
13169 * function without modification.
13171 * The return value indicates whether there is a tree view row at the given
13172 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
13173 * tooltips the row returned will be the cursor row. When %TRUE, then any of
13174 * @model, @path and @iter which have been provided will be set to point to
13175 * that row and the corresponding model. @x and @y will always be converted
13176 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
13178 * Return value: whether or not the given tooltip context points to a row.
13183 pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view,
13186 gboolean keyboard_tip,
13187 GtkTreeModel **model,
13188 GtkTreePath **path,
13191 GtkTreePath *tmppath = NULL;
13193 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
13194 g_return_val_if_fail (x != NULL, FALSE);
13195 g_return_val_if_fail (y != NULL, FALSE);
13199 pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
13206 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
13209 if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
13210 &tmppath, NULL, NULL, NULL))
13215 *model = pspp_sheet_view_get_model (tree_view);
13218 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
13224 gtk_tree_path_free (tmppath);
13230 pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget,
13233 gboolean keyboard_tip,
13234 GtkTooltip *tooltip,
13237 GValue value = { 0, };
13238 GValue transformed = { 0, };
13241 GtkTreeModel *model;
13242 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
13244 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
13247 &model, &path, &iter))
13250 gtk_tree_model_get_value (model, &iter,
13251 tree_view->priv->tooltip_column, &value);
13253 g_value_init (&transformed, G_TYPE_STRING);
13255 if (!g_value_transform (&value, &transformed))
13257 g_value_unset (&value);
13258 gtk_tree_path_free (path);
13263 g_value_unset (&value);
13265 if (!g_value_get_string (&transformed))
13267 g_value_unset (&transformed);
13268 gtk_tree_path_free (path);
13273 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
13274 pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
13276 gtk_tree_path_free (path);
13277 g_value_unset (&transformed);
13283 * pspp_sheet_view_set_tooltip_column:
13284 * @tree_view: a #PsppSheetView
13285 * @column: an integer, which is a valid column number for @tree_view's model
13287 * If you only plan to have simple (text-only) tooltips on full rows, you
13288 * can use this function to have #PsppSheetView handle these automatically
13289 * for you. @column should be set to the column in @tree_view's model
13290 * containing the tooltip texts, or -1 to disable this feature.
13292 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
13293 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
13295 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
13296 * so &, <, etc have to be escaped in the text.
13301 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
13304 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13306 if (column == tree_view->priv->tooltip_column)
13311 g_signal_handlers_disconnect_by_func (tree_view,
13312 pspp_sheet_view_set_tooltip_query_cb,
13314 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13318 if (tree_view->priv->tooltip_column == -1)
13320 g_signal_connect (tree_view, "query-tooltip",
13321 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13322 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13326 tree_view->priv->tooltip_column = column;
13327 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13331 * pspp_sheet_view_get_tooltip_column:
13332 * @tree_view: a #PsppSheetView
13334 * Returns the column of @tree_view's model which is being used for
13335 * displaying tooltips on @tree_view's rows.
13337 * Return value: the index of the tooltip column that is currently being
13338 * used, or -1 if this is disabled.
13343 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13345 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13347 return tree_view->priv->tooltip_column;
13351 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13352 GValue *return_accu,
13353 const GValue *handler_return,
13356 gboolean continue_emission;
13357 gboolean signal_handled;
13359 signal_handled = g_value_get_boolean (handler_return);
13360 g_value_set_boolean (return_accu, signal_handled);
13361 continue_emission = !signal_handled;
13363 return continue_emission;
13368 pspp_sheet_view_grid_lines_get_type (void)
13370 static GType etype = 0;
13371 if (G_UNLIKELY(etype == 0)) {
13372 static const GEnumValue values[] = {
13373 { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13374 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13375 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13376 { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13379 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13385 pspp_sheet_view_special_cells_get_type (void)
13387 static GType etype = 0;
13388 if (G_UNLIKELY(etype == 0)) {
13389 static const GEnumValue values[] = {
13390 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13391 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13392 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13395 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);