1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012, 2013 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Library General Public
22 * License as published by the Free Software Foundation; either
23 * version 2 of the License, or (at your option) any later version.
25 * This library is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Library General Public License for more details.
30 * You should have received a copy of the GNU Library General Public
31 * License along with this library; if not, write to the
32 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
33 * Boston, MA 02111-1307, USA.
38 #include "ui/gui/pspp-sheet-private.h"
42 #include <gdk/gdkkeysyms.h>
45 #include "ui/gui/psppire-marshal.h"
46 #include "ui/gui/pspp-sheet-selection.h"
48 #define P_(STRING) STRING
49 #define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
50 #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
52 /* Many keyboard shortcuts for Mac are the same as for X
53 * except they use Command key instead of Control (e.g. Cut,
54 * Copy, Paste). This symbol is for those simple cases. */
55 #ifndef GDK_WINDOWING_QUARTZ
56 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_CONTROL_MASK
58 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_META_MASK
61 #define PSPP_SHEET_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
62 #define PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC (PSPP_SHEET_VIEW_PRIORITY_VALIDATE + 2)
63 #define PSPP_SHEET_VIEW_TIME_MS_PER_IDLE 30
64 #define SCROLL_EDGE_SIZE 15
65 #define EXPANDER_EXTRA_PADDING 4
66 #define PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT 5000
68 /* The "background" areas of all rows/cells add up to cover the entire tree.
69 * The background includes all inter-row and inter-cell spacing.
70 * The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
71 * i.e. just the cells, no spacing.
74 #define BACKGROUND_HEIGHT(tree_view) (tree_view->priv->fixed_height)
75 #define CELL_HEIGHT(tree_view, separator) ((BACKGROUND_HEIGHT (tree_view)) - (separator))
77 /* Translate from bin_window coordinates to rbtree (tree coordinates) and
80 #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->priv->dy)
81 #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->priv->dy)
83 /* This is in bin_window coordinates */
84 #define BACKGROUND_FIRST_PIXEL(tree_view,node) (RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, pspp_sheet_view_node_find_offset (tree_view, (node))))
85 #define CELL_FIRST_PIXEL(tree_view,node,separator) (BACKGROUND_FIRST_PIXEL (tree_view,node) + separator/2)
87 #define ROW_HEIGHT(tree_view) \
88 ((tree_view->priv->fixed_height > 0) ? (tree_view->priv->fixed_height) : (tree_view)->priv->expander_size)
91 typedef struct _PsppSheetViewChild PsppSheetViewChild;
92 struct _PsppSheetViewChild
102 typedef struct _TreeViewDragInfo TreeViewDragInfo;
103 struct _TreeViewDragInfo
105 GdkModifierType start_button_mask;
106 GtkTargetList *_unused_source_target_list;
107 GdkDragAction source_actions;
109 GtkTargetList *_unused_dest_target_list;
111 guint source_set : 1;
127 START_INTERACTIVE_SEARCH,
137 PROP_HEADERS_VISIBLE,
138 PROP_HEADERS_CLICKABLE,
143 PROP_HOVER_SELECTION,
145 PROP_ENABLE_GRID_LINES,
149 PROP_FIXED_HEIGHT_SET
153 static void pspp_sheet_view_finalize (GObject *object);
154 static void pspp_sheet_view_set_property (GObject *object,
158 static void pspp_sheet_view_get_property (GObject *object,
163 static void pspp_sheet_view_dispose (GObject *object);
165 /* gtkwidget signals */
166 static void pspp_sheet_view_realize (GtkWidget *widget);
167 static void pspp_sheet_view_unrealize (GtkWidget *widget);
168 static void pspp_sheet_view_map (GtkWidget *widget);
169 static void pspp_sheet_view_size_request (GtkWidget *widget,
170 GtkRequisition *requisition);
171 static void pspp_sheet_view_size_allocate (GtkWidget *widget,
172 GtkAllocation *allocation);
173 static gboolean pspp_sheet_view_expose (GtkWidget *widget,
174 GdkEventExpose *event);
175 static gboolean pspp_sheet_view_key_press (GtkWidget *widget,
177 static gboolean pspp_sheet_view_key_release (GtkWidget *widget,
179 static gboolean pspp_sheet_view_motion (GtkWidget *widget,
180 GdkEventMotion *event);
181 static gboolean pspp_sheet_view_enter_notify (GtkWidget *widget,
182 GdkEventCrossing *event);
183 static gboolean pspp_sheet_view_leave_notify (GtkWidget *widget,
184 GdkEventCrossing *event);
185 static gboolean pspp_sheet_view_button_press (GtkWidget *widget,
186 GdkEventButton *event);
187 static gboolean pspp_sheet_view_button_release (GtkWidget *widget,
188 GdkEventButton *event);
189 static gboolean pspp_sheet_view_grab_broken (GtkWidget *widget,
190 GdkEventGrabBroken *event);
192 static void pspp_sheet_view_set_focus_child (GtkContainer *container,
194 static gint pspp_sheet_view_focus_out (GtkWidget *widget,
195 GdkEventFocus *event);
196 static gint pspp_sheet_view_focus (GtkWidget *widget,
197 GtkDirectionType direction);
198 static void pspp_sheet_view_grab_focus (GtkWidget *widget);
199 static void pspp_sheet_view_style_set (GtkWidget *widget,
200 GtkStyle *previous_style);
201 static void pspp_sheet_view_grab_notify (GtkWidget *widget,
202 gboolean was_grabbed);
203 static void pspp_sheet_view_state_changed (GtkWidget *widget,
204 GtkStateType previous_state);
206 /* container signals */
207 static void pspp_sheet_view_remove (GtkContainer *container,
209 static void pspp_sheet_view_forall (GtkContainer *container,
210 gboolean include_internals,
211 GtkCallback callback,
212 gpointer callback_data);
214 /* Source side drag signals */
215 static void pspp_sheet_view_drag_begin (GtkWidget *widget,
216 GdkDragContext *context);
217 static void pspp_sheet_view_drag_end (GtkWidget *widget,
218 GdkDragContext *context);
219 static void pspp_sheet_view_drag_data_get (GtkWidget *widget,
220 GdkDragContext *context,
221 GtkSelectionData *selection_data,
224 static void pspp_sheet_view_drag_data_delete (GtkWidget *widget,
225 GdkDragContext *context);
227 /* Target side drag signals */
228 static void pspp_sheet_view_drag_leave (GtkWidget *widget,
229 GdkDragContext *context,
231 static gboolean pspp_sheet_view_drag_motion (GtkWidget *widget,
232 GdkDragContext *context,
236 static gboolean pspp_sheet_view_drag_drop (GtkWidget *widget,
237 GdkDragContext *context,
241 static void pspp_sheet_view_drag_data_received (GtkWidget *widget,
242 GdkDragContext *context,
245 GtkSelectionData *selection_data,
249 /* tree_model signals */
250 static void pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
252 GtkAdjustment *vadj);
253 static gboolean pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
254 GtkMovementStep step,
256 static gboolean pspp_sheet_view_real_select_all (PsppSheetView *tree_view);
257 static gboolean pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view);
258 static gboolean pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
259 gboolean start_editing);
260 static gboolean pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view);
261 static void pspp_sheet_view_row_changed (GtkTreeModel *model,
265 static void pspp_sheet_view_row_inserted (GtkTreeModel *model,
269 static void pspp_sheet_view_row_deleted (GtkTreeModel *model,
272 static void pspp_sheet_view_rows_reordered (GtkTreeModel *model,
278 /* Incremental reflow */
279 static gint validate_row (PsppSheetView *tree_view,
283 static void validate_visible_area (PsppSheetView *tree_view);
284 static gboolean validate_rows_handler (PsppSheetView *tree_view);
285 static gboolean presize_handler_callback (gpointer data);
286 static void install_presize_handler (PsppSheetView *tree_view);
287 static void install_scroll_sync_handler (PsppSheetView *tree_view);
288 static void pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
291 static void pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view);
292 static void pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view);
293 static void invalidate_empty_focus (PsppSheetView *tree_view);
295 /* Internal functions */
296 static void pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
299 gboolean add_shifted_binding,
300 GtkMovementStep step,
302 static void pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
304 const GdkRectangle *clip_rect);
305 static gint pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
308 static void pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
309 PsppSheetView *tree_view);
310 static void pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
312 static void pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
313 PsppSheetViewColumn *column,
314 gboolean focus_to_cell);
315 static gboolean pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
316 GdkEventMotion *event);
317 static void pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view);
318 static gboolean pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
320 static void pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
322 static void pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
324 static void pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
326 static void pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
328 static void pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
330 static void pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
332 gboolean clear_and_select,
333 gboolean clamp_node);
334 static gboolean pspp_sheet_view_has_special_cell (PsppSheetView *tree_view);
335 static void pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view);
336 static void update_prelight (PsppSheetView *tree_view,
339 static void initialize_fixed_height_mode (PsppSheetView *tree_view);
341 /* interactive search */
342 static void pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view);
343 static void pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
344 PsppSheetView *tree_view);
345 static void pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
346 GtkWidget *search_dialog,
348 static void pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
351 static void pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
352 PsppSheetView *tree_view);
353 static void pspp_sheet_view_search_activate (GtkEntry *entry,
354 PsppSheetView *tree_view);
355 static gboolean pspp_sheet_view_real_search_enable_popdown(gpointer data);
356 static void pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
358 static gboolean pspp_sheet_view_search_delete_event (GtkWidget *widget,
360 PsppSheetView *tree_view);
361 static gboolean pspp_sheet_view_search_button_press_event (GtkWidget *widget,
362 GdkEventButton *event,
363 PsppSheetView *tree_view);
364 static gboolean pspp_sheet_view_search_scroll_event (GtkWidget *entry,
365 GdkEventScroll *event,
366 PsppSheetView *tree_view);
367 static gboolean pspp_sheet_view_search_key_press_event (GtkWidget *entry,
369 PsppSheetView *tree_view);
370 static gboolean pspp_sheet_view_search_move (GtkWidget *window,
371 PsppSheetView *tree_view,
373 static gboolean pspp_sheet_view_search_equal_func (GtkTreeModel *model,
377 gpointer search_data);
378 static gboolean pspp_sheet_view_search_iter (GtkTreeModel *model,
379 PsppSheetSelection *selection,
384 static void pspp_sheet_view_search_init (GtkWidget *entry,
385 PsppSheetView *tree_view);
386 static void pspp_sheet_view_put (PsppSheetView *tree_view,
387 GtkWidget *child_widget,
392 static gboolean pspp_sheet_view_start_editing (PsppSheetView *tree_view,
393 GtkTreePath *cursor_path);
394 static gboolean pspp_sheet_view_editable_button_press_event (GtkWidget *,
397 static void pspp_sheet_view_editable_clicked (GtkButton *, PsppSheetView *);
398 static void pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
399 PsppSheetViewColumn *column,
401 GtkCellEditable *cell_editable,
402 GdkRectangle *cell_area,
405 static gboolean pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
406 gboolean keybinding);
407 static gboolean pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view);
408 static PsppSheetViewColumn *pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
409 PsppSheetViewColumn *column,
412 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
413 PsppSheetViewColumn *column,
414 const GdkRectangle *background_area,
415 gboolean subtract_focus_rect,
416 GdkRectangle *cell_area);
417 static gint pspp_sheet_view_find_offset (PsppSheetView *tree_view,
422 static void pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
426 static void pspp_sheet_view_buildable_init (GtkBuildableIface *iface);
429 static gboolean scroll_row_timeout (gpointer data);
430 static void add_scroll_timeout (PsppSheetView *tree_view);
431 static void remove_scroll_timeout (PsppSheetView *tree_view);
433 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
435 static GtkBindingSet *edit_bindings;
442 G_DEFINE_TYPE_WITH_CODE (PsppSheetView, pspp_sheet_view, GTK_TYPE_CONTAINER,
443 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
444 pspp_sheet_view_buildable_init))
447 pspp_sheet_view_class_init (PsppSheetViewClass *class)
449 GObjectClass *o_class;
450 GtkWidgetClass *widget_class;
451 GtkContainerClass *container_class;
452 GtkBindingSet *binding_set[2];
455 binding_set[0] = gtk_binding_set_by_class (class);
457 binding_set[1] = gtk_binding_set_new ("PsppSheetViewEditing");
458 edit_bindings = binding_set[1];
460 o_class = (GObjectClass *) class;
461 widget_class = (GtkWidgetClass *) class;
462 container_class = (GtkContainerClass *) class;
464 /* GObject signals */
465 o_class->set_property = pspp_sheet_view_set_property;
466 o_class->get_property = pspp_sheet_view_get_property;
467 o_class->finalize = pspp_sheet_view_finalize;
468 o_class->dispose = pspp_sheet_view_dispose;
470 /* GtkWidget signals */
471 widget_class->map = pspp_sheet_view_map;
472 widget_class->realize = pspp_sheet_view_realize;
473 widget_class->unrealize = pspp_sheet_view_unrealize;
474 widget_class->size_request = pspp_sheet_view_size_request;
475 widget_class->size_allocate = pspp_sheet_view_size_allocate;
476 widget_class->button_press_event = pspp_sheet_view_button_press;
477 widget_class->button_release_event = pspp_sheet_view_button_release;
478 widget_class->grab_broken_event = pspp_sheet_view_grab_broken;
479 /*widget_class->configure_event = pspp_sheet_view_configure;*/
480 widget_class->motion_notify_event = pspp_sheet_view_motion;
481 widget_class->expose_event = pspp_sheet_view_expose;
482 widget_class->key_press_event = pspp_sheet_view_key_press;
483 widget_class->key_release_event = pspp_sheet_view_key_release;
484 widget_class->enter_notify_event = pspp_sheet_view_enter_notify;
485 widget_class->leave_notify_event = pspp_sheet_view_leave_notify;
486 widget_class->focus_out_event = pspp_sheet_view_focus_out;
487 widget_class->drag_begin = pspp_sheet_view_drag_begin;
488 widget_class->drag_end = pspp_sheet_view_drag_end;
489 widget_class->drag_data_get = pspp_sheet_view_drag_data_get;
490 widget_class->drag_data_delete = pspp_sheet_view_drag_data_delete;
491 widget_class->drag_leave = pspp_sheet_view_drag_leave;
492 widget_class->drag_motion = pspp_sheet_view_drag_motion;
493 widget_class->drag_drop = pspp_sheet_view_drag_drop;
494 widget_class->drag_data_received = pspp_sheet_view_drag_data_received;
495 widget_class->focus = pspp_sheet_view_focus;
496 widget_class->grab_focus = pspp_sheet_view_grab_focus;
497 widget_class->style_set = pspp_sheet_view_style_set;
498 widget_class->grab_notify = pspp_sheet_view_grab_notify;
499 widget_class->state_changed = pspp_sheet_view_state_changed;
501 /* GtkContainer signals */
502 container_class->remove = pspp_sheet_view_remove;
503 container_class->forall = pspp_sheet_view_forall;
504 container_class->set_focus_child = pspp_sheet_view_set_focus_child;
506 class->set_scroll_adjustments = pspp_sheet_view_set_adjustments;
507 class->move_cursor = pspp_sheet_view_real_move_cursor;
508 class->select_all = pspp_sheet_view_real_select_all;
509 class->unselect_all = pspp_sheet_view_real_unselect_all;
510 class->select_cursor_row = pspp_sheet_view_real_select_cursor_row;
511 class->toggle_cursor_row = pspp_sheet_view_real_toggle_cursor_row;
512 class->start_interactive_search = pspp_sheet_view_start_interactive_search;
516 g_object_class_install_property (o_class,
518 g_param_spec_object ("model",
519 P_("TreeView Model"),
520 P_("The model for the tree view"),
522 GTK_PARAM_READWRITE));
524 g_object_class_install_property (o_class,
526 g_param_spec_object ("hadjustment",
527 P_("Horizontal Adjustment"),
528 P_("Horizontal Adjustment for the widget"),
530 GTK_PARAM_READWRITE));
532 g_object_class_install_property (o_class,
534 g_param_spec_object ("vadjustment",
535 P_("Vertical Adjustment"),
536 P_("Vertical Adjustment for the widget"),
538 GTK_PARAM_READWRITE));
540 g_object_class_install_property (o_class,
541 PROP_HEADERS_VISIBLE,
542 g_param_spec_boolean ("headers-visible",
543 P_("Headers Visible"),
544 P_("Show the column header buttons"),
546 GTK_PARAM_READWRITE));
548 g_object_class_install_property (o_class,
549 PROP_HEADERS_CLICKABLE,
550 g_param_spec_boolean ("headers-clickable",
551 P_("Headers Clickable"),
552 P_("Column headers respond to click events"),
554 GTK_PARAM_READWRITE));
556 g_object_class_install_property (o_class,
558 g_param_spec_boolean ("reorderable",
560 P_("View is reorderable"),
562 GTK_PARAM_READWRITE));
564 g_object_class_install_property (o_class,
566 g_param_spec_boolean ("rules-hint",
568 P_("Set a hint to the theme engine to draw rows in alternating colors"),
570 GTK_PARAM_READWRITE));
572 g_object_class_install_property (o_class,
574 g_param_spec_boolean ("enable-search",
576 P_("View allows user to search through columns interactively"),
578 GTK_PARAM_READWRITE));
580 g_object_class_install_property (o_class,
582 g_param_spec_int ("search-column",
584 P_("Model column to search through during interactive search"),
588 GTK_PARAM_READWRITE));
591 * PsppSheetView:hover-selection:
593 * Enables of disables the hover selection mode of @tree_view.
594 * Hover selection makes the selected row follow the pointer.
595 * Currently, this works only for the selection modes
596 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
598 * This mode is primarily intended for treeviews in popups, e.g.
599 * in #GtkComboBox or #GtkEntryCompletion.
603 g_object_class_install_property (o_class,
604 PROP_HOVER_SELECTION,
605 g_param_spec_boolean ("hover-selection",
606 P_("Hover Selection"),
607 P_("Whether the selection should follow the pointer"),
609 GTK_PARAM_READWRITE));
611 g_object_class_install_property (o_class,
613 g_param_spec_boolean ("rubber-banding",
614 P_("Rubber Banding"),
615 P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
617 GTK_PARAM_READWRITE));
619 g_object_class_install_property (o_class,
620 PROP_ENABLE_GRID_LINES,
621 g_param_spec_enum ("enable-grid-lines",
622 P_("Enable Grid Lines"),
623 P_("Whether grid lines should be drawn in the tree view"),
624 PSPP_TYPE_SHEET_VIEW_GRID_LINES,
625 PSPP_SHEET_VIEW_GRID_LINES_NONE,
626 GTK_PARAM_READWRITE));
628 g_object_class_install_property (o_class,
630 g_param_spec_int ("tooltip-column",
631 P_("Tooltip Column"),
632 P_("The column in the model containing the tooltip texts for the rows"),
636 GTK_PARAM_READWRITE));
638 g_object_class_install_property (o_class,
640 g_param_spec_enum ("special-cells",
642 P_("Whether rows have special cells."),
643 PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS,
644 PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT,
645 GTK_PARAM_READWRITE));
647 g_object_class_install_property (o_class,
649 g_param_spec_int ("fixed-height",
651 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."),
655 GTK_PARAM_READWRITE));
657 g_object_class_install_property (o_class,
658 PROP_FIXED_HEIGHT_SET,
659 g_param_spec_boolean ("fixed-height-set",
660 P_("Fixed Height Set"),
661 P_("Whether fixed-height was set externally."),
663 GTK_PARAM_READWRITE));
665 /* Style properties */
666 #define _TREE_VIEW_EXPANDER_SIZE 12
667 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
668 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
670 gtk_widget_class_install_style_property (widget_class,
671 g_param_spec_int ("expander-size",
673 P_("Size of the expander arrow"),
676 _TREE_VIEW_EXPANDER_SIZE,
677 GTK_PARAM_READABLE));
679 gtk_widget_class_install_style_property (widget_class,
680 g_param_spec_int ("vertical-separator",
681 P_("Vertical Separator Width"),
682 P_("Vertical space between cells. Must be an even number"),
685 _TREE_VIEW_VERTICAL_SEPARATOR,
686 GTK_PARAM_READABLE));
688 gtk_widget_class_install_style_property (widget_class,
689 g_param_spec_int ("horizontal-separator",
690 P_("Horizontal Separator Width"),
691 P_("Horizontal space between cells. Must be an even number"),
694 _TREE_VIEW_HORIZONTAL_SEPARATOR,
695 GTK_PARAM_READABLE));
697 gtk_widget_class_install_style_property (widget_class,
698 g_param_spec_boolean ("allow-rules",
700 P_("Allow drawing of alternating color rows"),
702 GTK_PARAM_READABLE));
704 gtk_widget_class_install_style_property (widget_class,
705 g_param_spec_boxed ("even-row-color",
706 P_("Even Row Color"),
707 P_("Color to use for even rows"),
709 GTK_PARAM_READABLE));
711 gtk_widget_class_install_style_property (widget_class,
712 g_param_spec_boxed ("odd-row-color",
714 P_("Color to use for odd rows"),
716 GTK_PARAM_READABLE));
718 gtk_widget_class_install_style_property (widget_class,
719 g_param_spec_boolean ("row-ending-details",
720 P_("Row Ending details"),
721 P_("Enable extended row background theming"),
723 GTK_PARAM_READABLE));
725 gtk_widget_class_install_style_property (widget_class,
726 g_param_spec_int ("grid-line-width",
727 P_("Grid line width"),
728 P_("Width, in pixels, of the tree view grid lines"),
730 GTK_PARAM_READABLE));
732 gtk_widget_class_install_style_property (widget_class,
733 g_param_spec_int ("tree-line-width",
734 P_("Tree line width"),
735 P_("Width, in pixels, of the tree view lines"),
737 GTK_PARAM_READABLE));
739 gtk_widget_class_install_style_property (widget_class,
740 g_param_spec_string ("tree-line-pattern",
741 P_("Tree line pattern"),
742 P_("Dash pattern used to draw the tree view lines"),
744 GTK_PARAM_READABLE));
748 * PsppSheetView::set-scroll-adjustments
749 * @horizontal: the horizontal #GtkAdjustment
750 * @vertical: the vertical #GtkAdjustment
752 * Set the scroll adjustments for the tree view. Usually scrolled containers
753 * like #GtkScrolledWindow will emit this signal to connect two instances
754 * of #GtkScrollbar to the scroll directions of the #PsppSheetView.
756 widget_class->set_scroll_adjustments_signal =
757 g_signal_new ("set-scroll-adjustments",
758 G_TYPE_FROM_CLASS (o_class),
759 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
760 G_STRUCT_OFFSET (PsppSheetViewClass, set_scroll_adjustments),
762 psppire_marshal_VOID__OBJECT_OBJECT,
765 GTK_TYPE_ADJUSTMENT);
768 * PsppSheetView::row-activated:
769 * @tree_view: the object on which the signal is emitted
770 * @path: the #GtkTreePath for the activated row
771 * @column: the #PsppSheetViewColumn in which the activation occurred
773 * The "row-activated" signal is emitted when the method
774 * pspp_sheet_view_row_activated() is called or the user double clicks
775 * a treeview row. It is also emitted when a non-editable row is
776 * selected and one of the keys: Space, Shift+Space, Return or
779 * For selection handling refer to the <link linkend="TreeWidget">tree
780 * widget conceptual overview</link> as well as #PsppSheetSelection.
782 tree_view_signals[ROW_ACTIVATED] =
783 g_signal_new ("row-activated",
784 G_TYPE_FROM_CLASS (o_class),
785 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
786 G_STRUCT_OFFSET (PsppSheetViewClass, row_activated),
788 psppire_marshal_VOID__BOXED_OBJECT,
791 PSPP_TYPE_SHEET_VIEW_COLUMN);
794 * PsppSheetView::columns-changed:
795 * @tree_view: the object on which the signal is emitted
797 * The number of columns of the treeview has changed.
799 tree_view_signals[COLUMNS_CHANGED] =
800 g_signal_new ("columns-changed",
801 G_TYPE_FROM_CLASS (o_class),
803 G_STRUCT_OFFSET (PsppSheetViewClass, columns_changed),
805 g_cclosure_marshal_VOID__VOID,
809 * PsppSheetView::cursor-changed:
810 * @tree_view: the object on which the signal is emitted
812 * The position of the cursor (focused cell) has changed.
814 tree_view_signals[CURSOR_CHANGED] =
815 g_signal_new ("cursor-changed",
816 G_TYPE_FROM_CLASS (o_class),
818 G_STRUCT_OFFSET (PsppSheetViewClass, cursor_changed),
820 g_cclosure_marshal_VOID__VOID,
823 tree_view_signals[MOVE_CURSOR] =
824 g_signal_new ("move-cursor",
825 G_TYPE_FROM_CLASS (o_class),
826 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
827 G_STRUCT_OFFSET (PsppSheetViewClass, move_cursor),
829 psppire_marshal_BOOLEAN__ENUM_INT,
831 GTK_TYPE_MOVEMENT_STEP,
834 tree_view_signals[SELECT_ALL] =
835 g_signal_new ("select-all",
836 G_TYPE_FROM_CLASS (o_class),
837 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
838 G_STRUCT_OFFSET (PsppSheetViewClass, select_all),
840 psppire_marshal_BOOLEAN__VOID,
843 tree_view_signals[UNSELECT_ALL] =
844 g_signal_new ("unselect-all",
845 G_TYPE_FROM_CLASS (o_class),
846 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
847 G_STRUCT_OFFSET (PsppSheetViewClass, unselect_all),
849 psppire_marshal_BOOLEAN__VOID,
852 tree_view_signals[SELECT_CURSOR_ROW] =
853 g_signal_new ("select-cursor-row",
854 G_TYPE_FROM_CLASS (o_class),
855 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
856 G_STRUCT_OFFSET (PsppSheetViewClass, select_cursor_row),
858 psppire_marshal_BOOLEAN__BOOLEAN,
862 tree_view_signals[TOGGLE_CURSOR_ROW] =
863 g_signal_new ("toggle-cursor-row",
864 G_TYPE_FROM_CLASS (o_class),
865 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
866 G_STRUCT_OFFSET (PsppSheetViewClass, toggle_cursor_row),
868 psppire_marshal_BOOLEAN__VOID,
871 tree_view_signals[START_INTERACTIVE_SEARCH] =
872 g_signal_new ("start-interactive-search",
873 G_TYPE_FROM_CLASS (o_class),
874 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
875 G_STRUCT_OFFSET (PsppSheetViewClass, start_interactive_search),
877 psppire_marshal_BOOLEAN__VOID,
881 for (i = 0; i < 2; i++)
883 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Up, 0, TRUE,
884 GTK_MOVEMENT_DISPLAY_LINES, -1);
885 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Up, 0, TRUE,
886 GTK_MOVEMENT_DISPLAY_LINES, -1);
888 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Down, 0, TRUE,
889 GTK_MOVEMENT_DISPLAY_LINES, 1);
890 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Down, 0, TRUE,
891 GTK_MOVEMENT_DISPLAY_LINES, 1);
893 pspp_sheet_view_add_move_binding (binding_set[i], GDK_p, GDK_CONTROL_MASK, FALSE,
894 GTK_MOVEMENT_DISPLAY_LINES, -1);
896 pspp_sheet_view_add_move_binding (binding_set[i], GDK_n, GDK_CONTROL_MASK, FALSE,
897 GTK_MOVEMENT_DISPLAY_LINES, 1);
899 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Home, 0, TRUE,
900 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
901 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Home, 0, TRUE,
902 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
904 pspp_sheet_view_add_move_binding (binding_set[i], GDK_End, 0, TRUE,
905 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
906 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_End, 0, TRUE,
907 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
909 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Up, 0, TRUE,
910 GTK_MOVEMENT_PAGES, -1);
911 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Up, 0, TRUE,
912 GTK_MOVEMENT_PAGES, -1);
914 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Down, 0, TRUE,
915 GTK_MOVEMENT_PAGES, 1);
916 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Down, 0, TRUE,
917 GTK_MOVEMENT_PAGES, 1);
920 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, 0, "move-cursor", 2,
921 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
924 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, 0, "move-cursor", 2,
925 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
928 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, 0, "move-cursor", 2,
929 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
932 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, GDK_SHIFT_MASK, "move-cursor", 2,
933 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
936 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, 0, "move-cursor", 2,
937 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
940 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, 0, "move-cursor", 2,
941 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
944 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, GDK_CONTROL_MASK,
946 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
949 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, GDK_CONTROL_MASK,
951 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
954 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, GDK_CONTROL_MASK,
956 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
959 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, GDK_CONTROL_MASK,
961 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
964 gtk_binding_entry_add_signal (binding_set[i], GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
966 gtk_binding_entry_add_signal (binding_set[i], GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
969 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
970 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
972 gtk_binding_entry_add_signal (binding_set[0], GDK_a, GDK_CONTROL_MASK, "select-all", 0);
973 gtk_binding_entry_add_signal (binding_set[0], GDK_slash, GDK_CONTROL_MASK, "select-all", 0);
975 gtk_binding_entry_add_signal (binding_set[0], GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
976 gtk_binding_entry_add_signal (binding_set[0], GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0);
978 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1,
979 G_TYPE_BOOLEAN, TRUE);
980 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1,
981 G_TYPE_BOOLEAN, TRUE);
983 gtk_binding_entry_add_signal (binding_set[0], GDK_space, 0, "select-cursor-row", 1,
984 G_TYPE_BOOLEAN, TRUE);
985 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, 0, "select-cursor-row", 1,
986 G_TYPE_BOOLEAN, TRUE);
987 gtk_binding_entry_add_signal (binding_set[0], GDK_Return, 0, "select-cursor-row", 1,
988 G_TYPE_BOOLEAN, TRUE);
989 gtk_binding_entry_add_signal (binding_set[0], GDK_ISO_Enter, 0, "select-cursor-row", 1,
990 G_TYPE_BOOLEAN, TRUE);
991 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Enter, 0, "select-cursor-row", 1,
992 G_TYPE_BOOLEAN, TRUE);
994 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, 0, "select-cursor-parent", 0);
995 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0);
997 g_type_class_add_private (o_class, sizeof (PsppSheetViewPrivate));
1001 pspp_sheet_view_buildable_init (GtkBuildableIface *iface)
1003 iface->add_child = pspp_sheet_view_buildable_add_child;
1007 pspp_sheet_view_init (PsppSheetView *tree_view)
1009 tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, PSPP_TYPE_SHEET_VIEW, PsppSheetViewPrivate);
1011 gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
1012 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE);
1014 tree_view->priv->flags = PSPP_SHEET_VIEW_DRAW_KEYFOCUS
1015 | PSPP_SHEET_VIEW_HEADERS_VISIBLE;
1017 /* We need some padding */
1018 tree_view->priv->selected = range_tower_create ();
1019 tree_view->priv->dy = 0;
1020 tree_view->priv->cursor_offset = 0;
1021 tree_view->priv->n_columns = 0;
1022 tree_view->priv->header_height = 1;
1023 tree_view->priv->x_drag = 0;
1024 tree_view->priv->drag_pos = -1;
1025 tree_view->priv->header_has_focus = FALSE;
1026 tree_view->priv->pressed_button = -1;
1027 tree_view->priv->press_start_x = -1;
1028 tree_view->priv->press_start_y = -1;
1029 tree_view->priv->reorderable = FALSE;
1030 tree_view->priv->presize_handler_timer = 0;
1031 tree_view->priv->scroll_sync_timer = 0;
1032 tree_view->priv->fixed_height = -1;
1033 tree_view->priv->fixed_height_set = FALSE;
1034 pspp_sheet_view_set_adjustments (tree_view, NULL, NULL);
1035 tree_view->priv->selection = _pspp_sheet_selection_new_with_tree_view (tree_view);
1036 tree_view->priv->enable_search = TRUE;
1037 tree_view->priv->search_column = -1;
1038 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
1039 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
1040 tree_view->priv->search_custom_entry_set = FALSE;
1041 tree_view->priv->typeselect_flush_timeout = 0;
1042 tree_view->priv->init_hadjust_value = TRUE;
1043 tree_view->priv->width = 0;
1045 tree_view->priv->hover_selection = FALSE;
1047 tree_view->priv->rubber_banding_enable = FALSE;
1049 tree_view->priv->grid_lines = PSPP_SHEET_VIEW_GRID_LINES_NONE;
1051 tree_view->priv->tooltip_column = -1;
1053 tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT;
1055 tree_view->priv->post_validation_flag = FALSE;
1057 tree_view->priv->last_button_x = -1;
1058 tree_view->priv->last_button_y = -1;
1060 tree_view->priv->event_last_x = -10000;
1061 tree_view->priv->event_last_y = -10000;
1063 tree_view->priv->prelight_node = -1;
1064 tree_view->priv->rubber_band_start_node = -1;
1065 tree_view->priv->rubber_band_end_node = -1;
1067 tree_view->priv->anchor_column = NULL;
1069 tree_view->priv->button_style = NULL;
1071 tree_view->dispose_has_run = FALSE;
1080 pspp_sheet_view_set_property (GObject *object,
1082 const GValue *value,
1085 PsppSheetView *tree_view;
1087 tree_view = PSPP_SHEET_VIEW (object);
1092 pspp_sheet_view_set_model (tree_view, g_value_get_object (value));
1094 case PROP_HADJUSTMENT:
1095 pspp_sheet_view_set_hadjustment (tree_view, g_value_get_object (value));
1097 case PROP_VADJUSTMENT:
1098 pspp_sheet_view_set_vadjustment (tree_view, g_value_get_object (value));
1100 case PROP_HEADERS_VISIBLE:
1101 pspp_sheet_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1103 case PROP_HEADERS_CLICKABLE:
1104 pspp_sheet_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1106 case PROP_REORDERABLE:
1107 pspp_sheet_view_set_reorderable (tree_view, g_value_get_boolean (value));
1109 case PROP_RULES_HINT:
1110 pspp_sheet_view_set_rules_hint (tree_view, g_value_get_boolean (value));
1112 case PROP_ENABLE_SEARCH:
1113 pspp_sheet_view_set_enable_search (tree_view, g_value_get_boolean (value));
1115 case PROP_SEARCH_COLUMN:
1116 pspp_sheet_view_set_search_column (tree_view, g_value_get_int (value));
1118 case PROP_HOVER_SELECTION:
1119 tree_view->priv->hover_selection = g_value_get_boolean (value);
1121 case PROP_RUBBER_BANDING:
1122 tree_view->priv->rubber_banding_enable = g_value_get_boolean (value);
1124 case PROP_ENABLE_GRID_LINES:
1125 pspp_sheet_view_set_grid_lines (tree_view, g_value_get_enum (value));
1127 case PROP_TOOLTIP_COLUMN:
1128 pspp_sheet_view_set_tooltip_column (tree_view, g_value_get_int (value));
1130 case PROP_SPECIAL_CELLS:
1131 pspp_sheet_view_set_special_cells (tree_view, g_value_get_enum (value));
1133 case PROP_FIXED_HEIGHT:
1134 pspp_sheet_view_set_fixed_height (tree_view, g_value_get_int (value));
1136 case PROP_FIXED_HEIGHT_SET:
1137 if (g_value_get_boolean (value))
1139 if (!tree_view->priv->fixed_height_set
1140 && tree_view->priv->fixed_height >= 0)
1142 tree_view->priv->fixed_height_set = true;
1143 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1148 if (tree_view->priv->fixed_height_set)
1150 tree_view->priv->fixed_height_set = false;
1151 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1152 install_presize_handler (tree_view);
1157 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1163 pspp_sheet_view_get_property (GObject *object,
1168 PsppSheetView *tree_view;
1170 tree_view = PSPP_SHEET_VIEW (object);
1175 g_value_set_object (value, tree_view->priv->model);
1177 case PROP_HADJUSTMENT:
1178 g_value_set_object (value, tree_view->priv->hadjustment);
1180 case PROP_VADJUSTMENT:
1181 g_value_set_object (value, tree_view->priv->vadjustment);
1183 case PROP_HEADERS_VISIBLE:
1184 g_value_set_boolean (value, pspp_sheet_view_get_headers_visible (tree_view));
1186 case PROP_HEADERS_CLICKABLE:
1187 g_value_set_boolean (value, pspp_sheet_view_get_headers_clickable (tree_view));
1189 case PROP_REORDERABLE:
1190 g_value_set_boolean (value, tree_view->priv->reorderable);
1192 case PROP_RULES_HINT:
1193 g_value_set_boolean (value, tree_view->priv->has_rules);
1195 case PROP_ENABLE_SEARCH:
1196 g_value_set_boolean (value, tree_view->priv->enable_search);
1198 case PROP_SEARCH_COLUMN:
1199 g_value_set_int (value, tree_view->priv->search_column);
1201 case PROP_HOVER_SELECTION:
1202 g_value_set_boolean (value, tree_view->priv->hover_selection);
1204 case PROP_RUBBER_BANDING:
1205 g_value_set_boolean (value, tree_view->priv->rubber_banding_enable);
1207 case PROP_ENABLE_GRID_LINES:
1208 g_value_set_enum (value, tree_view->priv->grid_lines);
1210 case PROP_TOOLTIP_COLUMN:
1211 g_value_set_int (value, tree_view->priv->tooltip_column);
1213 case PROP_SPECIAL_CELLS:
1214 g_value_set_enum (value, tree_view->priv->special_cells);
1216 case PROP_FIXED_HEIGHT:
1217 g_value_set_int (value, pspp_sheet_view_get_fixed_height (tree_view));
1219 case PROP_FIXED_HEIGHT_SET:
1220 g_value_set_boolean (value, tree_view->priv->fixed_height_set);
1223 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1229 pspp_sheet_view_dispose (GObject *object)
1231 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1233 if (tree_view->dispose_has_run)
1236 tree_view->dispose_has_run = TRUE;
1238 if (tree_view->priv->selection != NULL)
1240 _pspp_sheet_selection_set_tree_view (tree_view->priv->selection, NULL);
1241 g_object_unref (tree_view->priv->selection);
1242 tree_view->priv->selection = NULL;
1245 if (tree_view->priv->hadjustment)
1247 g_object_unref (tree_view->priv->hadjustment);
1248 tree_view->priv->hadjustment = NULL;
1250 if (tree_view->priv->vadjustment)
1252 g_object_unref (tree_view->priv->vadjustment);
1253 tree_view->priv->vadjustment = NULL;
1256 if (tree_view->priv->button_style)
1258 g_object_unref (tree_view->priv->button_style);
1259 tree_view->priv->button_style = NULL;
1263 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->dispose (object);
1269 pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
1270 GtkBuilder *builder,
1274 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), PSPP_SHEET_VIEW_COLUMN (child));
1278 pspp_sheet_view_finalize (GObject *object)
1280 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1282 pspp_sheet_view_stop_editing (tree_view, TRUE);
1284 if (tree_view->priv->selected != NULL)
1286 range_tower_destroy (tree_view->priv->selected);
1287 tree_view->priv->selected = NULL;
1291 tree_view->priv->prelight_node = -1;
1294 if (tree_view->priv->scroll_to_path != NULL)
1296 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
1297 tree_view->priv->scroll_to_path = NULL;
1300 if (tree_view->priv->drag_dest_row != NULL)
1302 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
1303 tree_view->priv->drag_dest_row = NULL;
1306 if (tree_view->priv->top_row != NULL)
1308 gtk_tree_row_reference_free (tree_view->priv->top_row);
1309 tree_view->priv->top_row = NULL;
1312 if (tree_view->priv->column_drop_func_data &&
1313 tree_view->priv->column_drop_func_data_destroy)
1315 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
1316 tree_view->priv->column_drop_func_data = NULL;
1319 if (tree_view->priv->destroy_count_destroy &&
1320 tree_view->priv->destroy_count_data)
1322 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
1323 tree_view->priv->destroy_count_data = NULL;
1326 gtk_tree_row_reference_free (tree_view->priv->cursor);
1327 tree_view->priv->cursor = NULL;
1329 gtk_tree_row_reference_free (tree_view->priv->anchor);
1330 tree_view->priv->anchor = NULL;
1332 /* destroy interactive search dialog */
1333 if (tree_view->priv->search_window)
1335 gtk_widget_destroy (tree_view->priv->search_window);
1336 tree_view->priv->search_window = NULL;
1337 tree_view->priv->search_entry = NULL;
1338 if (tree_view->priv->typeselect_flush_timeout)
1340 g_source_remove (tree_view->priv->typeselect_flush_timeout);
1341 tree_view->priv->typeselect_flush_timeout = 0;
1345 if (tree_view->priv->search_destroy && tree_view->priv->search_user_data)
1347 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
1348 tree_view->priv->search_user_data = NULL;
1351 if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data)
1353 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
1354 tree_view->priv->search_position_user_data = NULL;
1357 pspp_sheet_view_set_model (tree_view, NULL);
1360 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->finalize (object);
1365 /* GtkWidget Methods
1368 /* GtkWidget::map helper */
1370 pspp_sheet_view_map_buttons (PsppSheetView *tree_view)
1374 g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
1376 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
1378 PsppSheetViewColumn *column;
1380 for (list = tree_view->priv->columns; list; list = list->next)
1382 column = list->data;
1383 if (column->button != NULL &&
1384 gtk_widget_get_visible (column->button) &&
1385 !gtk_widget_get_mapped (column->button))
1386 gtk_widget_map (column->button);
1388 for (list = tree_view->priv->columns; list; list = list->next)
1390 column = list->data;
1391 if (column->visible == FALSE || column->window == NULL)
1393 if (column->resizable)
1395 gdk_window_raise (column->window);
1396 gdk_window_show (column->window);
1399 gdk_window_hide (column->window);
1401 gdk_window_show (tree_view->priv->header_window);
1406 pspp_sheet_view_map (GtkWidget *widget)
1408 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1411 gtk_widget_set_mapped (widget, TRUE);
1413 tmp_list = tree_view->priv->children;
1416 PsppSheetViewChild *child = tmp_list->data;
1417 tmp_list = tmp_list->next;
1419 if (gtk_widget_get_visible (child->widget))
1421 if (!gtk_widget_get_mapped (child->widget))
1422 gtk_widget_map (child->widget);
1425 gdk_window_show (tree_view->priv->bin_window);
1427 pspp_sheet_view_map_buttons (tree_view);
1429 gdk_window_show (widget->window);
1433 pspp_sheet_view_realize (GtkWidget *widget)
1436 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1438 GdkWindowAttr attributes;
1439 gint attributes_mask;
1441 gtk_widget_set_realized (widget, TRUE);
1443 /* Make the main, clipping window */
1444 attributes.window_type = GDK_WINDOW_CHILD;
1445 attributes.x = widget->allocation.x;
1446 attributes.y = widget->allocation.y;
1447 attributes.width = widget->allocation.width;
1448 attributes.height = widget->allocation.height;
1449 attributes.wclass = GDK_INPUT_OUTPUT;
1450 attributes.visual = gtk_widget_get_visual (widget);
1451 attributes.colormap = gtk_widget_get_colormap (widget);
1452 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1454 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1456 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1457 &attributes, attributes_mask);
1458 gdk_window_set_user_data (widget->window, widget);
1460 /* Make the window for the tree */
1462 attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
1463 attributes.width = MAX (tree_view->priv->width, widget->allocation.width);
1464 attributes.height = widget->allocation.height;
1465 attributes.event_mask = (GDK_EXPOSURE_MASK |
1467 GDK_POINTER_MOTION_MASK |
1468 GDK_ENTER_NOTIFY_MASK |
1469 GDK_LEAVE_NOTIFY_MASK |
1470 GDK_BUTTON_PRESS_MASK |
1471 GDK_BUTTON_RELEASE_MASK |
1472 gtk_widget_get_events (widget));
1474 tree_view->priv->bin_window = gdk_window_new (widget->window,
1475 &attributes, attributes_mask);
1476 gdk_window_set_user_data (tree_view->priv->bin_window, widget);
1478 /* Make the column header window */
1481 attributes.width = MAX (tree_view->priv->width, widget->allocation.width);
1482 attributes.height = tree_view->priv->header_height;
1483 attributes.event_mask = (GDK_EXPOSURE_MASK |
1485 GDK_BUTTON_PRESS_MASK |
1486 GDK_BUTTON_RELEASE_MASK |
1487 GDK_KEY_PRESS_MASK |
1488 GDK_KEY_RELEASE_MASK |
1489 gtk_widget_get_events (widget));
1491 tree_view->priv->header_window = gdk_window_new (widget->window,
1492 &attributes, attributes_mask);
1493 gdk_window_set_user_data (tree_view->priv->header_window, widget);
1495 /* Add them all up. */
1496 widget->style = gtk_style_attach (widget->style, widget->window);
1497 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
1498 gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
1499 gtk_style_set_background (widget->style, tree_view->priv->header_window, GTK_STATE_NORMAL);
1501 tmp_list = tree_view->priv->children;
1504 PsppSheetViewChild *child = tmp_list->data;
1505 tmp_list = tmp_list->next;
1507 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
1510 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
1511 _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list->data));
1513 /* Need to call those here, since they create GCs */
1514 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
1516 install_presize_handler (tree_view);
1518 for (i = 0; i < 5; ++i)
1520 tree_view->priv->grid_line_gc[i] = gdk_gc_new (widget->window);
1521 gdk_gc_copy (tree_view->priv->grid_line_gc[i], widget->style->text_aa_gc[i]);
1526 pspp_sheet_view_unrealize (GtkWidget *widget)
1529 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1530 PsppSheetViewPrivate *priv = tree_view->priv;
1533 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget);
1535 if (priv->scroll_timeout != 0)
1537 g_source_remove (priv->scroll_timeout);
1538 priv->scroll_timeout = 0;
1541 if (priv->open_dest_timeout != 0)
1543 g_source_remove (priv->open_dest_timeout);
1544 priv->open_dest_timeout = 0;
1547 if (priv->presize_handler_timer != 0)
1549 g_source_remove (priv->presize_handler_timer);
1550 priv->presize_handler_timer = 0;
1553 if (priv->validate_rows_timer != 0)
1555 g_source_remove (priv->validate_rows_timer);
1556 priv->validate_rows_timer = 0;
1559 if (priv->scroll_sync_timer != 0)
1561 g_source_remove (priv->scroll_sync_timer);
1562 priv->scroll_sync_timer = 0;
1565 if (priv->typeselect_flush_timeout)
1567 g_source_remove (priv->typeselect_flush_timeout);
1568 priv->typeselect_flush_timeout = 0;
1571 for (list = priv->columns; list; list = list->next)
1572 _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list->data));
1574 gdk_window_set_user_data (priv->bin_window, NULL);
1575 gdk_window_destroy (priv->bin_window);
1576 priv->bin_window = NULL;
1578 gdk_window_set_user_data (priv->header_window, NULL);
1579 gdk_window_destroy (priv->header_window);
1580 priv->header_window = NULL;
1582 if (priv->drag_window)
1584 gdk_window_set_user_data (priv->drag_window, NULL);
1585 gdk_window_destroy (priv->drag_window);
1586 priv->drag_window = NULL;
1589 if (priv->drag_highlight_window)
1591 gdk_window_set_user_data (priv->drag_highlight_window, NULL);
1592 gdk_window_destroy (priv->drag_highlight_window);
1593 priv->drag_highlight_window = NULL;
1596 for (x = 0 ; x < 5 ; ++x)
1597 g_object_unref (priv->grid_line_gc[x]);
1599 if (tree_view->priv->columns != NULL)
1601 list = tree_view->priv->columns;
1604 PsppSheetViewColumn *column;
1605 column = PSPP_SHEET_VIEW_COLUMN (list->data);
1607 pspp_sheet_view_remove_column (tree_view, column);
1609 tree_view->priv->columns = NULL;
1613 /* GtkWidget::size_request helper */
1615 pspp_sheet_view_size_request_columns (PsppSheetView *tree_view)
1619 tree_view->priv->header_height = 0;
1621 if (tree_view->priv->model)
1623 for (list = tree_view->priv->columns; list; list = list->next)
1625 GtkRequisition requisition;
1626 PsppSheetViewColumn *column = list->data;
1628 pspp_sheet_view_column_size_request (column, &requisition);
1629 column->button_request = requisition.width;
1630 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
1636 /* Called only by ::size_request */
1638 pspp_sheet_view_update_size (PsppSheetView *tree_view)
1641 PsppSheetViewColumn *column;
1644 if (tree_view->priv->model == NULL)
1646 tree_view->priv->width = 0;
1647 tree_view->priv->prev_width = 0;
1648 tree_view->priv->height = 0;
1652 tree_view->priv->prev_width = tree_view->priv->width;
1653 tree_view->priv->width = 0;
1655 /* keep this in sync with size_allocate below */
1656 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
1658 gint real_requested_width = 0;
1659 column = list->data;
1660 if (!column->visible)
1663 if (column->use_resized_width)
1665 real_requested_width = column->resized_width;
1669 real_requested_width = column->fixed_width;
1672 if (column->min_width != -1)
1673 real_requested_width = MAX (real_requested_width, column->min_width);
1674 if (column->max_width != -1)
1675 real_requested_width = MIN (real_requested_width, column->max_width);
1677 tree_view->priv->width += real_requested_width;
1680 tree_view->priv->height = tree_view->priv->fixed_height * tree_view->priv->row_count;
1684 pspp_sheet_view_size_request (GtkWidget *widget,
1685 GtkRequisition *requisition)
1687 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1690 /* we validate some rows initially just to make sure we have some size.
1691 * In practice, with a lot of static lists, this should get a good width.
1693 initialize_fixed_height_mode (tree_view);
1694 pspp_sheet_view_size_request_columns (tree_view);
1695 pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget));
1697 requisition->width = tree_view->priv->width;
1698 requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
1700 tmp_list = tree_view->priv->children;
1704 PsppSheetViewChild *child = tmp_list->data;
1705 GtkRequisition child_requisition;
1707 tmp_list = tmp_list->next;
1709 if (gtk_widget_get_visible (child->widget))
1710 gtk_widget_size_request (child->widget, &child_requisition);
1715 invalidate_column (PsppSheetView *tree_view,
1716 PsppSheetViewColumn *column)
1718 gint column_offset = 0;
1720 GtkWidget *widget = GTK_WIDGET (tree_view);
1723 if (!gtk_widget_get_realized (widget))
1726 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1727 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
1729 list = (rtl ? list->prev : list->next))
1731 PsppSheetViewColumn *tmpcolumn = list->data;
1732 if (tmpcolumn == column)
1734 GdkRectangle invalid_rect;
1736 invalid_rect.x = column_offset;
1738 invalid_rect.width = column->width;
1739 invalid_rect.height = widget->allocation.height;
1741 gdk_window_invalidate_rect (widget->window, &invalid_rect, TRUE);
1745 column_offset += tmpcolumn->width;
1750 invalidate_last_column (PsppSheetView *tree_view)
1755 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1757 for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
1759 last_column = (rtl ? last_column->next : last_column->prev))
1761 if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible)
1763 invalidate_column (tree_view, last_column->data);
1770 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_view,
1771 PsppSheetViewColumn *column)
1773 gint real_requested_width;
1775 if (column->use_resized_width)
1777 real_requested_width = column->resized_width;
1781 real_requested_width = column->fixed_width;
1784 if (column->min_width != -1)
1785 real_requested_width = MAX (real_requested_width, column->min_width);
1786 if (column->max_width != -1)
1787 real_requested_width = MIN (real_requested_width, column->max_width);
1789 return real_requested_width;
1793 span_intersects (int a0, int a_width,
1794 int b0, int b_width)
1796 int a1 = a0 + a_width;
1797 int b1 = b0 + b_width;
1798 return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1);
1801 /* GtkWidget::size_allocate helper */
1803 pspp_sheet_view_size_allocate_columns (GtkWidget *widget,
1804 gboolean *width_changed)
1806 PsppSheetView *tree_view;
1807 GList *list, *first_column, *last_column;
1808 PsppSheetViewColumn *column;
1809 GtkAllocation allocation;
1811 gint extra, extra_per_column;
1812 gint full_requested_width = 0;
1813 gint number_of_expand_columns = 0;
1814 gboolean column_changed = FALSE;
1817 tree_view = PSPP_SHEET_VIEW (widget);
1819 for (last_column = g_list_last (tree_view->priv->columns);
1820 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
1821 last_column = last_column->prev)
1823 if (last_column == NULL)
1826 for (first_column = g_list_first (tree_view->priv->columns);
1827 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
1828 first_column = first_column->next)
1832 allocation.height = tree_view->priv->header_height;
1834 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1836 /* find out how many extra space and expandable columns we have */
1837 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
1839 column = (PsppSheetViewColumn *)list->data;
1841 if (!column->visible)
1844 full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1847 number_of_expand_columns++;
1850 extra = MAX (widget->allocation.width - full_requested_width, 0);
1851 if (number_of_expand_columns > 0)
1852 extra_per_column = extra/number_of_expand_columns;
1854 extra_per_column = 0;
1856 for (list = (rtl ? last_column : first_column);
1857 list != (rtl ? first_column->prev : last_column->next);
1858 list = (rtl ? list->prev : list->next))
1860 gint real_requested_width = 0;
1863 column = list->data;
1864 old_width = column->width;
1866 if (!column->visible)
1869 /* We need to handle the dragged button specially.
1871 if (column == tree_view->priv->drag_column)
1873 GtkAllocation drag_allocation;
1874 gdk_drawable_get_size (tree_view->priv->drag_window,
1875 &(drag_allocation.width),
1876 &(drag_allocation.height));
1877 drag_allocation.x = 0;
1878 drag_allocation.y = 0;
1879 pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column,
1881 width += drag_allocation.width;
1885 real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1887 allocation.x = width;
1888 column->width = real_requested_width;
1892 if (number_of_expand_columns == 1)
1894 /* We add the remander to the last column as
1896 column->width += extra;
1900 column->width += extra_per_column;
1901 extra -= extra_per_column;
1902 number_of_expand_columns --;
1906 if (column->width != old_width)
1907 g_object_notify (G_OBJECT (column), "width");
1909 allocation.width = column->width;
1910 width += column->width;
1912 if (column->width > old_width)
1913 column_changed = TRUE;
1915 pspp_sheet_view_column_size_allocate (column, &allocation);
1917 if (span_intersects (allocation.x, allocation.width,
1918 tree_view->priv->hadjustment->value,
1919 widget->allocation.width)
1920 && gtk_widget_get_realized (widget))
1921 pspp_sheet_view_column_set_need_button (column, TRUE);
1924 gdk_window_move_resize (column->window,
1925 allocation.x + (rtl ? 0 : allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
1927 TREE_VIEW_DRAG_WIDTH, allocation.height);
1930 /* We change the width here. The user might have been resizing columns,
1931 * so the total width of the tree view changes.
1933 tree_view->priv->width = width;
1935 *width_changed = TRUE;
1938 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
1943 pspp_sheet_view_size_allocate (GtkWidget *widget,
1944 GtkAllocation *allocation)
1946 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1948 gboolean width_changed = FALSE;
1949 gint old_width = widget->allocation.width;
1951 if (allocation->width != widget->allocation.width)
1952 width_changed = TRUE;
1954 widget->allocation = *allocation;
1956 tmp_list = tree_view->priv->children;
1960 GtkAllocation allocation;
1962 PsppSheetViewChild *child = tmp_list->data;
1963 tmp_list = tmp_list->next;
1965 /* totally ignore our child's requisition */
1966 allocation.x = child->x;
1967 allocation.y = child->y;
1968 allocation.width = child->width;
1969 allocation.height = child->height;
1970 gtk_widget_size_allocate (child->widget, &allocation);
1973 /* We size-allocate the columns first because the width of the
1974 * tree view (used in updating the adjustments below) might change.
1976 pspp_sheet_view_size_allocate_columns (widget, &width_changed);
1978 tree_view->priv->hadjustment->page_size = allocation->width;
1979 tree_view->priv->hadjustment->page_increment = allocation->width * 0.9;
1980 tree_view->priv->hadjustment->step_increment = allocation->width * 0.1;
1981 tree_view->priv->hadjustment->lower = 0;
1982 tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->page_size, tree_view->priv->width);
1984 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
1986 if (allocation->width < tree_view->priv->width)
1988 if (tree_view->priv->init_hadjust_value)
1990 tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
1991 tree_view->priv->init_hadjust_value = FALSE;
1993 else if (allocation->width != old_width)
1995 tree_view->priv->hadjustment->value = CLAMP (tree_view->priv->hadjustment->value - allocation->width + old_width, 0, tree_view->priv->width - allocation->width);
1998 tree_view->priv->hadjustment->value = CLAMP (tree_view->priv->width - (tree_view->priv->prev_width - tree_view->priv->hadjustment->value), 0, tree_view->priv->width - allocation->width);
2002 tree_view->priv->hadjustment->value = 0;
2003 tree_view->priv->init_hadjust_value = TRUE;
2007 if (tree_view->priv->hadjustment->value + allocation->width > tree_view->priv->width)
2008 tree_view->priv->hadjustment->value = MAX (tree_view->priv->width - allocation->width, 0);
2010 gtk_adjustment_changed (tree_view->priv->hadjustment);
2012 tree_view->priv->vadjustment->page_size = allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view);
2013 tree_view->priv->vadjustment->step_increment = tree_view->priv->vadjustment->page_size * 0.1;
2014 tree_view->priv->vadjustment->page_increment = tree_view->priv->vadjustment->page_size * 0.9;
2015 tree_view->priv->vadjustment->lower = 0;
2016 tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->page_size, tree_view->priv->height);
2018 gtk_adjustment_changed (tree_view->priv->vadjustment);
2020 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2021 if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size)
2022 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2023 else if (tree_view->priv->vadjustment->value + tree_view->priv->vadjustment->page_size > tree_view->priv->height)
2024 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2025 tree_view->priv->height - tree_view->priv->vadjustment->page_size);
2026 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2027 pspp_sheet_view_top_row_to_dy (tree_view);
2029 pspp_sheet_view_dy_to_top_row (tree_view);
2031 if (gtk_widget_get_realized (widget))
2033 gdk_window_move_resize (widget->window,
2034 allocation->x, allocation->y,
2035 allocation->width, allocation->height);
2036 gdk_window_move_resize (tree_view->priv->header_window,
2037 - (gint) tree_view->priv->hadjustment->value,
2039 MAX (tree_view->priv->width, allocation->width),
2040 tree_view->priv->header_height);
2041 gdk_window_move_resize (tree_view->priv->bin_window,
2042 - (gint) tree_view->priv->hadjustment->value,
2043 TREE_VIEW_HEADER_HEIGHT (tree_view),
2044 MAX (tree_view->priv->width, allocation->width),
2045 allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2048 if (tree_view->priv->row_count == 0)
2049 invalidate_empty_focus (tree_view);
2051 if (gtk_widget_get_realized (widget))
2053 gboolean has_expand_column = FALSE;
2054 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2056 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2058 has_expand_column = TRUE;
2063 /* This little hack only works if we have an LTR locale, and no column has the */
2066 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2067 ! has_expand_column)
2068 invalidate_last_column (tree_view);
2070 gtk_widget_queue_draw (widget);
2075 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2077 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2079 GtkWidget *widget = GTK_WIDGET (tree_view);
2081 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2082 gtk_widget_grab_focus (widget);
2083 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2087 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2090 return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2094 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2097 range_tower_set1 (tree_view->priv->selected, node, 1);
2101 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2104 range_tower_set0 (tree_view->priv->selected, node, 1);
2108 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2111 return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2115 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2118 return node > 0 ? node - 1 : -1;
2122 all_columns_selected (PsppSheetView *tree_view)
2126 for (list = tree_view->priv->columns; list; list = list->next)
2128 PsppSheetViewColumn *column = list->data;
2129 if (column->selectable && !column->selected)
2137 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2139 PsppSheetViewColumn *column,
2140 GdkEventButton *event)
2142 PsppSheetSelection *selection;
2143 PsppSheetSelectionMode mode;
2145 gboolean update_anchor;
2149 g_return_val_if_fail (tree_view != NULL, FALSE);
2150 g_return_val_if_fail (column != NULL, FALSE);
2152 selection = tree_view->priv->selection;
2153 mode = pspp_sheet_selection_get_mode (selection);
2154 if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2157 if (!column->row_head)
2162 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2163 if (event->type != GDK_BUTTON_PRESS
2164 || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2170 path = gtk_tree_path_new_from_indices (node, -1);
2173 pspp_sheet_selection_unselect_all (selection);
2174 pspp_sheet_selection_select_path (selection, path);
2175 pspp_sheet_selection_select_all_columns (selection);
2176 update_anchor = TRUE;
2179 else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2181 if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2182 || !all_columns_selected (tree_view))
2184 pspp_sheet_selection_unselect_all (selection);
2185 pspp_sheet_selection_select_path (selection, path);
2186 pspp_sheet_selection_select_all_columns (selection);
2187 update_anchor = TRUE;
2191 update_anchor = handled = FALSE;
2193 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2194 && modifiers == GDK_CONTROL_MASK)
2196 if (!all_columns_selected (tree_view))
2198 pspp_sheet_selection_unselect_all (selection);
2199 pspp_sheet_selection_select_all_columns (selection);
2202 if (pspp_sheet_selection_path_is_selected (selection, path))
2203 pspp_sheet_selection_unselect_path (selection, path);
2205 pspp_sheet_selection_select_path (selection, path);
2206 update_anchor = TRUE;
2209 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2210 && modifiers == GDK_SHIFT_MASK)
2212 GtkTreeRowReference *anchor = tree_view->priv->anchor;
2213 GtkTreePath *anchor_path;
2215 if (all_columns_selected (tree_view)
2216 && gtk_tree_row_reference_valid (anchor))
2218 update_anchor = FALSE;
2219 anchor_path = gtk_tree_row_reference_get_path (anchor);
2223 update_anchor = TRUE;
2224 anchor_path = gtk_tree_path_copy (path);
2227 pspp_sheet_selection_unselect_all (selection);
2228 pspp_sheet_selection_select_range (selection, anchor_path, path);
2229 pspp_sheet_selection_select_all_columns (selection);
2231 gtk_tree_path_free (anchor_path);
2236 update_anchor = handled = FALSE;
2240 if (tree_view->priv->anchor)
2241 gtk_tree_row_reference_free (tree_view->priv->anchor);
2242 tree_view->priv->anchor =
2243 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2244 tree_view->priv->model,
2248 gtk_tree_path_free (path);
2253 find_click (PsppSheetView *tree_view,
2256 PsppSheetViewColumn **column,
2257 GdkRectangle *background_area,
2258 GdkRectangle *cell_area)
2265 /* find the node that was clicked */
2266 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2269 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2274 background_area->y = y_offset + y;
2275 background_area->height = ROW_HEIGHT (tree_view);
2276 background_area->x = 0;
2278 /* Let the column have a chance at selecting it. */
2279 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2280 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2281 list; list = (rtl ? list->prev : list->next))
2283 PsppSheetViewColumn *candidate = list->data;
2285 if (!candidate->visible)
2288 background_area->width = candidate->width;
2289 if ((background_area->x > x) ||
2290 (background_area->x + background_area->width <= x))
2292 background_area->x += background_area->width;
2296 /* we found the focus column */
2298 pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2300 *column = candidate;
2308 pspp_sheet_view_button_press (GtkWidget *widget,
2309 GdkEventButton *event)
2311 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2313 PsppSheetViewColumn *column = NULL;
2315 GdkRectangle background_area;
2316 GdkRectangle cell_area;
2319 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2320 pspp_sheet_view_stop_editing (tree_view, FALSE);
2323 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2324 * we're done handling the button press.
2327 if (event->window == tree_view->priv->bin_window)
2332 gint pre_val, aft_val;
2333 PsppSheetViewColumn *column = NULL;
2334 GtkCellRenderer *focus_cell = NULL;
2335 gboolean row_double_click = FALSE;
2338 if (tree_view->priv->row_count == 0)
2340 grab_focus_and_unset_draw_keyfocus (tree_view);
2344 if (!find_click (tree_view, event->x, event->y, &node, &column,
2345 &background_area, &cell_area))
2347 grab_focus_and_unset_draw_keyfocus (tree_view);
2351 tree_view->priv->focus_column = column;
2353 if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2357 pre_val = tree_view->priv->vadjustment->value;
2359 path = _pspp_sheet_view_find_path (tree_view, node);
2361 /* we only handle selection modifications on the first button press
2363 if (event->type == GDK_BUTTON_PRESS)
2365 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2366 tree_view->priv->ctrl_pressed = TRUE;
2367 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2368 tree_view->priv->shift_pressed = TRUE;
2370 focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2372 pspp_sheet_view_column_focus_cell (column, focus_cell);
2374 if (event->state & GDK_CONTROL_MASK)
2376 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE);
2377 pspp_sheet_view_real_toggle_cursor_row (tree_view);
2379 else if (event->state & GDK_SHIFT_MASK)
2381 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE);
2382 pspp_sheet_view_real_select_cursor_row (tree_view, FALSE);
2386 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE);
2389 if (tree_view->priv->anchor_column == NULL ||
2390 !(event->state & GDK_SHIFT_MASK))
2391 tree_view->priv->anchor_column = column;
2392 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2393 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2394 tree_view->priv->anchor_column,
2397 tree_view->priv->ctrl_pressed = FALSE;
2398 tree_view->priv->shift_pressed = FALSE;
2401 /* the treeview may have been scrolled because of _set_cursor,
2405 aft_val = tree_view->priv->vadjustment->value;
2406 dval = pre_val - aft_val;
2408 cell_area.y += dval;
2409 background_area.y += dval;
2411 /* Save press to possibly begin a drag
2413 if (!tree_view->priv->in_grab &&
2414 tree_view->priv->pressed_button < 0)
2416 tree_view->priv->pressed_button = event->button;
2417 tree_view->priv->press_start_x = event->x;
2418 tree_view->priv->press_start_y = event->y;
2419 tree_view->priv->press_start_node = node;
2421 if (tree_view->priv->rubber_banding_enable
2422 && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2423 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2425 tree_view->priv->press_start_y += tree_view->priv->dy;
2426 tree_view->priv->rubber_band_x = event->x;
2427 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2428 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2430 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2431 tree_view->priv->rubber_band_ctrl = TRUE;
2432 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2433 tree_view->priv->rubber_band_shift = TRUE;
2438 /* Test if a double click happened on the same row. */
2439 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2441 int double_click_time, double_click_distance;
2443 g_object_get (gtk_settings_get_for_screen (
2444 gtk_widget_get_screen (widget)),
2445 "gtk-double-click-time", &double_click_time,
2446 "gtk-double-click-distance", &double_click_distance,
2449 /* Same conditions as _gdk_event_button_generate */
2450 if (tree_view->priv->last_button_x != -1 &&
2451 (event->time < tree_view->priv->last_button_time + double_click_time) &&
2452 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2453 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2455 /* We do no longer compare paths of this row and the
2456 * row clicked previously. We use the double click
2457 * distance to decide whether this is a valid click,
2458 * allowing the mouse to slightly move over another row.
2460 row_double_click = TRUE;
2462 tree_view->priv->last_button_time = 0;
2463 tree_view->priv->last_button_x = -1;
2464 tree_view->priv->last_button_y = -1;
2468 tree_view->priv->last_button_time = event->time;
2469 tree_view->priv->last_button_x = event->x;
2470 tree_view->priv->last_button_y = event->y;
2474 if (row_double_click)
2476 gtk_grab_remove (widget);
2477 pspp_sheet_view_row_activated (tree_view, path, column);
2479 if (tree_view->priv->pressed_button == event->button)
2480 tree_view->priv->pressed_button = -1;
2483 gtk_tree_path_free (path);
2485 /* If we activated the row through a double click we don't want to grab
2486 * focus back, as moving focus to another widget is pretty common.
2488 if (!row_double_click)
2489 grab_focus_and_unset_draw_keyfocus (tree_view);
2494 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2496 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2498 column = list->data;
2499 if (event->window == column->window &&
2500 column->resizable &&
2505 if (gdk_pointer_grab (column->window, FALSE,
2506 GDK_POINTER_MOTION_HINT_MASK |
2507 GDK_BUTTON1_MOTION_MASK |
2508 GDK_BUTTON_RELEASE_MASK,
2509 NULL, NULL, event->time))
2512 gtk_grab_add (widget);
2513 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2514 column->resized_width = column->width;
2516 /* block attached dnd signal handler */
2517 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2519 g_signal_handlers_block_matched (widget,
2520 G_SIGNAL_MATCH_DATA,
2524 tree_view->priv->drag_pos = i;
2525 tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2527 if (!gtk_widget_has_focus (widget))
2528 gtk_widget_grab_focus (widget);
2536 /* GtkWidget::button_release_event helper */
2538 pspp_sheet_view_button_release_drag_column (GtkWidget *widget,
2539 GdkEventButton *event)
2541 PsppSheetView *tree_view;
2545 tree_view = PSPP_SHEET_VIEW (widget);
2547 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2548 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2549 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2551 /* Move the button back */
2552 g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2554 g_object_ref (tree_view->priv->drag_column->button);
2555 gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2556 gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2557 gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2558 g_object_unref (tree_view->priv->drag_column->button);
2559 gtk_widget_queue_resize (widget);
2560 if (tree_view->priv->drag_column->resizable)
2562 gdk_window_raise (tree_view->priv->drag_column->window);
2563 gdk_window_show (tree_view->priv->drag_column->window);
2566 gdk_window_hide (tree_view->priv->drag_column->window);
2568 gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2572 if (tree_view->priv->cur_reorder &&
2573 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2574 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2575 tree_view->priv->cur_reorder->right_column);
2579 if (tree_view->priv->cur_reorder &&
2580 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2581 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2582 tree_view->priv->cur_reorder->left_column);
2584 tree_view->priv->drag_column = NULL;
2585 gdk_window_hide (tree_view->priv->drag_window);
2587 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2588 g_slice_free (PsppSheetViewColumnReorder, l->data);
2589 g_list_free (tree_view->priv->column_drag_info);
2590 tree_view->priv->column_drag_info = NULL;
2591 tree_view->priv->cur_reorder = NULL;
2593 if (tree_view->priv->drag_highlight_window)
2594 gdk_window_hide (tree_view->priv->drag_highlight_window);
2596 /* Reset our flags */
2597 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2598 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2603 /* GtkWidget::button_release_event helper */
2605 pspp_sheet_view_button_release_column_resize (GtkWidget *widget,
2606 GdkEventButton *event)
2608 PsppSheetView *tree_view;
2611 tree_view = PSPP_SHEET_VIEW (widget);
2613 tree_view->priv->drag_pos = -1;
2615 /* unblock attached dnd signal handler */
2616 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2618 g_signal_handlers_unblock_matched (widget,
2619 G_SIGNAL_MATCH_DATA,
2623 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2624 gtk_grab_remove (widget);
2625 gdk_display_pointer_ungrab (gdk_drawable_get_display (event->window),
2631 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2632 GdkEventButton *event)
2634 GtkCellEditable *cell_editable;
2639 PsppSheetViewColumn *column;
2640 GdkRectangle background_area;
2641 GdkRectangle cell_area;
2647 if (event->window != tree_view->priv->bin_window)
2650 /* Ignore a released button, if that button wasn't depressed */
2651 if (tree_view->priv->pressed_button != event->button)
2654 if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2658 /* decide if we edit */
2659 path = _pspp_sheet_view_find_path (tree_view, node);
2660 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2661 if (event->button != 1 || modifiers)
2664 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2665 pspp_sheet_view_column_cell_set_cell_data (column,
2666 tree_view->priv->model,
2669 if (!pspp_sheet_view_column_get_quick_edit (column)
2670 && _pspp_sheet_view_column_has_editable_cell (column))
2673 flags = 0; /* FIXME: get the right flags */
2674 path_string = gtk_tree_path_to_string (path);
2676 if (!_pspp_sheet_view_column_cell_event (column,
2684 if (cell_editable == NULL)
2687 pspp_sheet_view_real_set_cursor (tree_view, path,
2689 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2692 _pspp_sheet_view_column_get_neighbor_sizes (
2693 column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2696 area.width -= right + left;
2698 pspp_sheet_view_real_start_editing (tree_view,
2705 g_free (path_string);
2706 gtk_tree_path_free (path);
2711 pspp_sheet_view_button_release (GtkWidget *widget,
2712 GdkEventButton *event)
2714 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2716 pspp_sheet_view_stop_editing (tree_view, FALSE);
2717 if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2718 && pspp_sheet_view_button_release_edit (tree_view, event))
2720 if (tree_view->priv->pressed_button == event->button)
2721 tree_view->priv->pressed_button = -1;
2723 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2727 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2728 return pspp_sheet_view_button_release_drag_column (widget, event);
2730 if (tree_view->priv->rubber_band_status)
2731 pspp_sheet_view_stop_rubber_band (tree_view);
2733 if (tree_view->priv->pressed_button == event->button)
2734 tree_view->priv->pressed_button = -1;
2736 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2737 return pspp_sheet_view_button_release_column_resize (widget, event);
2743 pspp_sheet_view_grab_broken (GtkWidget *widget,
2744 GdkEventGrabBroken *event)
2746 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2748 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2749 pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2751 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2752 pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2757 /* GtkWidget::motion_event function set.
2761 do_prelight (PsppSheetView *tree_view,
2763 /* these are in bin_window coords */
2767 int prev_node = tree_view->priv->prelight_node;
2769 if (prev_node != node)
2771 tree_view->priv->prelight_node = node;
2774 _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2777 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2782 prelight_or_select (PsppSheetView *tree_view,
2784 /* these are in bin_window coords */
2788 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2790 if (tree_view->priv->hover_selection &&
2791 (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2792 !(tree_view->priv->edited_column &&
2793 tree_view->priv->edited_column->editable_widget))
2797 if (!pspp_sheet_view_node_is_selected (tree_view, node))
2801 path = _pspp_sheet_view_find_path (tree_view, node);
2802 pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2803 if (pspp_sheet_view_node_is_selected (tree_view, node))
2805 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2806 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE);
2808 gtk_tree_path_free (path);
2812 else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2813 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2816 do_prelight (tree_view, node, x, y);
2820 ensure_unprelighted (PsppSheetView *tree_view)
2822 do_prelight (tree_view,
2824 -1000, -1000); /* coords not possibly over an arrow */
2826 g_assert (tree_view->priv->prelight_node < 0);
2830 update_prelight (PsppSheetView *tree_view,
2837 if (tree_view->priv->row_count == 0)
2842 ensure_unprelighted (tree_view);
2846 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2850 pspp_sheet_view_find_offset (tree_view, new_y, &node);
2853 prelight_or_select (tree_view, node, x, y);
2859 /* Our motion arrow is either a box (in the case of the original spot)
2860 * or an arrow. It is expander_size wide.
2883 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2885 PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2886 GtkWidget *widget = GTK_WIDGET (tree_view);
2887 GdkBitmap *mask = NULL;
2892 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2893 GdkWindowAttr attributes;
2894 guint attributes_mask;
2897 reorder->left_column == tree_view->priv->drag_column ||
2898 reorder->right_column == tree_view->priv->drag_column)
2899 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2900 else if (reorder->left_column || reorder->right_column)
2902 GdkRectangle visible_rect;
2903 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2904 if (reorder->left_column)
2905 x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2907 x = reorder->right_column->allocation.x;
2909 if (x < visible_rect.x)
2910 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2911 else if (x > visible_rect.x + visible_rect.width)
2912 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2914 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2917 /* We want to draw the rectangle over the initial location. */
2918 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2923 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2925 if (tree_view->priv->drag_highlight_window)
2927 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2929 gdk_window_destroy (tree_view->priv->drag_highlight_window);
2932 attributes.window_type = GDK_WINDOW_CHILD;
2933 attributes.wclass = GDK_INPUT_OUTPUT;
2934 attributes.x = tree_view->priv->drag_column_x;
2936 width = attributes.width = tree_view->priv->drag_column->allocation.width;
2937 height = attributes.height = tree_view->priv->drag_column->allocation.height;
2938 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
2939 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
2940 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
2941 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
2942 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
2943 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
2945 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
2946 gc = gdk_gc_new (mask);
2948 gdk_gc_set_foreground (gc, &col);
2949 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
2951 gdk_gc_set_foreground(gc, &col);
2952 gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
2953 g_object_unref (gc);
2955 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
2957 if (mask) g_object_unref (mask);
2958 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2961 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
2967 width = tree_view->priv->expander_size;
2969 /* Get x, y, width, height of arrow */
2970 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
2971 if (reorder->left_column)
2973 x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
2974 height = reorder->left_column->allocation.height;
2978 x += reorder->right_column->allocation.x - width/2;
2979 height = reorder->right_column->allocation.height;
2981 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
2982 height += tree_view->priv->expander_size;
2984 /* Create the new window */
2985 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
2987 if (tree_view->priv->drag_highlight_window)
2989 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2991 gdk_window_destroy (tree_view->priv->drag_highlight_window);
2994 attributes.window_type = GDK_WINDOW_TEMP;
2995 attributes.wclass = GDK_INPUT_OUTPUT;
2996 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
2997 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
2998 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
2999 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3002 attributes.width = width;
3003 attributes.height = height;
3004 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3005 &attributes, attributes_mask);
3006 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3008 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3009 gc = gdk_gc_new (mask);
3011 gdk_gc_set_foreground (gc, &col);
3012 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3014 /* Draw the 2 arrows as per above */
3016 gdk_gc_set_foreground (gc, &col);
3017 for (i = 0; i < width; i ++)
3019 if (i == (width/2 - 1))
3021 gdk_draw_line (mask, gc, i, j, i, height - j);
3022 if (i < (width/2 - 1))
3027 g_object_unref (gc);
3028 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3030 if (mask) g_object_unref (mask);
3033 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3034 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3036 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3037 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3043 width = tree_view->priv->expander_size;
3045 /* Get x, y, width, height of arrow */
3046 width = width/2; /* remember, the arrow only takes half the available width */
3047 gdk_window_get_origin (widget->window, &x, &y);
3048 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3049 x += widget->allocation.width - width;
3051 if (reorder->left_column)
3052 height = reorder->left_column->allocation.height;
3054 height = reorder->right_column->allocation.height;
3056 y -= tree_view->priv->expander_size;
3057 height += 2*tree_view->priv->expander_size;
3059 /* Create the new window */
3060 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3061 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3063 if (tree_view->priv->drag_highlight_window)
3065 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3067 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3070 attributes.window_type = GDK_WINDOW_TEMP;
3071 attributes.wclass = GDK_INPUT_OUTPUT;
3072 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3073 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3074 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3075 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3078 attributes.width = width;
3079 attributes.height = height;
3080 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3081 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3083 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3084 gc = gdk_gc_new (mask);
3086 gdk_gc_set_foreground (gc, &col);
3087 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3089 /* Draw the 2 arrows as per above */
3091 gdk_gc_set_foreground (gc, &col);
3092 j = tree_view->priv->expander_size;
3093 for (i = 0; i < width; i ++)
3096 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3100 gdk_draw_line (mask, gc, k, j, k, height - j);
3101 gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3102 gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3105 g_object_unref (gc);
3106 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3108 if (mask) g_object_unref (mask);
3111 tree_view->priv->drag_column_window_state = arrow_type;
3112 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3116 g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3117 gdk_window_hide (tree_view->priv->drag_highlight_window);
3121 gdk_window_show (tree_view->priv->drag_highlight_window);
3122 gdk_window_raise (tree_view->priv->drag_highlight_window);
3126 pspp_sheet_view_motion_resize_column (GtkWidget *widget,
3127 GdkEventMotion *event)
3131 PsppSheetViewColumn *column;
3132 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3134 column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3136 if (event->is_hint || event->window != widget->window)
3137 gtk_widget_get_pointer (widget, &x, NULL);
3141 if (tree_view->priv->hadjustment)
3142 x += tree_view->priv->hadjustment->value;
3144 new_width = pspp_sheet_view_new_column_width (tree_view,
3145 tree_view->priv->drag_pos, &x);
3146 if (x != tree_view->priv->x_drag &&
3147 (new_width != column->fixed_width))
3149 column->use_resized_width = TRUE;
3150 column->resized_width = new_width;
3153 column->resized_width -= tree_view->priv->last_extra_space_per_column;
3155 gtk_widget_queue_resize (widget);
3163 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3165 PsppSheetViewColumnReorder *reorder = NULL;
3169 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3170 for (list = tree_view->priv->column_drag_info; list; list = list->next)
3172 reorder = (PsppSheetViewColumnReorder *) list->data;
3173 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3178 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3181 tree_view->priv->cur_reorder = reorder;
3182 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3186 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3188 GdkRectangle visible_rect;
3193 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3194 y += tree_view->priv->dy;
3196 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3198 /* see if we are near the edge. */
3199 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3202 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3207 value = CLAMP (tree_view->priv->vadjustment->value + offset, 0.0,
3208 tree_view->priv->vadjustment->upper - tree_view->priv->vadjustment->page_size);
3209 gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3213 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3215 GdkRectangle visible_rect;
3220 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3222 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3224 /* See if we are near the edge. */
3225 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3228 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3234 value = CLAMP (tree_view->priv->hadjustment->value + offset,
3235 0.0, tree_view->priv->hadjustment->upper - tree_view->priv->hadjustment->page_size);
3236 gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3243 pspp_sheet_view_motion_drag_column (GtkWidget *widget,
3244 GdkEventMotion *event)
3246 PsppSheetView *tree_view = (PsppSheetView *) widget;
3247 PsppSheetViewColumn *column = tree_view->priv->drag_column;
3251 if ((column == NULL) ||
3252 (event->window != tree_view->priv->drag_window))
3255 /* Handle moving the header */
3256 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3257 x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3258 MAX (tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.width) - column->allocation.width);
3259 gdk_window_move (tree_view->priv->drag_window, x, y);
3261 /* autoscroll, if needed */
3262 pspp_sheet_view_horizontal_autoscroll (tree_view);
3263 /* Update the current reorder position and arrow; */
3264 pspp_sheet_view_update_current_reorder (tree_view);
3270 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3272 remove_scroll_timeout (tree_view);
3273 gtk_grab_remove (GTK_WIDGET (tree_view));
3275 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3277 GtkTreePath *tmp_path;
3279 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3281 /* The anchor path should be set to the start path */
3282 tmp_path = _pspp_sheet_view_find_path (tree_view,
3283 tree_view->priv->rubber_band_start_node);
3285 if (tree_view->priv->anchor)
3286 gtk_tree_row_reference_free (tree_view->priv->anchor);
3288 tree_view->priv->anchor =
3289 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3290 tree_view->priv->model,
3293 gtk_tree_path_free (tmp_path);
3295 /* ... and the cursor to the end path */
3296 tmp_path = _pspp_sheet_view_find_path (tree_view,
3297 tree_view->priv->rubber_band_end_node);
3298 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE);
3299 gtk_tree_path_free (tmp_path);
3301 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3304 /* Clear status variables */
3305 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3306 tree_view->priv->rubber_band_shift = 0;
3307 tree_view->priv->rubber_band_ctrl = 0;
3309 tree_view->priv->rubber_band_start_node = -1;
3310 tree_view->priv->rubber_band_end_node = -1;
3314 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3318 gboolean skip_start,
3321 if (start_node == end_node)
3324 /* We skip the first node and jump inside the loop */
3330 /* Small optimization by assuming insensitive nodes are never
3335 if (tree_view->priv->rubber_band_shift)
3336 pspp_sheet_view_node_select (tree_view, start_node);
3337 else if (tree_view->priv->rubber_band_ctrl)
3339 /* Toggle the selection state */
3340 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3341 pspp_sheet_view_node_unselect (tree_view, start_node);
3343 pspp_sheet_view_node_select (tree_view, start_node);
3346 pspp_sheet_view_node_select (tree_view, start_node);
3350 /* Mirror the above */
3351 if (tree_view->priv->rubber_band_shift)
3352 pspp_sheet_view_node_unselect (tree_view, start_node);
3353 else if (tree_view->priv->rubber_band_ctrl)
3355 /* Toggle the selection state */
3356 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3357 pspp_sheet_view_node_unselect (tree_view, start_node);
3359 pspp_sheet_view_node_select (tree_view, start_node);
3362 pspp_sheet_view_node_unselect (tree_view, start_node);
3365 _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3367 if (start_node == end_node)
3372 start_node = pspp_sheet_view_node_next (tree_view, start_node);
3375 /* Ran out of tree */
3378 if (skip_end && start_node == end_node)
3385 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3388 return node * tree_view->priv->fixed_height;
3392 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3396 int fixed_height = tree_view->priv->fixed_height;
3397 if (fixed_height <= 0
3399 || height >= tree_view->priv->row_count * fixed_height)
3406 *new_node = height / fixed_height;
3407 return height % fixed_height;
3412 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3417 pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3418 pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3420 /* Handle the start area first */
3421 if (tree_view->priv->rubber_band_start_node < 0)
3423 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3430 else if (start_node < tree_view->priv->rubber_band_start_node)
3432 /* New node is above the old one; selection became bigger */
3433 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3435 tree_view->priv->rubber_band_start_node,
3440 else if (start_node > tree_view->priv->rubber_band_start_node)
3442 /* New node is below the old one; selection became smaller */
3443 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3444 tree_view->priv->rubber_band_start_node,
3451 tree_view->priv->rubber_band_start_node = start_node;
3453 /* Next, handle the end area */
3454 if (tree_view->priv->rubber_band_end_node < 0)
3456 /* In the event this happens, start_node was also -1; this case is
3460 else if (end_node < 0)
3462 /* Find the last node in the tree */
3463 pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3466 /* Selection reached end of the tree */
3467 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3468 tree_view->priv->rubber_band_end_node,
3474 else if (end_node > tree_view->priv->rubber_band_end_node)
3476 /* New node is below the old one; selection became bigger */
3477 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3478 tree_view->priv->rubber_band_end_node,
3484 else if (end_node < tree_view->priv->rubber_band_end_node)
3486 /* New node is above the old one; selection became smaller */
3487 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3489 tree_view->priv->rubber_band_end_node,
3495 tree_view->priv->rubber_band_end_node = end_node;
3499 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3502 GdkRectangle old_area;
3503 GdkRectangle new_area;
3504 GdkRectangle common;
3505 GdkRegion *invalid_region;
3506 PsppSheetViewColumn *column;
3508 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3509 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3510 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3511 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3513 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3516 y = MAX (y, 0) + tree_view->priv->dy;
3518 new_area.x = MIN (tree_view->priv->press_start_x, x);
3519 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3520 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3521 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3523 invalid_region = gdk_region_rectangle (&old_area);
3524 gdk_region_union_with_rect (invalid_region, &new_area);
3526 gdk_rectangle_intersect (&old_area, &new_area, &common);
3527 if (common.width > 2 && common.height > 2)
3529 GdkRegion *common_region;
3531 /* make sure the border is invalidated */
3537 common_region = gdk_region_rectangle (&common);
3539 gdk_region_subtract (invalid_region, common_region);
3540 gdk_region_destroy (common_region);
3543 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
3545 gdk_region_destroy (invalid_region);
3547 tree_view->priv->rubber_band_x = x;
3548 tree_view->priv->rubber_band_y = y;
3549 pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3551 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3552 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3553 tree_view->priv->anchor_column,
3556 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3558 pspp_sheet_view_update_rubber_band_selection (tree_view);
3562 pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view,
3567 GdkRectangle rubber_rect;
3570 rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3571 rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3572 rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3573 rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3575 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3578 cr = gdk_cairo_create (tree_view->priv->bin_window);
3579 cairo_set_line_width (cr, 1.0);
3581 cairo_set_source_rgba (cr,
3582 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].red / 65535.,
3583 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].green / 65535.,
3584 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].blue / 65535.,
3587 gdk_cairo_rectangle (cr, &rect);
3591 cairo_set_source_rgb (cr,
3592 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].red / 65535.,
3593 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].green / 65535.,
3594 GTK_WIDGET (tree_view)->style->fg[GTK_STATE_NORMAL].blue / 65535.);
3596 cairo_rectangle (cr,
3597 rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3598 rubber_rect.width - 1, rubber_rect.height - 1);
3605 pspp_sheet_view_motion_bin_window (GtkWidget *widget,
3606 GdkEventMotion *event)
3608 PsppSheetView *tree_view;
3612 tree_view = (PsppSheetView *) widget;
3614 if (tree_view->priv->row_count == 0)
3617 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3619 GdkRectangle background_area, cell_area;
3620 PsppSheetViewColumn *column;
3622 if (find_click (tree_view, event->x, event->y, &node, &column,
3623 &background_area, &cell_area)
3624 && tree_view->priv->focus_column == column
3625 && tree_view->priv->press_start_node == node)
3628 gtk_grab_add (GTK_WIDGET (tree_view));
3629 pspp_sheet_view_update_rubber_band (tree_view);
3631 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3633 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3635 pspp_sheet_view_update_rubber_band (tree_view);
3637 add_scroll_timeout (tree_view);
3640 /* only check for an initiated drag when a button is pressed */
3641 if (tree_view->priv->pressed_button >= 0
3642 && !tree_view->priv->rubber_band_status)
3643 pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3645 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3649 pspp_sheet_view_find_offset (tree_view, new_y, &node);
3651 tree_view->priv->event_last_x = event->x;
3652 tree_view->priv->event_last_y = event->y;
3654 prelight_or_select (tree_view, node, event->x, event->y);
3660 pspp_sheet_view_motion (GtkWidget *widget,
3661 GdkEventMotion *event)
3663 PsppSheetView *tree_view;
3665 tree_view = (PsppSheetView *) widget;
3667 /* Resizing a column */
3668 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3669 return pspp_sheet_view_motion_resize_column (widget, event);
3672 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3673 return pspp_sheet_view_motion_drag_column (widget, event);
3675 /* Sanity check it */
3676 if (event->window == tree_view->priv->bin_window)
3677 return pspp_sheet_view_motion_bin_window (widget, event);
3682 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3683 * the tree is empty.
3686 invalidate_empty_focus (PsppSheetView *tree_view)
3690 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3695 gdk_drawable_get_size (tree_view->priv->bin_window, &area.width, &area.height);
3696 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3699 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3703 draw_empty_focus (PsppSheetView *tree_view, GdkRectangle *clip_area)
3705 GtkWidget *widget = GTK_WIDGET (tree_view);
3708 if (!gtk_widget_has_focus (widget))
3711 gdk_drawable_get_size (tree_view->priv->bin_window, &w, &h);
3717 gtk_paint_focus (gtk_widget_get_style (widget),
3718 tree_view->priv->bin_window,
3719 gtk_widget_get_state (widget),
3727 pspp_sheet_view_draw_grid_lines (PsppSheetView *tree_view,
3728 GdkEventExpose *event,
3729 gint n_visible_columns,
3733 GList *list = tree_view->priv->columns;
3738 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3739 && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3742 gdk_drawable_get_size (event->window, NULL, &height);
3744 /* Only draw the lines for visible rows and columns */
3745 for (list = tree_view->priv->columns; list; list = list->next, i++)
3747 PsppSheetViewColumn *column = list->data;
3749 /* We don't want a line for the last column */
3750 if (i == n_visible_columns - 1)
3753 if (! column->visible)
3756 current_x += column->width;
3758 if (current_x - 1 >= event->area.x
3759 && current_x - 1 < event->area.x + event->area.width)
3760 gdk_draw_line (event->window,
3761 tree_view->priv->grid_line_gc[GTK_WIDGET(tree_view)->state],
3762 current_x - 1, min_y,
3763 current_x - 1, max_y - min_y);
3767 /* Warning: Very scary function.
3768 * Modify at your own risk
3770 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3771 * FIXME: It's not...
3774 pspp_sheet_view_bin_expose (GtkWidget *widget,
3775 GdkEventExpose *event)
3777 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3782 int drag_highlight = -1;
3785 gint y_offset, cell_offset;
3787 GdkRectangle background_area;
3788 GdkRectangle cell_area;
3790 gint bin_window_width;
3791 gint bin_window_height;
3792 GtkTreePath *cursor_path;
3793 GtkTreePath *drag_dest_path;
3794 GList *first_column, *last_column;
3795 gint vertical_separator;
3796 gint horizontal_separator;
3797 gint focus_line_width;
3798 gboolean allow_rules;
3799 gboolean has_special_cell;
3801 gint n_visible_columns;
3802 gint grid_line_width;
3803 gboolean row_ending_details;
3804 gboolean draw_vgrid_lines, draw_hgrid_lines;
3807 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3809 gtk_widget_style_get (widget,
3810 "horizontal-separator", &horizontal_separator,
3811 "vertical-separator", &vertical_separator,
3812 "allow-rules", &allow_rules,
3813 "focus-line-width", &focus_line_width,
3814 "row-ending-details", &row_ending_details,
3817 if (tree_view->priv->row_count == 0)
3819 draw_empty_focus (tree_view, &event->area);
3823 /* clip event->area to the visible area */
3824 if (event->area.height < 0)
3827 validate_visible_area (tree_view);
3829 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, event->area.y);
3833 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3834 gdk_drawable_get_size (tree_view->priv->bin_window,
3835 &bin_window_width, &bin_window_height);
3837 if (tree_view->priv->height < bin_window_height)
3839 gtk_paint_flat_box (widget->style,
3846 0, tree_view->priv->height,
3848 bin_window_height - tree_view->priv->height);
3854 /* find the path for the node */
3855 path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3856 gtk_tree_model_get_iter (tree_view->priv->model,
3859 gtk_tree_path_free (path);
3862 drag_dest_path = NULL;
3864 if (tree_view->priv->cursor)
3865 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3868 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3870 if (tree_view->priv->drag_dest_row)
3871 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3874 _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3878 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3879 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3881 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
3882 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3884 if (draw_vgrid_lines || draw_hgrid_lines)
3885 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
3887 n_visible_columns = 0;
3888 for (list = tree_view->priv->columns; list; list = list->next)
3890 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
3892 n_visible_columns ++;
3895 /* Find the last column */
3896 for (last_column = g_list_last (tree_view->priv->columns);
3897 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
3898 last_column = last_column->prev)
3902 for (first_column = g_list_first (tree_view->priv->columns);
3903 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
3904 first_column = first_column->next)
3907 /* Actually process the expose event. To do this, we want to
3908 * start at the first node of the event, and walk the tree in
3909 * order, drawing each successive node.
3916 gboolean is_first = FALSE;
3917 gboolean is_last = FALSE;
3918 gboolean done = FALSE;
3921 max_height = ROW_HEIGHT (tree_view);
3925 background_area.y = y_offset + event->area.y;
3926 background_area.height = max_height;
3927 max_y = background_area.y + max_height;
3931 if (node == tree_view->priv->prelight_node)
3932 flags |= GTK_CELL_RENDERER_PRELIT;
3934 selected = pspp_sheet_view_node_is_selected (tree_view, node);
3938 if (tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
3940 /* we *need* to set cell data on all cells before the call
3941 * to _has_special_cell, else _has_special_cell() does not
3942 * return a correct value.
3944 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
3946 list = (rtl ? list->prev : list->next))
3948 PsppSheetViewColumn *column = list->data;
3949 pspp_sheet_view_column_cell_set_cell_data (column,
3950 tree_view->priv->model,
3954 has_special_cell = pspp_sheet_view_has_special_cell (tree_view);
3957 has_special_cell = tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
3959 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
3961 list = (rtl ? list->prev : list->next))
3963 PsppSheetViewColumn *column = list->data;
3964 const gchar *detail = NULL;
3965 gboolean selected_column;
3968 if (!column->visible)
3971 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
3972 selected_column = column->selected && column->selectable;
3974 selected_column = TRUE;
3976 if (cell_offset > event->area.x + event->area.width ||
3977 cell_offset + column->width < event->area.x)
3979 cell_offset += column->width;
3983 if (selected && selected_column)
3984 flags |= GTK_CELL_RENDERER_SELECTED;
3986 flags &= ~GTK_CELL_RENDERER_SELECTED;
3988 if (column->show_sort_indicator)
3989 flags |= GTK_CELL_RENDERER_SORTED;
3991 flags &= ~GTK_CELL_RENDERER_SORTED;
3994 flags |= GTK_CELL_RENDERER_FOCUSED;
3996 flags &= ~GTK_CELL_RENDERER_FOCUSED;
3998 background_area.x = cell_offset;
3999 background_area.width = column->width;
4001 cell_area = background_area;
4002 cell_area.y += vertical_separator / 2;
4003 cell_area.x += horizontal_separator / 2;
4004 cell_area.height -= vertical_separator;
4005 cell_area.width -= horizontal_separator;
4007 if (draw_vgrid_lines)
4009 if (list == first_column)
4011 cell_area.width -= grid_line_width / 2;
4013 else if (list == last_column)
4015 cell_area.x += grid_line_width / 2;
4016 cell_area.width -= grid_line_width / 2;
4020 cell_area.x += grid_line_width / 2;
4021 cell_area.width -= grid_line_width;
4025 if (draw_hgrid_lines)
4027 cell_area.y += grid_line_width / 2;
4028 cell_area.height -= grid_line_width;
4031 if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4033 cell_offset += column->width;
4037 pspp_sheet_view_column_cell_set_cell_data (column,
4038 tree_view->priv->model,
4041 /* Select the detail for drawing the cell. relevant
4042 * factors are parity, sortedness, and whether to
4045 if (allow_rules && tree_view->priv->has_rules)
4047 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4048 n_visible_columns >= 3)
4051 detail = "cell_odd_ruled_sorted";
4053 detail = "cell_even_ruled_sorted";
4058 detail = "cell_odd_ruled";
4060 detail = "cell_even_ruled";
4065 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4066 n_visible_columns >= 3)
4069 detail = "cell_odd_sorted";
4071 detail = "cell_even_sorted";
4076 detail = "cell_odd";
4078 detail = "cell_even";
4084 if (widget->state == GTK_STATE_INSENSITIVE)
4085 state = GTK_STATE_INSENSITIVE;
4086 else if (flags & GTK_CELL_RENDERER_SELECTED)
4087 state = GTK_STATE_SELECTED;
4089 state = GTK_STATE_NORMAL;
4091 /* Draw background */
4092 if (row_ending_details)
4094 char new_detail[128];
4096 is_first = (rtl ? !list->next : !list->prev);
4097 is_last = (rtl ? !list->prev : !list->next);
4099 /* (I don't like the snprintfs either, but couldn't find a
4102 if (is_first && is_last)
4103 g_snprintf (new_detail, 127, "%s", detail);
4105 g_snprintf (new_detail, 127, "%s_start", detail);
4107 g_snprintf (new_detail, 127, "%s_end", detail);
4109 g_snprintf (new_detail, 128, "%s_middle", detail);
4111 gtk_paint_flat_box (widget->style,
4120 background_area.width,
4121 background_area.height);
4125 gtk_paint_flat_box (widget->style,
4134 background_area.width,
4135 background_area.height);
4138 if (draw_hgrid_lines)
4140 if (background_area.y > 0)
4141 gdk_draw_line (event->window,
4142 tree_view->priv->grid_line_gc[widget->state],
4143 background_area.x, background_area.y,
4144 background_area.x + background_area.width,
4147 if (y_offset + max_height >= event->area.height)
4148 gdk_draw_line (event->window,
4149 tree_view->priv->grid_line_gc[widget->state],
4150 background_area.x, background_area.y + max_height,
4151 background_area.x + background_area.width,
4152 background_area.y + max_height);
4155 _pspp_sheet_view_column_cell_render (column,
4162 if (node == cursor && has_special_cell &&
4163 ((column == tree_view->priv->focus_column &&
4164 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4165 gtk_widget_has_focus (widget)) ||
4166 (column == tree_view->priv->edited_column)))
4168 _pspp_sheet_view_column_cell_draw_focus (column,
4176 cell_offset += column->width;
4179 if (cell_offset < event->area.x)
4181 gtk_paint_flat_box (widget->style,
4190 event->area.x - cell_offset,
4191 background_area.height);
4194 if (node == drag_highlight)
4196 /* Draw indicator for the drop
4198 gint highlight_y = -1;
4202 switch (tree_view->priv->drag_dest_pos)
4204 case PSPP_SHEET_VIEW_DROP_BEFORE:
4205 highlight_y = background_area.y - 1;
4206 if (highlight_y < 0)
4210 case PSPP_SHEET_VIEW_DROP_AFTER:
4211 highlight_y = background_area.y + background_area.height - 1;
4214 case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE:
4215 case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER:
4216 _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node);
4220 gdk_drawable_get_size (tree_view->priv->bin_window,
4223 if (row_ending_details)
4224 gtk_paint_focus (widget->style,
4225 tree_view->priv->bin_window,
4226 gtk_widget_get_state (widget),
4230 ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4231 : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4232 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4233 - focus_line_width / 2,
4234 width, ROW_HEIGHT (tree_view)
4235 - focus_line_width + 1);
4237 gtk_paint_focus (widget->style,
4238 tree_view->priv->bin_window,
4239 gtk_widget_get_state (widget),
4242 "treeview-drop-indicator",
4243 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4244 - focus_line_width / 2,
4245 width, ROW_HEIGHT (tree_view)
4246 - focus_line_width + 1);
4250 if (highlight_y >= 0)
4252 gdk_draw_line (event->window,
4253 widget->style->fg_gc[gtk_widget_get_state (widget)],
4256 rtl ? 0 : bin_window_width,
4261 /* draw the big row-spanning focus rectangle, if needed */
4262 if (!has_special_cell && node == cursor &&
4263 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4264 gtk_widget_has_focus (widget))
4266 gint tmp_y, tmp_height;
4268 GtkStateType focus_rect_state;
4271 flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
4272 (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
4273 (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE :
4276 gdk_drawable_get_size (tree_view->priv->bin_window,
4279 if (draw_hgrid_lines)
4281 tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node) + grid_line_width / 2;
4282 tmp_height = ROW_HEIGHT (tree_view) - grid_line_width;
4286 tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node);
4287 tmp_height = ROW_HEIGHT (tree_view);
4290 if (row_ending_details)
4291 gtk_paint_focus (widget->style,
4292 tree_view->priv->bin_window,
4297 ? (is_last ? "treeview" : "treeview-left" )
4298 : (is_last ? "treeview-right" : "treeview-middle" )),
4302 gtk_paint_focus (widget->style,
4303 tree_view->priv->bin_window,
4312 y_offset += max_height;
4316 node = pspp_sheet_view_node_next (tree_view, node);
4319 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
4323 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
4330 while (y_offset < event->area.height);
4333 pspp_sheet_view_draw_grid_lines (tree_view, event, n_visible_columns,
4336 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4338 GdkRectangle *rectangles;
4341 gdk_region_get_rectangles (event->region,
4345 while (n_rectangles--)
4346 pspp_sheet_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
4348 g_free (rectangles);
4352 gtk_tree_path_free (cursor_path);
4355 gtk_tree_path_free (drag_dest_path);
4361 pspp_sheet_view_expose (GtkWidget *widget,
4362 GdkEventExpose *event)
4364 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4366 if (event->window == tree_view->priv->bin_window)
4371 retval = pspp_sheet_view_bin_expose (widget, event);
4373 /* We can't just chain up to Container::expose as it will try to send the
4374 * event to the headers, so we handle propagating it to our children
4375 * (eg. widgets being edited) ourselves.
4377 tmp_list = tree_view->priv->children;
4380 PsppSheetViewChild *child = tmp_list->data;
4381 tmp_list = tmp_list->next;
4383 gtk_container_propagate_expose (GTK_CONTAINER (tree_view), child->widget, event);
4389 else if (event->window == tree_view->priv->header_window)
4391 gint n_visible_columns;
4394 gtk_paint_flat_box (widget->style,
4404 event->area.height);
4406 for (list = tree_view->priv->columns; list != NULL; list = list->next)
4408 PsppSheetViewColumn *column = list->data;
4410 if (column == tree_view->priv->drag_column || !column->visible)
4413 if (span_intersects (column->allocation.x, column->allocation.width,
4414 event->area.x, event->area.width)
4415 && column->button != NULL)
4416 gtk_container_propagate_expose (GTK_CONTAINER (tree_view),
4417 column->button, event);
4420 n_visible_columns = 0;
4421 for (list = tree_view->priv->columns; list; list = list->next)
4423 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4425 n_visible_columns ++;
4427 pspp_sheet_view_draw_grid_lines (tree_view,
4431 event->area.height);
4433 else if (event->window == tree_view->priv->drag_window)
4435 gtk_container_propagate_expose (GTK_CONTAINER (tree_view),
4436 tree_view->priv->drag_column->button,
4450 /* returns 0x1 when no column has been found -- yes it's hackish */
4451 static PsppSheetViewColumn *
4452 pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
4453 PsppSheetViewColumn *column,
4456 PsppSheetViewColumn *left_column = NULL;
4457 PsppSheetViewColumn *cur_column = NULL;
4460 if (!column->reorderable)
4461 return (PsppSheetViewColumn *)0x1;
4463 switch (drop_position)
4466 /* find first column where we can drop */
4467 tmp_list = tree_view->priv->columns;
4468 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4469 return (PsppSheetViewColumn *)0x1;
4473 g_assert (tmp_list);
4475 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4476 tmp_list = tmp_list->next;
4478 if (left_column && left_column->visible == FALSE)
4481 if (!tree_view->priv->column_drop_func)
4484 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4486 left_column = cur_column;
4493 if (!tree_view->priv->column_drop_func)
4496 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4499 return (PsppSheetViewColumn *)0x1;
4503 /* find first column after column where we can drop */
4504 tmp_list = tree_view->priv->columns;
4506 for (; tmp_list; tmp_list = tmp_list->next)
4507 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4510 if (!tmp_list || !tmp_list->next)
4511 return (PsppSheetViewColumn *)0x1;
4513 tmp_list = tmp_list->next;
4514 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4515 tmp_list = tmp_list->next;
4519 g_assert (tmp_list);
4521 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4522 tmp_list = tmp_list->next;
4524 if (left_column && left_column->visible == FALSE)
4526 left_column = cur_column;
4528 tmp_list = tmp_list->next;
4532 if (!tree_view->priv->column_drop_func)
4535 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4537 left_column = cur_column;
4544 if (!tree_view->priv->column_drop_func)
4547 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4550 return (PsppSheetViewColumn *)0x1;
4554 /* find first column before column where we can drop */
4555 tmp_list = tree_view->priv->columns;
4557 for (; tmp_list; tmp_list = tmp_list->next)
4558 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4561 if (!tmp_list || !tmp_list->prev)
4562 return (PsppSheetViewColumn *)0x1;
4564 tmp_list = tmp_list->prev;
4565 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4566 tmp_list = tmp_list->prev;
4570 g_assert (tmp_list);
4572 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4574 if (left_column && !left_column->visible)
4576 /*if (!tmp_list->prev)
4577 return (PsppSheetViewColumn *)0x1;
4580 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4581 tmp_list = tmp_list->prev->prev;
4584 cur_column = left_column;
4586 tmp_list = tmp_list->prev;
4590 if (!tree_view->priv->column_drop_func)
4593 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4596 cur_column = left_column;
4597 tmp_list = tmp_list->prev;
4600 if (!tree_view->priv->column_drop_func)
4603 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4606 return (PsppSheetViewColumn *)0x1;
4610 /* same as DROP_HOME case, but doing it backwards */
4611 tmp_list = g_list_last (tree_view->priv->columns);
4614 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4615 return (PsppSheetViewColumn *)0x1;
4619 g_assert (tmp_list);
4621 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4623 if (left_column && !left_column->visible)
4625 cur_column = left_column;
4626 tmp_list = tmp_list->prev;
4629 if (!tree_view->priv->column_drop_func)
4632 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4635 cur_column = left_column;
4636 tmp_list = tmp_list->prev;
4639 if (!tree_view->priv->column_drop_func)
4642 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4645 return (PsppSheetViewColumn *)0x1;
4649 return (PsppSheetViewColumn *)0x1;
4653 pspp_sheet_view_key_press (GtkWidget *widget,
4656 PsppSheetView *tree_view = (PsppSheetView *) widget;
4658 if (tree_view->priv->rubber_band_status)
4660 if (event->keyval == GDK_Escape)
4661 pspp_sheet_view_stop_rubber_band (tree_view);
4666 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4668 if (event->keyval == GDK_Escape)
4670 tree_view->priv->cur_reorder = NULL;
4671 pspp_sheet_view_button_release_drag_column (widget, NULL);
4676 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4678 GList *focus_column;
4681 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4683 for (focus_column = tree_view->priv->columns;
4685 focus_column = focus_column->next)
4687 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4689 if (column->button && gtk_widget_has_focus (column->button))
4694 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4695 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4696 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4698 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4700 if (!column->resizable)
4702 gtk_widget_error_bell (widget);
4706 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4707 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4709 gint old_width = column->resized_width;
4711 column->resized_width = MAX (column->resized_width,
4713 column->resized_width -= 2;
4714 if (column->resized_width < 0)
4715 column->resized_width = 0;
4717 if (column->min_width == -1)
4718 column->resized_width = MAX (column->button_request,
4719 column->resized_width);
4721 column->resized_width = MAX (column->min_width,
4722 column->resized_width);
4724 if (column->max_width != -1)
4725 column->resized_width = MIN (column->resized_width,
4728 column->use_resized_width = TRUE;
4730 if (column->resized_width != old_width)
4731 gtk_widget_queue_resize (widget);
4733 gtk_widget_error_bell (widget);
4735 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4736 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4738 gint old_width = column->resized_width;
4740 column->resized_width = MAX (column->resized_width,
4742 column->resized_width += 2;
4744 if (column->max_width != -1)
4745 column->resized_width = MIN (column->resized_width,
4748 column->use_resized_width = TRUE;
4750 if (column->resized_width != old_width)
4751 gtk_widget_queue_resize (widget);
4753 gtk_widget_error_bell (widget);
4760 (event->state & GDK_MOD1_MASK) &&
4761 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4762 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4763 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4764 || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4766 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4768 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4769 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4771 PsppSheetViewColumn *col;
4772 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
4773 if (col != (PsppSheetViewColumn *)0x1)
4774 pspp_sheet_view_move_column_after (tree_view, column, col);
4776 gtk_widget_error_bell (widget);
4778 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4779 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4781 PsppSheetViewColumn *col;
4782 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4783 if (col != (PsppSheetViewColumn *)0x1)
4784 pspp_sheet_view_move_column_after (tree_view, column, col);
4786 gtk_widget_error_bell (widget);
4788 else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4790 PsppSheetViewColumn *col;
4791 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4792 if (col != (PsppSheetViewColumn *)0x1)
4793 pspp_sheet_view_move_column_after (tree_view, column, col);
4795 gtk_widget_error_bell (widget);
4797 else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4799 PsppSheetViewColumn *col;
4800 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4801 if (col != (PsppSheetViewColumn *)0x1)
4802 pspp_sheet_view_move_column_after (tree_view, column, col);
4804 gtk_widget_error_bell (widget);
4811 /* Chain up to the parent class. It handles the keybindings. */
4812 if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4815 if (tree_view->priv->search_entry_avoid_unhandled_binding)
4817 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4821 /* We pass the event to the search_entry. If its text changes, then we start
4822 * the typeahead find capabilities. */
4823 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4824 && tree_view->priv->enable_search
4825 && !tree_view->priv->search_custom_entry_set)
4827 GdkEvent *new_event;
4829 const char *new_text;
4832 gboolean text_modified;
4833 gulong popup_menu_id;
4835 pspp_sheet_view_ensure_interactive_directory (tree_view);
4837 /* Make a copy of the current text */
4838 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4839 new_event = gdk_event_copy ((GdkEvent *) event);
4840 g_object_unref (((GdkEventKey *) new_event)->window);
4841 ((GdkEventKey *) new_event)->window = g_object_ref (tree_view->priv->search_window->window);
4842 gtk_widget_realize (tree_view->priv->search_window);
4844 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
4845 "popup-menu", G_CALLBACK (gtk_true),
4848 /* Move the entry off screen */
4849 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4850 gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4851 gdk_screen_get_width (screen) + 1,
4852 gdk_screen_get_height (screen) + 1);
4853 gtk_widget_show (tree_view->priv->search_window);
4855 /* Send the event to the window. If the preedit_changed signal is emitted
4856 * during this event, we will set priv->imcontext_changed */
4857 tree_view->priv->imcontext_changed = FALSE;
4858 retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4859 gdk_event_free (new_event);
4860 gtk_widget_hide (tree_view->priv->search_window);
4862 g_signal_handler_disconnect (tree_view->priv->search_entry,
4865 /* We check to make sure that the entry tried to handle the text, and that
4866 * the text has changed.
4868 new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
4869 text_modified = strcmp (old_text, new_text) != 0;
4871 if (tree_view->priv->imcontext_changed || /* we're in a preedit */
4872 (retval && text_modified)) /* ...or the text was modified */
4874 if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
4876 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
4881 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
4891 pspp_sheet_view_key_release (GtkWidget *widget,
4894 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4896 if (tree_view->priv->rubber_band_status)
4899 return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
4902 /* FIXME Is this function necessary? Can I get an enter_notify event
4903 * w/o either an expose event or a mouse motion event?
4906 pspp_sheet_view_enter_notify (GtkWidget *widget,
4907 GdkEventCrossing *event)
4909 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4913 /* Sanity check it */
4914 if (event->window != tree_view->priv->bin_window)
4917 if (tree_view->priv->row_count == 0)
4920 if (event->mode == GDK_CROSSING_GRAB ||
4921 event->mode == GDK_CROSSING_GTK_GRAB ||
4922 event->mode == GDK_CROSSING_GTK_UNGRAB ||
4923 event->mode == GDK_CROSSING_STATE_CHANGED)
4926 /* find the node internally */
4927 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
4930 pspp_sheet_view_find_offset (tree_view, new_y, &node);
4932 tree_view->priv->event_last_x = event->x;
4933 tree_view->priv->event_last_y = event->y;
4935 prelight_or_select (tree_view, node, event->x, event->y);
4941 pspp_sheet_view_leave_notify (GtkWidget *widget,
4942 GdkEventCrossing *event)
4944 PsppSheetView *tree_view;
4946 if (event->mode == GDK_CROSSING_GRAB)
4949 tree_view = PSPP_SHEET_VIEW (widget);
4951 if (tree_view->priv->prelight_node >= 0)
4952 _pspp_sheet_view_queue_draw_node (tree_view,
4953 tree_view->priv->prelight_node,
4956 tree_view->priv->event_last_x = -10000;
4957 tree_view->priv->event_last_y = -10000;
4959 prelight_or_select (tree_view,
4961 -1000, -1000); /* coords not possibly over an arrow */
4968 pspp_sheet_view_focus_out (GtkWidget *widget,
4969 GdkEventFocus *event)
4971 PsppSheetView *tree_view;
4973 tree_view = PSPP_SHEET_VIEW (widget);
4975 gtk_widget_queue_draw (widget);
4977 /* destroy interactive search dialog */
4978 if (tree_view->priv->search_window)
4979 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
4985 /* Incremental Reflow
4989 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
4994 y = pspp_sheet_view_node_find_offset (tree_view, node)
4995 - tree_view->priv->vadjustment->value
4996 + TREE_VIEW_HEADER_HEIGHT (tree_view);
4998 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
5000 GTK_WIDGET (tree_view)->allocation.width,
5001 tree_view->priv->fixed_height);
5005 node_is_visible (PsppSheetView *tree_view,
5011 y = pspp_sheet_view_node_find_offset (tree_view, node);
5012 height = ROW_HEIGHT (tree_view);
5014 if (y >= tree_view->priv->vadjustment->value &&
5015 y + height <= (tree_view->priv->vadjustment->value
5016 + tree_view->priv->vadjustment->page_size))
5022 /* Returns the row height. */
5024 validate_row (PsppSheetView *tree_view,
5029 PsppSheetViewColumn *column;
5030 GList *list, *first_column, *last_column;
5032 gint horizontal_separator;
5033 gint vertical_separator;
5034 gint focus_line_width;
5035 gboolean draw_vgrid_lines, draw_hgrid_lines;
5037 gint grid_line_width;
5038 gboolean wide_separators;
5039 gint separator_height;
5041 gtk_widget_style_get (GTK_WIDGET (tree_view),
5042 "focus-padding", &focus_pad,
5043 "focus-line-width", &focus_line_width,
5044 "horizontal-separator", &horizontal_separator,
5045 "vertical-separator", &vertical_separator,
5046 "grid-line-width", &grid_line_width,
5047 "wide-separators", &wide_separators,
5048 "separator-height", &separator_height,
5052 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5053 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5055 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5056 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5058 for (last_column = g_list_last (tree_view->priv->columns);
5059 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5060 last_column = last_column->prev)
5063 for (first_column = g_list_first (tree_view->priv->columns);
5064 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5065 first_column = first_column->next)
5068 for (list = tree_view->priv->columns; list; list = list->next)
5073 column = list->data;
5075 if (! column->visible)
5078 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5079 pspp_sheet_view_column_cell_get_size (column,
5081 &tmp_width, &tmp_height);
5083 tmp_height += vertical_separator;
5084 height = MAX (height, tmp_height);
5086 tmp_width = tmp_width + horizontal_separator;
5088 if (draw_vgrid_lines)
5090 if (list->data == first_column || list->data == last_column)
5091 tmp_width += grid_line_width / 2.0;
5093 tmp_width += grid_line_width;
5096 if (tmp_width > column->requested_width)
5097 column->requested_width = tmp_width;
5100 if (draw_hgrid_lines)
5101 height += grid_line_width;
5103 tree_view->priv->post_validation_flag = TRUE;
5109 validate_visible_area (PsppSheetView *tree_view)
5111 GtkTreePath *path = NULL;
5112 GtkTreePath *above_path = NULL;
5115 gboolean size_changed = FALSE;
5117 gint area_above = 0;
5118 gint area_below = 0;
5120 if (tree_view->priv->row_count == 0)
5123 if (tree_view->priv->scroll_to_path == NULL)
5126 total_height = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5128 if (total_height == 0)
5131 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5134 /* we are going to scroll, and will update dy */
5135 _pspp_sheet_view_find_node (tree_view, path, &node);
5136 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5138 if (tree_view->priv->scroll_to_use_align)
5140 gint height = ROW_HEIGHT (tree_view);
5141 area_above = (total_height - height) *
5142 tree_view->priv->scroll_to_row_align;
5143 area_below = total_height - area_above - height;
5144 area_above = MAX (area_above, 0);
5145 area_below = MAX (area_below, 0);
5150 * 1) row not visible
5154 gint height = ROW_HEIGHT (tree_view);
5156 dy = pspp_sheet_view_node_find_offset (tree_view, node);
5158 if (dy >= tree_view->priv->vadjustment->value &&
5159 dy + height <= (tree_view->priv->vadjustment->value
5160 + tree_view->priv->vadjustment->page_size))
5162 /* row visible: keep the row at the same position */
5163 area_above = dy - tree_view->priv->vadjustment->value;
5164 area_below = (tree_view->priv->vadjustment->value +
5165 tree_view->priv->vadjustment->page_size)
5170 /* row not visible */
5172 && dy + height <= tree_view->priv->vadjustment->page_size)
5174 /* row at the beginning -- fixed */
5176 area_below = tree_view->priv->vadjustment->page_size
5177 - area_above - height;
5179 else if (dy >= (tree_view->priv->vadjustment->upper -
5180 tree_view->priv->vadjustment->page_size))
5182 /* row at the end -- fixed */
5183 area_above = dy - (tree_view->priv->vadjustment->upper -
5184 tree_view->priv->vadjustment->page_size);
5185 area_below = tree_view->priv->vadjustment->page_size -
5186 area_above - height;
5190 area_above = tree_view->priv->vadjustment->page_size - height;
5196 /* row somewhere in the middle, bring it to the top
5200 area_below = total_height - height;
5206 /* the scroll to isn't valid; ignore it.
5209 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5210 tree_view->priv->scroll_to_path = NULL;
5214 above_path = gtk_tree_path_copy (path);
5216 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5217 * backwards is much slower then forward, as there is no iter_prev function.
5218 * We go forwards first in case we run out of tree. Then we go backwards to
5221 while (node >= 0 && area_below > 0)
5223 gboolean done = FALSE;
5226 node = pspp_sheet_view_node_next (tree_view, node);
5229 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5231 gtk_tree_path_next (path);
5234 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5244 area_below -= ROW_HEIGHT (tree_view);
5246 gtk_tree_path_free (path);
5248 /* If we ran out of tree, and have extra area_below left, we need to add it
5251 area_above += area_below;
5253 _pspp_sheet_view_find_node (tree_view, above_path, &node);
5255 /* We walk backwards */
5256 while (area_above > 0)
5258 node = pspp_sheet_view_node_prev (tree_view, node);
5260 /* Always find the new path in the tree. We cannot just assume
5261 * a gtk_tree_path_prev() is enough here, as there might be children
5262 * in between this node and the previous sibling node. If this
5263 * appears to be a performance hotspot in profiles, we can look into
5264 * intrigate logic for keeping path, node and iter in sync like
5265 * we do for forward walks. (Which will be hard because of the lacking
5272 gtk_tree_path_free (above_path);
5273 above_path = _pspp_sheet_view_find_path (tree_view, node);
5275 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5277 area_above -= ROW_HEIGHT (tree_view);
5280 /* set the dy here to scroll to the path,
5281 * and sync the top row accordingly
5283 pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5284 pspp_sheet_view_top_row_to_dy (tree_view);
5286 /* update width/height and queue a resize */
5289 GtkRequisition requisition;
5291 /* We temporarily guess a size, under the assumption that it will be the
5292 * same when we get our next size_allocate. If we don't do this, we'll be
5293 * in an inconsistent state if we call top_row_to_dy. */
5295 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5296 tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width);
5297 tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height);
5298 gtk_adjustment_changed (tree_view->priv->hadjustment);
5299 gtk_adjustment_changed (tree_view->priv->vadjustment);
5300 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5303 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5304 tree_view->priv->scroll_to_path = NULL;
5307 gtk_tree_path_free (above_path);
5309 if (tree_view->priv->scroll_to_column)
5311 tree_view->priv->scroll_to_column = NULL;
5313 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5317 initialize_fixed_height_mode (PsppSheetView *tree_view)
5319 if (!tree_view->priv->row_count)
5322 if (tree_view->priv->fixed_height_set)
5325 if (tree_view->priv->fixed_height < 0)
5332 path = _pspp_sheet_view_find_path (tree_view, node);
5333 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5335 tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5337 gtk_tree_path_free (path);
5339 g_object_notify (G_OBJECT (tree_view), "fixed-height");
5343 /* Our strategy for finding nodes to validate is a little convoluted. We find
5344 * the left-most uninvalidated node. We then try walking right, validating
5345 * nodes. Once we find a valid node, we repeat the previous process of finding
5346 * the first invalid node.
5350 validate_rows_handler (PsppSheetView *tree_view)
5352 initialize_fixed_height_mode (tree_view);
5353 if (tree_view->priv->validate_rows_timer)
5355 g_source_remove (tree_view->priv->validate_rows_timer);
5356 tree_view->priv->validate_rows_timer = 0;
5363 do_presize_handler (PsppSheetView *tree_view)
5365 GtkRequisition requisition;
5367 validate_visible_area (tree_view);
5368 tree_view->priv->presize_handler_timer = 0;
5370 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5373 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5375 tree_view->priv->hadjustment->upper = MAX (tree_view->priv->hadjustment->upper, (gfloat)requisition.width);
5376 tree_view->priv->vadjustment->upper = MAX (tree_view->priv->vadjustment->upper, (gfloat)requisition.height);
5377 gtk_adjustment_changed (tree_view->priv->hadjustment);
5378 gtk_adjustment_changed (tree_view->priv->vadjustment);
5379 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5385 presize_handler_callback (gpointer data)
5387 do_presize_handler (PSPP_SHEET_VIEW (data));
5393 install_presize_handler (PsppSheetView *tree_view)
5395 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5398 if (! tree_view->priv->presize_handler_timer)
5400 tree_view->priv->presize_handler_timer =
5401 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5403 if (! tree_view->priv->validate_rows_timer)
5405 tree_view->priv->validate_rows_timer =
5406 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5411 scroll_sync_handler (PsppSheetView *tree_view)
5413 if (tree_view->priv->height <= tree_view->priv->vadjustment->page_size)
5414 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5415 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5416 pspp_sheet_view_top_row_to_dy (tree_view);
5418 pspp_sheet_view_dy_to_top_row (tree_view);
5420 tree_view->priv->scroll_sync_timer = 0;
5426 install_scroll_sync_handler (PsppSheetView *tree_view)
5428 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5431 if (!tree_view->priv->scroll_sync_timer)
5433 tree_view->priv->scroll_sync_timer =
5434 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5439 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5443 gtk_tree_row_reference_free (tree_view->priv->top_row);
5447 tree_view->priv->top_row = NULL;
5448 tree_view->priv->top_row_dy = 0;
5452 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5453 tree_view->priv->top_row_dy = offset;
5457 /* Always call this iff dy is in the visible range. If the tree is empty, then
5458 * it's set to be NULL, and top_row_dy is 0;
5461 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5467 if (tree_view->priv->row_count == 0)
5469 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5473 offset = pspp_sheet_view_find_offset (tree_view,
5474 tree_view->priv->dy,
5479 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5483 path = _pspp_sheet_view_find_path (tree_view, node);
5484 pspp_sheet_view_set_top_row (tree_view, path, offset);
5485 gtk_tree_path_free (path);
5491 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5497 /* Avoid recursive calls */
5498 if (tree_view->priv->in_top_row_to_dy)
5501 if (tree_view->priv->top_row)
5502 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5509 _pspp_sheet_view_find_node (tree_view, path, &node);
5512 gtk_tree_path_free (path);
5516 /* keep dy and set new toprow */
5517 gtk_tree_row_reference_free (tree_view->priv->top_row);
5518 tree_view->priv->top_row = NULL;
5519 tree_view->priv->top_row_dy = 0;
5520 /* DO NOT install the idle handler */
5521 pspp_sheet_view_dy_to_top_row (tree_view);
5525 if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5527 /* new top row -- do NOT install the idle handler */
5528 pspp_sheet_view_dy_to_top_row (tree_view);
5532 new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5533 new_dy += tree_view->priv->top_row_dy;
5535 if (new_dy + tree_view->priv->vadjustment->page_size > tree_view->priv->height)
5536 new_dy = tree_view->priv->height - tree_view->priv->vadjustment->page_size;
5538 new_dy = MAX (0, new_dy);
5540 tree_view->priv->in_top_row_to_dy = TRUE;
5541 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5542 tree_view->priv->in_top_row_to_dy = FALSE;
5547 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5549 install_presize_handler (tree_view);
5555 set_source_row (GdkDragContext *context,
5556 GtkTreeModel *model,
5557 GtkTreePath *source_row)
5559 g_object_set_data_full (G_OBJECT (context),
5560 "gtk-tree-view-source-row",
5561 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5562 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5566 get_source_row (GdkDragContext *context)
5568 GtkTreeRowReference *ref =
5569 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5572 return gtk_tree_row_reference_get_path (ref);
5579 GtkTreeRowReference *dest_row;
5580 guint path_down_mode : 1;
5581 guint empty_view_drop : 1;
5582 guint drop_append_mode : 1;
5587 dest_row_free (gpointer data)
5589 DestRow *dr = (DestRow *)data;
5591 gtk_tree_row_reference_free (dr->dest_row);
5592 g_slice_free (DestRow, dr);
5596 set_dest_row (GdkDragContext *context,
5597 GtkTreeModel *model,
5598 GtkTreePath *dest_row,
5599 gboolean path_down_mode,
5600 gboolean empty_view_drop,
5601 gboolean drop_append_mode)
5607 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5612 dr = g_slice_new (DestRow);
5614 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5615 dr->path_down_mode = path_down_mode != FALSE;
5616 dr->empty_view_drop = empty_view_drop != FALSE;
5617 dr->drop_append_mode = drop_append_mode != FALSE;
5619 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5620 dr, (GDestroyNotify) dest_row_free);
5624 get_dest_row (GdkDragContext *context,
5625 gboolean *path_down_mode)
5628 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5632 GtkTreePath *path = NULL;
5635 *path_down_mode = dr->path_down_mode;
5638 path = gtk_tree_row_reference_get_path (dr->dest_row);
5639 else if (dr->empty_view_drop)
5640 path = gtk_tree_path_new_from_indices (0, -1);
5644 if (path && dr->drop_append_mode)
5645 gtk_tree_path_next (path);
5653 /* Get/set whether drag_motion requested the drag data and
5654 * drag_data_received should thus not actually insert the data,
5655 * since the data doesn't result from a drop.
5658 set_status_pending (GdkDragContext *context,
5659 GdkDragAction suggested_action)
5661 g_object_set_data (G_OBJECT (context),
5662 "gtk-tree-view-status-pending",
5663 GINT_TO_POINTER (suggested_action));
5666 static GdkDragAction
5667 get_status_pending (GdkDragContext *context)
5669 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5670 "gtk-tree-view-status-pending"));
5673 static TreeViewDragInfo*
5674 get_info (PsppSheetView *tree_view)
5676 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5680 destroy_info (TreeViewDragInfo *di)
5682 g_slice_free (TreeViewDragInfo, di);
5685 static TreeViewDragInfo*
5686 ensure_info (PsppSheetView *tree_view)
5688 TreeViewDragInfo *di;
5690 di = get_info (tree_view);
5694 di = g_slice_new0 (TreeViewDragInfo);
5696 g_object_set_data_full (G_OBJECT (tree_view),
5697 "gtk-tree-view-drag-info",
5699 (GDestroyNotify) destroy_info);
5706 remove_info (PsppSheetView *tree_view)
5708 g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5713 drag_scan_timeout (gpointer data)
5715 PsppSheetView *tree_view;
5717 GdkModifierType state;
5718 GtkTreePath *path = NULL;
5719 PsppSheetViewColumn *column = NULL;
5720 GdkRectangle visible_rect;
5722 GDK_THREADS_ENTER ();
5724 tree_view = PSPP_SHEET_VIEW (data);
5726 gdk_window_get_pointer (tree_view->priv->bin_window,
5729 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5731 /* See if we are near the edge. */
5732 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5733 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5734 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5735 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5737 pspp_sheet_view_get_path_at_pos (tree_view,
5738 tree_view->priv->bin_window,
5747 pspp_sheet_view_scroll_to_cell (tree_view,
5753 gtk_tree_path_free (path);
5757 GDK_THREADS_LEAVE ();
5764 add_scroll_timeout (PsppSheetView *tree_view)
5766 if (tree_view->priv->scroll_timeout == 0)
5768 tree_view->priv->scroll_timeout =
5769 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5774 remove_scroll_timeout (PsppSheetView *tree_view)
5776 if (tree_view->priv->scroll_timeout != 0)
5778 g_source_remove (tree_view->priv->scroll_timeout);
5779 tree_view->priv->scroll_timeout = 0;
5784 check_model_dnd (GtkTreeModel *model,
5785 GType required_iface,
5786 const gchar *signal)
5788 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5790 g_warning ("You must override the default '%s' handler "
5791 "on PsppSheetView when using models that don't support "
5792 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5793 "is to connect to '%s' and call "
5794 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5795 "the default handler from running. Look at the source code "
5796 "for the default handler in gtktreeview.c to get an idea what "
5797 "your handler should do. (gtktreeview.c is in the GTK source "
5798 "code.) If you're using GTK from a language other than C, "
5799 "there may be a more natural way to override default handlers, e.g. via derivation.",
5800 signal, g_type_name (required_iface), signal);
5808 scroll_row_timeout (gpointer data)
5810 PsppSheetView *tree_view = data;
5812 pspp_sheet_view_horizontal_autoscroll (tree_view);
5813 pspp_sheet_view_vertical_autoscroll (tree_view);
5815 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5816 pspp_sheet_view_update_rubber_band (tree_view);
5821 /* Returns TRUE if event should not be propagated to parent widgets */
5823 set_destination_row (PsppSheetView *tree_view,
5824 GdkDragContext *context,
5825 /* coordinates relative to the widget */
5828 GdkDragAction *suggested_action,
5831 GtkTreePath *path = NULL;
5832 PsppSheetViewDropPosition pos;
5833 PsppSheetViewDropPosition old_pos;
5834 TreeViewDragInfo *di;
5836 GtkTreePath *old_dest_path = NULL;
5837 gboolean can_drop = FALSE;
5839 *suggested_action = 0;
5842 widget = GTK_WIDGET (tree_view);
5844 di = get_info (tree_view);
5846 if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5848 /* someone unset us as a drag dest, note that if
5849 * we return FALSE drag_leave isn't called
5852 pspp_sheet_view_set_drag_dest_row (tree_view,
5854 PSPP_SHEET_VIEW_DROP_BEFORE);
5856 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5858 return FALSE; /* no longer a drop site */
5861 *target = gtk_drag_dest_find_target (widget, context,
5862 gtk_drag_dest_get_target_list (widget));
5863 if (*target == GDK_NONE)
5868 if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
5874 GtkTreeModel *model;
5876 /* the row got dropped on empty space, let's setup a special case
5880 gtk_tree_path_free (path);
5882 model = pspp_sheet_view_get_model (tree_view);
5884 n_children = gtk_tree_model_iter_n_children (model, NULL);
5887 pos = PSPP_SHEET_VIEW_DROP_AFTER;
5888 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
5892 pos = PSPP_SHEET_VIEW_DROP_BEFORE;
5893 path = gtk_tree_path_new_from_indices (0, -1);
5903 /* If we left the current row's "open" zone, unset the timeout for
5906 pspp_sheet_view_get_drag_dest_row (tree_view,
5911 gtk_tree_path_free (old_dest_path);
5913 if (TRUE /* FIXME if the location droppable predicate */)
5921 GtkWidget *source_widget;
5923 *suggested_action = context->suggested_action;
5924 source_widget = gtk_drag_get_source_widget (context);
5926 if (source_widget == widget)
5928 /* Default to MOVE, unless the user has
5929 * pressed ctrl or shift to affect available actions
5931 if ((context->actions & GDK_ACTION_MOVE) != 0)
5932 *suggested_action = GDK_ACTION_MOVE;
5935 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5940 /* can't drop here */
5941 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
5943 PSPP_SHEET_VIEW_DROP_BEFORE);
5947 gtk_tree_path_free (path);
5953 get_logical_dest_row (PsppSheetView *tree_view,
5954 gboolean *path_down_mode,
5955 gboolean *drop_append_mode)
5957 /* adjust path to point to the row the drop goes in front of */
5958 GtkTreePath *path = NULL;
5959 PsppSheetViewDropPosition pos;
5961 g_return_val_if_fail (path_down_mode != NULL, NULL);
5962 g_return_val_if_fail (drop_append_mode != NULL, NULL);
5964 *path_down_mode = FALSE;
5965 *drop_append_mode = 0;
5967 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
5972 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
5974 else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
5975 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
5976 *path_down_mode = TRUE;
5980 GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
5982 g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
5984 if (!gtk_tree_model_get_iter (model, &iter, path) ||
5985 !gtk_tree_model_iter_next (model, &iter))
5986 *drop_append_mode = 1;
5989 *drop_append_mode = 0;
5990 gtk_tree_path_next (path);
5998 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
5999 GdkEventMotion *event)
6001 GtkWidget *widget = GTK_WIDGET (tree_view);
6002 GdkDragContext *context;
6003 TreeViewDragInfo *di;
6004 GtkTreePath *path = NULL;
6006 gint cell_x, cell_y;
6007 GtkTreeModel *model;
6008 gboolean retval = FALSE;
6010 di = get_info (tree_view);
6012 if (di == NULL || !di->source_set)
6015 if (tree_view->priv->pressed_button < 0)
6018 if (!gtk_drag_check_threshold (widget,
6019 tree_view->priv->press_start_x,
6020 tree_view->priv->press_start_y,
6021 event->x, event->y))
6024 model = pspp_sheet_view_get_model (tree_view);
6029 button = tree_view->priv->pressed_button;
6030 tree_view->priv->pressed_button = -1;
6032 pspp_sheet_view_get_path_at_pos (tree_view,
6033 tree_view->priv->press_start_x,
6034 tree_view->priv->press_start_y,
6043 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6044 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6048 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6051 /* Now we can begin the drag */
6055 context = gtk_drag_begin (widget,
6056 gtk_drag_source_get_target_list (widget),
6061 set_source_row (context, model, path);
6065 gtk_tree_path_free (path);
6072 pspp_sheet_view_drag_begin (GtkWidget *widget,
6073 GdkDragContext *context)
6075 PsppSheetView *tree_view;
6076 GtkTreePath *path = NULL;
6077 gint cell_x, cell_y;
6079 TreeViewDragInfo *di;
6081 tree_view = PSPP_SHEET_VIEW (widget);
6083 /* if the user uses a custom DND source impl, we don't set the icon here */
6084 di = get_info (tree_view);
6086 if (di == NULL || !di->source_set)
6089 pspp_sheet_view_get_path_at_pos (tree_view,
6090 tree_view->priv->press_start_x,
6091 tree_view->priv->press_start_y,
6097 g_return_if_fail (path != NULL);
6099 row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6102 gtk_drag_set_icon_pixmap (context,
6103 gdk_drawable_get_colormap (row_pix),
6106 /* the + 1 is for the black border in the icon */
6107 tree_view->priv->press_start_x + 1,
6110 g_object_unref (row_pix);
6111 gtk_tree_path_free (path);
6115 pspp_sheet_view_drag_end (GtkWidget *widget,
6116 GdkDragContext *context)
6121 /* Default signal implementations for the drag signals */
6123 pspp_sheet_view_drag_data_get (GtkWidget *widget,
6124 GdkDragContext *context,
6125 GtkSelectionData *selection_data,
6129 PsppSheetView *tree_view;
6130 GtkTreeModel *model;
6131 TreeViewDragInfo *di;
6132 GtkTreePath *source_row;
6134 tree_view = PSPP_SHEET_VIEW (widget);
6136 model = pspp_sheet_view_get_model (tree_view);
6141 di = get_info (PSPP_SHEET_VIEW (widget));
6146 source_row = get_source_row (context);
6148 if (source_row == NULL)
6151 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6152 * any model; for DragSource models there are some other targets
6156 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6157 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6162 /* If drag_data_get does nothing, try providing row data. */
6163 if (selection_data->target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6165 gtk_tree_set_row_drag_data (selection_data,
6171 gtk_tree_path_free (source_row);
6176 pspp_sheet_view_drag_data_delete (GtkWidget *widget,
6177 GdkDragContext *context)
6179 TreeViewDragInfo *di;
6180 GtkTreeModel *model;
6181 PsppSheetView *tree_view;
6182 GtkTreePath *source_row;
6184 tree_view = PSPP_SHEET_VIEW (widget);
6185 model = pspp_sheet_view_get_model (tree_view);
6187 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6190 di = get_info (tree_view);
6195 source_row = get_source_row (context);
6197 if (source_row == NULL)
6200 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6203 gtk_tree_path_free (source_row);
6205 set_source_row (context, NULL, NULL);
6209 pspp_sheet_view_drag_leave (GtkWidget *widget,
6210 GdkDragContext *context,
6213 /* unset any highlight row */
6214 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6216 PSPP_SHEET_VIEW_DROP_BEFORE);
6218 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6223 pspp_sheet_view_drag_motion (GtkWidget *widget,
6224 GdkDragContext *context,
6225 /* coordinates relative to the widget */
6231 GtkTreePath *path = NULL;
6232 PsppSheetViewDropPosition pos;
6233 PsppSheetView *tree_view;
6234 GdkDragAction suggested_action = 0;
6237 tree_view = PSPP_SHEET_VIEW (widget);
6239 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6242 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6244 /* we only know this *after* set_desination_row */
6245 empty = tree_view->priv->empty_view_drop;
6247 if (path == NULL && !empty)
6249 /* Can't drop here. */
6250 gdk_drag_status (context, 0, time);
6254 if (tree_view->priv->open_dest_timeout == 0 &&
6255 (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6256 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6262 add_scroll_timeout (tree_view);
6265 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6267 /* Request data so we can use the source row when
6268 * determining whether to accept the drop
6270 set_status_pending (context, suggested_action);
6271 gtk_drag_get_data (widget, context, target, time);
6275 set_status_pending (context, 0);
6276 gdk_drag_status (context, suggested_action, time);
6281 gtk_tree_path_free (path);
6288 pspp_sheet_view_drag_drop (GtkWidget *widget,
6289 GdkDragContext *context,
6290 /* coordinates relative to the widget */
6295 PsppSheetView *tree_view;
6297 GdkDragAction suggested_action = 0;
6298 GdkAtom target = GDK_NONE;
6299 TreeViewDragInfo *di;
6300 GtkTreeModel *model;
6301 gboolean path_down_mode;
6302 gboolean drop_append_mode;
6304 tree_view = PSPP_SHEET_VIEW (widget);
6306 model = pspp_sheet_view_get_model (tree_view);
6308 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6310 di = get_info (tree_view);
6315 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6318 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6321 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6323 if (target != GDK_NONE && path != NULL)
6325 /* in case a motion had requested drag data, change things so we
6326 * treat drag data receives as a drop.
6328 set_status_pending (context, 0);
6329 set_dest_row (context, model, path,
6330 path_down_mode, tree_view->priv->empty_view_drop,
6335 gtk_tree_path_free (path);
6337 /* Unset this thing */
6338 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6340 PSPP_SHEET_VIEW_DROP_BEFORE);
6342 if (target != GDK_NONE)
6344 gtk_drag_get_data (widget, context, target, time);
6352 pspp_sheet_view_drag_data_received (GtkWidget *widget,
6353 GdkDragContext *context,
6354 /* coordinates relative to the widget */
6357 GtkSelectionData *selection_data,
6362 TreeViewDragInfo *di;
6363 gboolean accepted = FALSE;
6364 GtkTreeModel *model;
6365 PsppSheetView *tree_view;
6366 GtkTreePath *dest_row;
6367 GdkDragAction suggested_action;
6368 gboolean path_down_mode;
6369 gboolean drop_append_mode;
6371 tree_view = PSPP_SHEET_VIEW (widget);
6373 model = pspp_sheet_view_get_model (tree_view);
6375 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6378 di = get_info (tree_view);
6383 suggested_action = get_status_pending (context);
6385 if (suggested_action)
6387 /* We are getting this data due to a request in drag_motion,
6388 * rather than due to a request in drag_drop, so we are just
6389 * supposed to call drag_status, not actually paste in the
6392 path = get_logical_dest_row (tree_view, &path_down_mode,
6396 suggested_action = 0;
6397 else if (path_down_mode)
6398 gtk_tree_path_down (path);
6400 if (suggested_action)
6402 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6408 path_down_mode = FALSE;
6409 gtk_tree_path_up (path);
6411 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6414 suggested_action = 0;
6417 suggested_action = 0;
6421 gdk_drag_status (context, suggested_action, time);
6424 gtk_tree_path_free (path);
6426 /* If you can't drop, remove user drop indicator until the next motion */
6427 if (suggested_action == 0)
6428 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6430 PSPP_SHEET_VIEW_DROP_BEFORE);
6435 dest_row = get_dest_row (context, &path_down_mode);
6437 if (dest_row == NULL)
6440 if (selection_data->length >= 0)
6444 gtk_tree_path_down (dest_row);
6445 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6446 dest_row, selection_data))
6447 gtk_tree_path_up (dest_row);
6451 if (selection_data->length >= 0)
6453 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6459 gtk_drag_finish (context,
6461 (context->action == GDK_ACTION_MOVE),
6464 if (gtk_tree_path_get_depth (dest_row) == 1
6465 && gtk_tree_path_get_indices (dest_row)[0] == 0)
6467 /* special special case drag to "0", scroll to first item */
6468 if (!tree_view->priv->scroll_to_path)
6469 pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6472 gtk_tree_path_free (dest_row);
6475 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6480 /* GtkContainer Methods
6485 pspp_sheet_view_remove (GtkContainer *container,
6488 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6489 PsppSheetViewChild *child = NULL;
6492 tmp_list = tree_view->priv->children;
6495 child = tmp_list->data;
6496 if (child->widget == widget)
6498 gtk_widget_unparent (widget);
6500 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6501 g_list_free_1 (tmp_list);
6502 g_slice_free (PsppSheetViewChild, child);
6506 tmp_list = tmp_list->next;
6509 tmp_list = tree_view->priv->columns;
6513 PsppSheetViewColumn *column;
6514 column = tmp_list->data;
6515 if (column->button == widget)
6517 gtk_widget_unparent (widget);
6520 tmp_list = tmp_list->next;
6525 pspp_sheet_view_forall (GtkContainer *container,
6526 gboolean include_internals,
6527 GtkCallback callback,
6528 gpointer callback_data)
6530 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6531 PsppSheetViewChild *child = NULL;
6532 PsppSheetViewColumn *column;
6535 tmp_list = tree_view->priv->children;
6538 child = tmp_list->data;
6539 tmp_list = tmp_list->next;
6541 (* callback) (child->widget, callback_data);
6543 if (include_internals == FALSE)
6546 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6548 column = tmp_list->data;
6551 (* callback) (column->button, callback_data);
6555 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6556 * cells. If so we draw one big row-spanning focus rectangle.
6559 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6563 if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6564 return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6566 for (list = tree_view->priv->columns; list; list = list->next)
6568 if (!((PsppSheetViewColumn *)list->data)->visible)
6570 if (_pspp_sheet_view_column_count_special_cells (list->data))
6578 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6579 PsppSheetViewColumn *focus_column,
6580 gboolean clamp_column_visible)
6582 g_return_if_fail (focus_column != NULL);
6584 tree_view->priv->focus_column = focus_column;
6585 if (!focus_column->button)
6587 pspp_sheet_view_column_set_need_button (focus_column, TRUE);
6588 g_return_if_fail (focus_column->button != NULL);
6591 if (GTK_CONTAINER (tree_view)->focus_child != focus_column->button)
6592 gtk_widget_grab_focus (focus_column->button);
6594 if (clamp_column_visible)
6595 pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6598 /* Returns TRUE if the focus is within the headers, after the focus operation is
6602 pspp_sheet_view_header_focus (PsppSheetView *tree_view,
6603 GtkDirectionType dir,
6604 gboolean clamp_column_visible)
6606 GtkWidget *focus_child;
6607 PsppSheetViewColumn *focus_column;
6608 GList *last_column, *first_column;
6612 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6615 focus_child = GTK_CONTAINER (tree_view)->focus_child;
6617 first_column = tree_view->priv->columns;
6618 while (first_column)
6620 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6622 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6624 first_column = first_column->next;
6627 /* No headers are visible, or are focusable. We can't focus in or out.
6629 if (first_column == NULL)
6632 last_column = g_list_last (tree_view->priv->columns);
6635 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6637 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6639 last_column = last_column->prev;
6643 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6647 case GTK_DIR_TAB_BACKWARD:
6648 case GTK_DIR_TAB_FORWARD:
6651 if (focus_child == NULL)
6653 if (tree_view->priv->focus_column != NULL &&
6654 pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6655 focus_column = tree_view->priv->focus_column;
6657 focus_column = first_column->data;
6658 pspp_sheet_view_focus_column (tree_view, focus_column,
6659 clamp_column_visible);
6666 if (focus_child == NULL)
6668 if (tree_view->priv->focus_column != NULL)
6669 focus_column = tree_view->priv->focus_column;
6670 else if (dir == GTK_DIR_LEFT)
6671 focus_column = last_column->data;
6673 focus_column = first_column->data;
6674 pspp_sheet_view_focus_column (tree_view, focus_column,
6675 clamp_column_visible);
6679 if (gtk_widget_child_focus (focus_child, dir))
6681 /* The focus moves inside the button. */
6682 /* This is probably a great example of bad UI */
6683 if (clamp_column_visible)
6684 pspp_sheet_view_clamp_column_visible (tree_view,
6685 tree_view->priv->focus_column,
6690 /* We need to move the focus among the row of buttons. */
6691 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6692 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6695 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6696 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6698 gtk_widget_error_bell (GTK_WIDGET (tree_view));
6704 PsppSheetViewColumn *column;
6706 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6707 tmp_list = tmp_list->next;
6709 tmp_list = tmp_list->prev;
6711 if (tmp_list == NULL)
6713 g_warning ("Internal button not found");
6716 column = tmp_list->data;
6717 if (column->visible &&
6718 pspp_sheet_view_column_can_focus (column))
6720 pspp_sheet_view_column_set_need_button (column, TRUE);
6723 pspp_sheet_view_focus_column (tree_view, column,
6724 clamp_column_visible);
6732 g_assert_not_reached ();
6739 /* This function returns in 'path' the first focusable path, if the given path
6740 * is already focusable, it's the returned one.
6744 search_first_focusable_path (PsppSheetView *tree_view,
6746 gboolean search_forward,
6749 /* XXX this function is trivial given that the sheetview doesn't support
6753 if (!path || !*path)
6756 _pspp_sheet_view_find_node (tree_view, *path, &node);
6764 return (*path != NULL);
6768 pspp_sheet_view_focus (GtkWidget *widget,
6769 GtkDirectionType direction)
6771 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6772 GtkContainer *container = GTK_CONTAINER (widget);
6773 GtkWidget *focus_child;
6775 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6778 focus_child = container->focus_child;
6780 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6781 /* Case 1. Headers currently have focus. */
6788 pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6790 case GTK_DIR_TAB_BACKWARD:
6793 case GTK_DIR_TAB_FORWARD:
6795 gtk_widget_grab_focus (widget);
6798 g_assert_not_reached ();
6803 /* Case 2. We don't have focus at all. */
6804 if (!gtk_widget_has_focus (widget))
6806 if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6807 gtk_widget_grab_focus (widget);
6811 /* Case 3. We have focus already. */
6812 if (direction == GTK_DIR_TAB_BACKWARD)
6813 return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6814 else if (direction == GTK_DIR_TAB_FORWARD)
6817 /* Other directions caught by the keybindings */
6818 gtk_widget_grab_focus (widget);
6823 pspp_sheet_view_grab_focus (GtkWidget *widget)
6825 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6827 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6831 pspp_sheet_view_style_set (GtkWidget *widget,
6832 GtkStyle *previous_style)
6834 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6836 PsppSheetViewColumn *column;
6838 if (gtk_widget_get_realized (widget))
6841 PsppSheetViewPrivate *priv = PSPP_SHEET_VIEW (widget)->priv;
6843 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
6844 gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
6845 gtk_style_set_background (widget->style, tree_view->priv->header_window, GTK_STATE_NORMAL);
6846 for (i = 0; i < 5 ; ++i)
6848 g_object_unref (priv->grid_line_gc[i]);
6849 priv->grid_line_gc[i] = gdk_gc_new (widget->window);
6850 gdk_gc_copy (priv->grid_line_gc[i], widget->style->text_aa_gc[i]);
6853 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6856 gtk_widget_style_get (widget,
6857 "expander-size", &tree_view->priv->expander_size,
6859 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6861 for (list = tree_view->priv->columns; list; list = list->next)
6863 column = list->data;
6864 _pspp_sheet_view_column_cell_set_dirty (column);
6867 tree_view->priv->fixed_height = -1;
6869 /* Invalidate cached button style. */
6870 if (tree_view->priv->button_style)
6872 g_object_unref (tree_view->priv->button_style);
6873 tree_view->priv->button_style = NULL;
6876 gtk_widget_queue_resize (widget);
6881 pspp_sheet_view_set_focus_child (GtkContainer *container,
6884 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6887 for (list = tree_view->priv->columns; list; list = list->next)
6889 if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
6891 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
6896 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
6900 pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
6901 GtkAdjustment *hadj,
6902 GtkAdjustment *vadj)
6904 gboolean need_adjust = FALSE;
6906 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
6909 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
6911 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6913 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
6915 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
6917 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
6919 g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
6920 pspp_sheet_view_adjustment_changed,
6922 g_object_unref (tree_view->priv->hadjustment);
6925 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
6927 g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
6928 pspp_sheet_view_adjustment_changed,
6930 g_object_unref (tree_view->priv->vadjustment);
6933 if (tree_view->priv->hadjustment != hadj)
6935 tree_view->priv->hadjustment = hadj;
6936 g_object_ref_sink (tree_view->priv->hadjustment);
6938 g_signal_connect (tree_view->priv->hadjustment, "value-changed",
6939 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6944 if (tree_view->priv->vadjustment != vadj)
6946 tree_view->priv->vadjustment = vadj;
6947 g_object_ref_sink (tree_view->priv->vadjustment);
6949 g_signal_connect (tree_view->priv->vadjustment, "value-changed",
6950 G_CALLBACK (pspp_sheet_view_adjustment_changed),
6956 pspp_sheet_view_adjustment_changed (NULL, tree_view);
6961 pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
6962 GtkMovementStep step,
6965 GdkModifierType state;
6967 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
6968 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
6969 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
6970 step == GTK_MOVEMENT_DISPLAY_LINES ||
6971 step == GTK_MOVEMENT_PAGES ||
6972 step == GTK_MOVEMENT_BUFFER_ENDS ||
6973 step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
6975 if (tree_view->priv->row_count == 0)
6977 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
6980 pspp_sheet_view_stop_editing (tree_view, FALSE);
6981 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
6982 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
6984 if (gtk_get_current_event_state (&state))
6986 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
6987 tree_view->priv->ctrl_pressed = TRUE;
6988 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
6989 tree_view->priv->shift_pressed = TRUE;
6991 /* else we assume not pressed */
6995 case GTK_MOVEMENT_LOGICAL_POSITIONS:
6996 pspp_sheet_view_move_cursor_tab (tree_view, count);
6998 case GTK_MOVEMENT_VISUAL_POSITIONS:
6999 pspp_sheet_view_move_cursor_left_right (tree_view, count);
7001 case GTK_MOVEMENT_DISPLAY_LINES:
7002 pspp_sheet_view_move_cursor_up_down (tree_view, count);
7004 case GTK_MOVEMENT_PAGES:
7005 pspp_sheet_view_move_cursor_page_up_down (tree_view, count);
7007 case GTK_MOVEMENT_BUFFER_ENDS:
7008 pspp_sheet_view_move_cursor_start_end (tree_view, count);
7010 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7011 pspp_sheet_view_move_cursor_line_start_end (tree_view, count);
7014 g_assert_not_reached ();
7017 tree_view->priv->ctrl_pressed = FALSE;
7018 tree_view->priv->shift_pressed = FALSE;
7024 pspp_sheet_view_put (PsppSheetView *tree_view,
7025 GtkWidget *child_widget,
7026 /* in bin_window coordinates */
7032 PsppSheetViewChild *child;
7034 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7035 g_return_if_fail (GTK_IS_WIDGET (child_widget));
7037 child = g_slice_new (PsppSheetViewChild);
7039 child->widget = child_widget;
7042 child->width = width;
7043 child->height = height;
7045 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7047 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7048 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7050 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7054 _pspp_sheet_view_child_move_resize (PsppSheetView *tree_view,
7056 /* in tree coordinates */
7062 PsppSheetViewChild *child = NULL;
7064 GdkRectangle allocation;
7066 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7067 g_return_if_fail (GTK_IS_WIDGET (widget));
7069 for (list = tree_view->priv->children; list; list = list->next)
7071 if (((PsppSheetViewChild *)list->data)->widget == widget)
7080 allocation.x = child->x = x;
7081 allocation.y = child->y = y;
7082 allocation.width = child->width = width;
7083 allocation.height = child->height = height;
7085 if (gtk_widget_get_realized (widget))
7086 gtk_widget_size_allocate (widget, &allocation);
7090 /* TreeModel Callbacks
7094 pspp_sheet_view_row_changed (GtkTreeModel *model,
7099 PsppSheetView *tree_view = (PsppSheetView *)data;
7101 gboolean free_path = FALSE;
7102 GtkTreePath *cursor_path;
7104 g_return_if_fail (path != NULL || iter != NULL);
7106 if (tree_view->priv->cursor != NULL)
7107 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7111 if (tree_view->priv->edited_column &&
7112 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7113 pspp_sheet_view_stop_editing (tree_view, TRUE);
7115 if (cursor_path != NULL)
7116 gtk_tree_path_free (cursor_path);
7120 path = gtk_tree_model_get_path (model, iter);
7123 else if (iter == NULL)
7124 gtk_tree_model_get_iter (model, iter, path);
7126 _pspp_sheet_view_find_node (tree_view,
7132 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7133 pspp_sheet_view_node_queue_redraw (tree_view, node);
7137 gtk_tree_path_free (path);
7141 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7146 PsppSheetView *tree_view = (PsppSheetView *) data;
7149 gint height = tree_view->priv->fixed_height;
7150 gboolean free_path = FALSE;
7151 gboolean node_visible = TRUE;
7153 g_return_if_fail (path != NULL || iter != NULL);
7157 path = gtk_tree_model_get_path (model, iter);
7160 else if (iter == NULL)
7161 gtk_tree_model_get_iter (model, iter, path);
7163 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7165 /* Update all row-references */
7166 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7167 indices = gtk_tree_path_get_indices (path);
7168 tmpnode = indices[0];
7170 range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7174 if (node_visible && node_is_visible (tree_view, tmpnode))
7175 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7177 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7180 install_presize_handler (tree_view);
7182 gtk_tree_path_free (path);
7186 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7190 PsppSheetView *tree_view = (PsppSheetView *)data;
7193 g_return_if_fail (path != NULL);
7195 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7197 _pspp_sheet_view_find_node (tree_view, path, &node);
7202 range_tower_delete (tree_view->priv->selected, node, 1);
7204 /* Ensure we don't have a dangling pointer to a dead node */
7205 ensure_unprelighted (tree_view);
7207 /* Cancel editting if we've started */
7208 pspp_sheet_view_stop_editing (tree_view, TRUE);
7210 if (tree_view->priv->destroy_count_func)
7212 gint child_count = 0;
7213 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7216 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7218 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7220 gtk_tree_row_reference_free (tree_view->priv->top_row);
7221 tree_view->priv->top_row = NULL;
7224 install_scroll_sync_handler (tree_view);
7226 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7229 if (helper_data.changed)
7230 g_signal_emit_by_name (tree_view->priv->selection, "changed");
7235 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7236 GtkTreePath *parent,
7241 PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7244 /* XXX need to adjust selection */
7245 len = gtk_tree_model_iter_n_children (model, iter);
7250 gtk_tree_row_reference_reordered (G_OBJECT (data),
7255 if (gtk_tree_path_get_depth (parent) != 0)
7258 if (tree_view->priv->edited_column)
7259 pspp_sheet_view_stop_editing (tree_view, TRUE);
7261 /* we need to be unprelighted */
7262 ensure_unprelighted (tree_view);
7264 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7266 pspp_sheet_view_dy_to_top_row (tree_view);
7270 /* Internal tree functions
7275 pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view,
7276 PsppSheetViewColumn *column,
7280 PsppSheetViewColumn *tmp_column = NULL;
7291 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7294 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7296 list = (rtl ? list->prev : list->next))
7298 tmp_column = list->data;
7300 if (tmp_column == column)
7303 if (tmp_column->visible)
7304 total_width += tmp_column->width;
7307 if (tmp_column != column)
7309 g_warning (G_STRLOC": passed-in column isn't in the tree");
7318 if (column->visible)
7319 *x2 = total_width + column->width;
7321 *x2 = total_width; /* width of 0 */
7325 /* Make sure the node is visible vertically */
7327 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7330 gint node_dy, height;
7331 GtkTreePath *path = NULL;
7333 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7336 /* just return if the node is visible, avoiding a costly expose */
7337 node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7338 height = ROW_HEIGHT (tree_view);
7339 if (node_dy >= tree_view->priv->vadjustment->value
7340 && node_dy + height <= (tree_view->priv->vadjustment->value
7341 + tree_view->priv->vadjustment->page_size))
7344 path = _pspp_sheet_view_find_path (tree_view, node);
7347 /* We process updates because we want to clear old selected items when we scroll.
7348 * if this is removed, we get a "selection streak" at the bottom. */
7349 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7350 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7351 gtk_tree_path_free (path);
7356 pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
7357 PsppSheetViewColumn *column,
7358 gboolean focus_to_cell)
7365 x = column->allocation.x;
7366 width = column->allocation.width;
7368 if (width > tree_view->priv->hadjustment->page_size)
7370 /* The column is larger than the horizontal page size. If the
7371 * column has cells which can be focussed individually, then we make
7372 * sure the cell which gets focus is fully visible (if even the
7373 * focus cell is bigger than the page size, we make sure the
7374 * left-hand side of the cell is visible).
7376 * If the column does not have those so-called special cells, we
7377 * make sure the left-hand side of the column is visible.
7380 if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7382 GtkTreePath *cursor_path;
7383 GdkRectangle background_area, cell_area, focus_area;
7385 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7387 pspp_sheet_view_get_cell_area (tree_view,
7388 cursor_path, column, &cell_area);
7389 pspp_sheet_view_get_background_area (tree_view,
7390 cursor_path, column,
7393 gtk_tree_path_free (cursor_path);
7395 _pspp_sheet_view_column_get_focus_area (column,
7401 width = focus_area.width;
7403 if (width < tree_view->priv->hadjustment->page_size)
7405 if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) < (x + width))
7406 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7407 x + width - tree_view->priv->hadjustment->page_size);
7408 else if (tree_view->priv->hadjustment->value > x)
7409 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7413 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7415 tree_view->priv->hadjustment->lower,
7416 tree_view->priv->hadjustment->upper
7417 - tree_view->priv->hadjustment->page_size));
7421 if ((tree_view->priv->hadjustment->value + tree_view->priv->hadjustment->page_size) < (x + width))
7422 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7423 x + width - tree_view->priv->hadjustment->page_size);
7424 else if (tree_view->priv->hadjustment->value > x)
7425 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7430 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7435 path = gtk_tree_path_new ();
7437 gtk_tree_path_append_index (path, node);
7442 _pspp_sheet_view_find_node (PsppSheetView *tree_view,
7446 gint *indices = gtk_tree_path_get_indices (path);
7447 gint depth = gtk_tree_path_get_depth (path);
7450 if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7456 pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
7459 gboolean add_shifted_binding,
7460 GtkMovementStep step,
7464 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7469 if (add_shifted_binding)
7470 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7475 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7478 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7483 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7490 pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view,
7491 PsppSheetViewColumn *column)
7493 PsppSheetViewColumn *left_column;
7494 PsppSheetViewColumn *cur_column = NULL;
7495 PsppSheetViewColumnReorder *reorder;
7500 /* We want to precalculate the motion list such that we know what column slots
7504 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7506 /* First, identify all possible drop spots */
7508 tmp_list = g_list_last (tree_view->priv->columns);
7510 tmp_list = g_list_first (tree_view->priv->columns);
7514 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7515 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7517 if (cur_column->visible == FALSE)
7520 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7521 if (left_column != column && cur_column != column &&
7522 tree_view->priv->column_drop_func &&
7523 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7525 left_column = cur_column;
7528 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7529 reorder->left_column = left_column;
7530 left_column = reorder->right_column = cur_column;
7532 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7535 /* Add the last one */
7536 if (tree_view->priv->column_drop_func == NULL ||
7537 ((left_column != column) &&
7538 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7540 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7541 reorder->left_column = left_column;
7542 reorder->right_column = NULL;
7543 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7546 /* We quickly check to see if it even makes sense to reorder columns. */
7547 /* If there is nothing that can be moved, then we return */
7549 if (tree_view->priv->column_drag_info == NULL)
7552 /* We know there are always 2 slots possbile, as you can always return column. */
7553 /* If that's all there is, return */
7554 if (tree_view->priv->column_drag_info->next == NULL ||
7555 (tree_view->priv->column_drag_info->next->next == NULL &&
7556 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7557 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7559 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7560 g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7561 g_list_free (tree_view->priv->column_drag_info);
7562 tree_view->priv->column_drag_info = NULL;
7565 /* We fill in the ranges for the columns, now that we've isolated them */
7566 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7568 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7570 reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7572 reorder->left_align = left;
7573 if (tmp_list->next != NULL)
7575 g_assert (tmp_list->next->data);
7576 left = reorder->right_align = (reorder->right_column->allocation.x +
7577 reorder->right_column->allocation.width +
7578 ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7584 gdk_drawable_get_size (tree_view->priv->header_window, &width, NULL);
7585 reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7591 _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view,
7592 PsppSheetViewColumn *column)
7594 GdkEvent *send_event;
7595 GtkAllocation allocation;
7596 gint x, y, width, height;
7597 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7598 GdkDisplay *display = gdk_screen_get_display (screen);
7600 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7601 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7602 g_return_if_fail (column->button);
7604 pspp_sheet_view_set_column_drag_info (tree_view, column);
7606 if (tree_view->priv->column_drag_info == NULL)
7609 if (tree_view->priv->drag_window == NULL)
7611 GdkWindowAttr attributes;
7612 guint attributes_mask;
7614 attributes.window_type = GDK_WINDOW_CHILD;
7615 attributes.wclass = GDK_INPUT_OUTPUT;
7616 attributes.x = column->allocation.x;
7618 attributes.width = column->allocation.width;
7619 attributes.height = column->allocation.height;
7620 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7621 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
7622 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7623 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
7625 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7628 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7631 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7632 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7634 gtk_grab_remove (column->button);
7636 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7637 send_event->crossing.send_event = TRUE;
7638 send_event->crossing.window = g_object_ref (GTK_BUTTON (column->button)->event_window);
7639 send_event->crossing.subwindow = NULL;
7640 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7641 send_event->crossing.time = GDK_CURRENT_TIME;
7643 gtk_propagate_event (column->button, send_event);
7644 gdk_event_free (send_event);
7646 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7647 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7648 send_event->button.send_event = TRUE;
7649 send_event->button.time = GDK_CURRENT_TIME;
7650 send_event->button.x = -1;
7651 send_event->button.y = -1;
7652 send_event->button.axes = NULL;
7653 send_event->button.state = 0;
7654 send_event->button.button = 1;
7655 send_event->button.device = gdk_display_get_core_pointer (display);
7656 send_event->button.x_root = 0;
7657 send_event->button.y_root = 0;
7659 gtk_propagate_event (column->button, send_event);
7660 gdk_event_free (send_event);
7662 /* Kids, don't try this at home */
7663 g_object_ref (column->button);
7664 gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7665 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7666 gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7667 g_object_unref (column->button);
7669 tree_view->priv->drag_column_x = column->allocation.x;
7670 allocation = column->allocation;
7672 gtk_widget_size_allocate (column->button, &allocation);
7673 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7675 tree_view->priv->drag_column = column;
7676 gdk_window_show (tree_view->priv->drag_window);
7678 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7679 gdk_drawable_get_size (tree_view->priv->header_window, &width, &height);
7681 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7682 while (gtk_events_pending ())
7683 gtk_main_iteration ();
7685 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7686 gdk_pointer_grab (tree_view->priv->drag_window,
7688 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7689 NULL, NULL, GDK_CURRENT_TIME);
7690 gdk_keyboard_grab (tree_view->priv->drag_window,
7696 _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view,
7698 const GdkRectangle *clip_rect)
7702 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7706 rect.width = MAX (tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.width);
7708 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7709 rect.height = ROW_HEIGHT (tree_view);
7713 GdkRectangle new_rect;
7715 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7717 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7721 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7726 pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
7728 const GdkRectangle *clip_rect)
7732 _pspp_sheet_view_find_node (tree_view, path, &node);
7735 _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7739 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7742 GtkTreePath *cursor_path;
7744 if ((tree_view->priv->row_count == 0) ||
7745 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7749 if (tree_view->priv->cursor)
7750 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7752 if (cursor_path == NULL)
7754 /* There's no cursor. Move the cursor to the first selected row, if any
7755 * are selected, otherwise to the first row in the sheetview.
7757 GList *selected_rows;
7758 GtkTreeModel *model;
7759 PsppSheetSelection *selection;
7761 selection = pspp_sheet_view_get_selection (tree_view);
7762 selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7766 /* XXX we could avoid doing O(n) work to get this result */
7767 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7768 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7769 g_list_free (selected_rows);
7773 cursor_path = gtk_tree_path_new_first ();
7774 search_first_focusable_path (tree_view, &cursor_path,
7778 gtk_tree_row_reference_free (tree_view->priv->cursor);
7779 tree_view->priv->cursor = NULL;
7783 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7784 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7785 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE);
7787 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
7793 /* Now find a column for the cursor. */
7794 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7796 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7797 gtk_tree_path_free (cursor_path);
7799 if (tree_view->priv->focus_column == NULL)
7802 for (list = tree_view->priv->columns; list; list = list->next)
7804 if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7806 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7807 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7808 pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7818 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7821 gint selection_count;
7822 int cursor_node = -1;
7823 int new_cursor_node = -1;
7824 GtkTreePath *cursor_path = NULL;
7825 gboolean grab_focus = TRUE;
7827 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7831 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7832 /* FIXME: we lost the cursor; should we get the first? */
7835 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7836 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7838 if (cursor_node < 0)
7839 /* FIXME: we lost the cursor; should we get the first? */
7842 selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7844 if (selection_count == 0
7845 && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7846 && !tree_view->priv->ctrl_pressed)
7848 /* Don't move the cursor, but just select the current node */
7849 new_cursor_node = cursor_node;
7854 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7856 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7859 gtk_tree_path_free (cursor_path);
7861 if (new_cursor_node)
7863 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7865 search_first_focusable_path (tree_view, &cursor_path,
7870 gtk_tree_path_free (cursor_path);
7874 * If the list has only one item and multi-selection is set then select
7875 * the row (if not yet selected).
7877 if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7878 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7879 new_cursor_node < 0)
7882 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7884 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7886 if (new_cursor_node < 0
7887 && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
7889 new_cursor_node = cursor_node;
7893 new_cursor_node = -1;
7897 if (new_cursor_node >= 0)
7899 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7900 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE);
7901 gtk_tree_path_free (cursor_path);
7905 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
7907 if (!tree_view->priv->shift_pressed)
7909 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
7911 GTK_DIR_UP : GTK_DIR_DOWN))
7913 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
7916 gtk_widget_child_focus (toplevel,
7918 GTK_DIR_TAB_BACKWARD :
7919 GTK_DIR_TAB_FORWARD);
7926 gtk_widget_error_bell (GTK_WIDGET (tree_view));
7931 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7933 return new_cursor_node >= 0;
7937 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
7940 int cursor_node = -1;
7941 GtkTreePath *old_cursor_path = NULL;
7942 GtkTreePath *cursor_path = NULL;
7943 int start_cursor_node = -1;
7946 gint vertical_separator;
7948 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7951 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
7952 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7954 /* This is sorta weird. Focus in should give us a cursor */
7957 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
7958 _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
7960 if (cursor_node < 0)
7962 /* FIXME: we lost the cursor. Should we try to get one? */
7963 gtk_tree_path_free (old_cursor_path);
7967 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
7968 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
7969 y += tree_view->priv->cursor_offset;
7970 y += count * (int)tree_view->priv->vadjustment->page_increment;
7971 y = CLAMP (y, (gint)tree_view->priv->vadjustment->lower, (gint)tree_view->priv->vadjustment->upper - vertical_separator);
7973 if (y >= tree_view->priv->height)
7974 y = tree_view->priv->height - 1;
7976 tree_view->priv->cursor_offset =
7977 pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
7979 if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
7981 cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7982 tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
7985 y -= tree_view->priv->cursor_offset;
7986 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
7988 start_cursor_node = cursor_node;
7990 if (! search_first_focusable_path (tree_view, &cursor_path,
7994 /* It looks like we reached the end of the view without finding
7995 * a focusable row. We will step backwards to find the last
7998 cursor_node = start_cursor_node;
7999 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8001 search_first_focusable_path (tree_view, &cursor_path,
8010 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8012 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
8015 pspp_sheet_view_scroll_to_point (tree_view, -1, y);
8016 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8017 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8019 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
8020 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8022 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8025 gtk_tree_path_free (old_cursor_path);
8026 gtk_tree_path_free (cursor_path);
8030 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8033 int cursor_node = -1;
8034 GtkTreePath *cursor_path = NULL;
8035 PsppSheetViewColumn *column;
8038 gboolean found_column = FALSE;
8041 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8043 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8046 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8047 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8051 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8052 if (cursor_node < 0)
8054 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8056 gtk_tree_path_free (cursor_path);
8059 gtk_tree_path_free (cursor_path);
8061 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8062 if (tree_view->priv->focus_column)
8064 for (; list; list = (rtl ? list->prev : list->next))
8066 if (list->data == tree_view->priv->focus_column)
8073 gboolean left, right;
8075 column = list->data;
8076 if (column->visible == FALSE || column->row_head)
8079 pspp_sheet_view_column_cell_set_cell_data (column,
8080 tree_view->priv->model,
8085 right = list->prev ? TRUE : FALSE;
8086 left = list->next ? TRUE : FALSE;
8090 left = list->prev ? TRUE : FALSE;
8091 right = list->next ? TRUE : FALSE;
8094 if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8096 tree_view->priv->focus_column = column;
8097 found_column = TRUE;
8102 list = rtl ? list->prev : list->next;
8104 list = rtl ? list->next : list->prev;
8109 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8110 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8111 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8115 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8118 pspp_sheet_view_clamp_column_visible (tree_view,
8119 tree_view->priv->focus_column, TRUE);
8123 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8126 int cursor_node = -1;
8127 GtkTreePath *cursor_path = NULL;
8128 PsppSheetViewColumn *column;
8129 PsppSheetViewColumn *found_column;
8134 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8136 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8139 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8140 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8144 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8145 if (cursor_node < 0)
8147 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8149 gtk_tree_path_free (cursor_path);
8152 gtk_tree_path_free (cursor_path);
8154 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8155 if (tree_view->priv->focus_column)
8157 for (; list; list = (rtl ? list->prev : list->next))
8159 if (list->data == tree_view->priv->focus_column)
8164 found_column = NULL;
8167 gboolean left, right;
8169 column = list->data;
8170 if (column->visible == FALSE || column->row_head)
8173 pspp_sheet_view_column_cell_set_cell_data (column,
8174 tree_view->priv->model,
8179 right = list->prev ? TRUE : FALSE;
8180 left = list->next ? TRUE : FALSE;
8184 left = list->prev ? TRUE : FALSE;
8185 right = list->next ? TRUE : FALSE;
8188 if (column->tabbable
8189 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8190 found_column = column;
8194 list = rtl ? list->prev : list->next;
8196 list = rtl ? list->next : list->prev;
8201 tree_view->priv->focus_column = found_column;
8202 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8203 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8204 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8207 pspp_sheet_view_clamp_column_visible (tree_view,
8208 tree_view->priv->focus_column, TRUE);
8212 try_move_cursor_tab (PsppSheetView *tree_view,
8213 gboolean start_at_focus_column,
8216 PsppSheetViewColumn *column;
8218 int cursor_node = -1;
8219 GtkTreePath *cursor_path = NULL;
8223 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8224 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8228 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8229 if (cursor_node < 0)
8231 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8233 gtk_tree_path_free (cursor_path);
8236 gtk_tree_path_free (cursor_path);
8238 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8239 if (start_at_focus_column)
8242 ? g_list_last (tree_view->priv->columns)
8243 : g_list_first (tree_view->priv->columns));
8244 if (tree_view->priv->focus_column)
8246 for (; list; list = (rtl ? list->prev : list->next))
8248 if (list->data == tree_view->priv->focus_column)
8255 list = (rtl ^ (count == 1)
8256 ? g_list_first (tree_view->priv->columns)
8257 : g_list_last (tree_view->priv->columns));
8262 gboolean left, right;
8264 column = list->data;
8265 if (column->visible == FALSE || !column->tabbable)
8268 pspp_sheet_view_column_cell_set_cell_data (column,
8269 tree_view->priv->model,
8274 right = list->prev ? TRUE : FALSE;
8275 left = list->next ? TRUE : FALSE;
8279 left = list->prev ? TRUE : FALSE;
8280 right = list->next ? TRUE : FALSE;
8283 if (column->tabbable
8284 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8286 tree_view->priv->focus_column = column;
8287 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8288 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8289 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8294 list = rtl ? list->prev : list->next;
8296 list = rtl ? list->next : list->prev;
8303 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8306 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8309 if (!try_move_cursor_tab (tree_view, TRUE, count))
8311 /* Shift+Tab goes backward, but Shift isn't supposed to act as Shift does
8312 for other movement commands, that is, it shouldn't cause the selection
8313 to be extended, so we need to act as though it is off. */
8314 tree_view->priv->shift_pressed = FALSE;
8316 if (pspp_sheet_view_move_cursor_up_down (tree_view, count)
8317 && !try_move_cursor_tab (tree_view, FALSE, count))
8318 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8321 pspp_sheet_view_clamp_column_visible (tree_view,
8322 tree_view->priv->focus_column, TRUE);
8326 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8331 GtkTreePath *old_path;
8333 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8336 g_return_if_fail (tree_view->priv->row_count > 0);
8338 pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8342 /* Now go forward to find the first focusable row. */
8343 path = _pspp_sheet_view_find_path (tree_view, 0);
8344 search_first_focusable_path (tree_view, &path,
8345 TRUE, &cursor_node);
8349 /* Now go backwards to find last focusable row. */
8350 path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8351 search_first_focusable_path (tree_view, &path,
8352 FALSE, &cursor_node);
8358 if (gtk_tree_path_compare (old_path, path))
8360 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE);
8361 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8365 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8369 gtk_tree_path_free (old_path);
8370 gtk_tree_path_free (path);
8374 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8376 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8379 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8380 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8383 pspp_sheet_selection_select_all (tree_view->priv->selection);
8389 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8391 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8394 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8395 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8398 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8404 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8405 gboolean start_editing)
8408 int cursor_node = -1;
8409 GtkTreePath *cursor_path = NULL;
8410 GtkTreeSelectMode mode = 0;
8412 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8415 if (tree_view->priv->cursor)
8416 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8418 if (cursor_path == NULL)
8421 _pspp_sheet_view_find_node (tree_view, cursor_path,
8424 if (cursor_node < 0)
8426 gtk_tree_path_free (cursor_path);
8430 if (!tree_view->priv->shift_pressed && start_editing &&
8431 tree_view->priv->focus_column)
8433 if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8435 gtk_tree_path_free (cursor_path);
8440 if (tree_view->priv->ctrl_pressed)
8441 mode |= GTK_TREE_SELECT_MODE_TOGGLE;
8442 if (tree_view->priv->shift_pressed)
8443 mode |= GTK_TREE_SELECT_MODE_EXTEND;
8445 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8451 /* We bail out if the original (tree, node) don't exist anymore after
8452 * handling the selection-changed callback. We do return TRUE because
8453 * the key press has been handled at this point.
8455 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8457 if (cursor_node != new_node)
8460 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8462 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8463 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8465 if (!tree_view->priv->shift_pressed)
8466 pspp_sheet_view_row_activated (tree_view, cursor_path,
8467 tree_view->priv->focus_column);
8469 gtk_tree_path_free (cursor_path);
8475 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8478 int cursor_node = -1;
8479 GtkTreePath *cursor_path = NULL;
8481 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8485 if (tree_view->priv->cursor)
8486 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8488 if (cursor_path == NULL)
8491 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8492 if (cursor_node < 0)
8494 gtk_tree_path_free (cursor_path);
8498 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8501 GTK_TREE_SELECT_MODE_TOGGLE,
8504 /* We bail out if the original (tree, node) don't exist anymore after
8505 * handling the selection-changed callback. We do return TRUE because
8506 * the key press has been handled at this point.
8508 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8510 if (cursor_node != new_node)
8513 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8515 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8516 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8517 gtk_tree_path_free (cursor_path);
8523 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8525 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8526 tree_view->priv->typeselect_flush_timeout = 0;
8531 /* Cut and paste from gtkwindow.c */
8533 send_focus_change (GtkWidget *widget,
8536 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8538 g_object_ref (widget);
8541 GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
8543 GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
8545 fevent->focus_change.type = GDK_FOCUS_CHANGE;
8546 fevent->focus_change.window = g_object_ref (widget->window);
8547 fevent->focus_change.in = in;
8549 gtk_widget_event (widget, fevent);
8551 g_object_notify (G_OBJECT (widget), "has-focus");
8553 g_object_unref (widget);
8554 gdk_event_free (fevent);
8558 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8560 GtkWidget *frame, *vbox, *toplevel;
8563 if (tree_view->priv->search_custom_entry_set)
8566 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8567 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8569 if (tree_view->priv->search_window != NULL)
8571 if (GTK_WINDOW (toplevel)->group)
8572 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
8573 GTK_WINDOW (tree_view->priv->search_window));
8574 else if (GTK_WINDOW (tree_view->priv->search_window)->group)
8575 gtk_window_group_remove_window (GTK_WINDOW (tree_view->priv->search_window)->group,
8576 GTK_WINDOW (tree_view->priv->search_window));
8577 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8581 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8582 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8584 if (GTK_WINDOW (toplevel)->group)
8585 gtk_window_group_add_window (GTK_WINDOW (toplevel)->group,
8586 GTK_WINDOW (tree_view->priv->search_window));
8588 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8589 GDK_WINDOW_TYPE_HINT_UTILITY);
8590 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8591 g_signal_connect (tree_view->priv->search_window, "delete-event",
8592 G_CALLBACK (pspp_sheet_view_search_delete_event),
8594 g_signal_connect (tree_view->priv->search_window, "key-press-event",
8595 G_CALLBACK (pspp_sheet_view_search_key_press_event),
8597 g_signal_connect (tree_view->priv->search_window, "button-press-event",
8598 G_CALLBACK (pspp_sheet_view_search_button_press_event),
8600 g_signal_connect (tree_view->priv->search_window, "scroll-event",
8601 G_CALLBACK (pspp_sheet_view_search_scroll_event),
8604 frame = gtk_frame_new (NULL);
8605 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8606 gtk_widget_show (frame);
8607 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8609 vbox = gtk_vbox_new (FALSE, 0);
8610 gtk_widget_show (vbox);
8611 gtk_container_add (GTK_CONTAINER (frame), vbox);
8612 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8615 tree_view->priv->search_entry = gtk_entry_new ();
8616 gtk_widget_show (tree_view->priv->search_entry);
8617 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8618 G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8620 g_signal_connect (tree_view->priv->search_entry,
8621 "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8623 g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8625 G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8627 gtk_container_add (GTK_CONTAINER (vbox),
8628 tree_view->priv->search_entry);
8630 gtk_widget_realize (tree_view->priv->search_entry);
8633 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8634 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8637 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8638 gboolean keybinding)
8640 /* We only start interactive search if we have focus or the columns
8641 * have focus. If one of our children have focus, we don't want to
8645 gboolean found_focus = FALSE;
8646 GtkWidgetClass *entry_parent_class;
8648 if (!tree_view->priv->enable_search && !keybinding)
8651 if (tree_view->priv->search_custom_entry_set)
8654 if (tree_view->priv->search_window != NULL &&
8655 gtk_widget_get_visible (tree_view->priv->search_window))
8658 for (list = tree_view->priv->columns; list; list = list->next)
8660 PsppSheetViewColumn *column;
8662 column = list->data;
8663 if (! column->visible)
8666 if (column->button && gtk_widget_has_focus (column->button))
8673 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8679 if (tree_view->priv->search_column < 0)
8682 pspp_sheet_view_ensure_interactive_directory (tree_view);
8685 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8688 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8689 gtk_widget_show (tree_view->priv->search_window);
8690 if (tree_view->priv->search_entry_changed_id == 0)
8692 tree_view->priv->search_entry_changed_id =
8693 g_signal_connect (tree_view->priv->search_entry, "changed",
8694 G_CALLBACK (pspp_sheet_view_search_init),
8698 tree_view->priv->typeselect_flush_timeout =
8699 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8700 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8703 /* Grab focus will select all the text. We don't want that to happen, so we
8704 * call the parent instance and bypass the selection change. This is probably
8705 * really non-kosher. */
8706 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8707 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8709 /* send focus-in event */
8710 send_focus_change (tree_view->priv->search_entry, TRUE);
8712 /* search first matching iter */
8713 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8719 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8721 return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8724 /* this function returns the new width of the column being resized given
8725 * the column and x position of the cursor; the x cursor position is passed
8726 * in as a pointer and automagicly corrected if it's beyond min/max limits
8729 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8733 PsppSheetViewColumn *column;
8737 /* first translate the x position from widget->window
8738 * to clist->clist_window
8740 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8741 column = g_list_nth (tree_view->priv->columns, i)->data;
8742 width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8744 /* Clamp down the value */
8745 if (column->min_width == -1)
8746 width = MAX (column->button_request, width);
8748 width = MAX (column->min_width, width);
8749 if (column->max_width != -1)
8750 width = MIN (width, column->max_width);
8752 *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8758 /* FIXME this adjust_allocation is a big cut-and-paste from
8759 * GtkCList, needs to be some "official" way to do this
8769 /* The window to which widget->window is relative */
8770 #define ALLOCATION_WINDOW(widget) \
8771 (!gtk_widget_get_has_window (widget) ? \
8772 (widget)->window : \
8773 gdk_window_get_parent ((widget)->window))
8776 adjust_allocation_recurse (GtkWidget *widget,
8779 ScrollData *scroll_data = data;
8781 /* Need to really size allocate instead of just poking
8782 * into widget->allocation if the widget is not realized.
8783 * FIXME someone figure out why this was.
8785 if (!gtk_widget_get_realized (widget))
8787 if (gtk_widget_get_visible (widget))
8789 GdkRectangle tmp_rectangle = widget->allocation;
8790 tmp_rectangle.x += scroll_data->dx;
8791 tmp_rectangle.y += scroll_data->dy;
8793 gtk_widget_size_allocate (widget, &tmp_rectangle);
8798 if (ALLOCATION_WINDOW (widget) == scroll_data->window)
8800 widget->allocation.x += scroll_data->dx;
8801 widget->allocation.y += scroll_data->dy;
8803 if (GTK_IS_CONTAINER (widget))
8804 gtk_container_forall (GTK_CONTAINER (widget),
8805 adjust_allocation_recurse,
8812 adjust_allocation (GtkWidget *widget,
8816 ScrollData scroll_data;
8818 if (gtk_widget_get_realized (widget))
8819 scroll_data.window = ALLOCATION_WINDOW (widget);
8821 scroll_data.window = NULL;
8823 scroll_data.dx = dx;
8824 scroll_data.dy = dy;
8826 adjust_allocation_recurse (widget, &scroll_data);
8830 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8834 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8835 PsppSheetView *tree_view)
8837 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8842 gdk_window_move (tree_view->priv->bin_window,
8843 - tree_view->priv->hadjustment->value,
8844 TREE_VIEW_HEADER_HEIGHT (tree_view));
8845 gdk_window_move (tree_view->priv->header_window,
8846 - tree_view->priv->hadjustment->value,
8848 dy = tree_view->priv->dy - (int) tree_view->priv->vadjustment->value;
8851 update_prelight (tree_view,
8852 tree_view->priv->event_last_x,
8853 tree_view->priv->event_last_y - dy);
8855 if (tree_view->priv->edited_column &&
8856 GTK_IS_WIDGET (tree_view->priv->edited_column->editable_widget))
8860 PsppSheetViewChild *child = NULL;
8862 widget = GTK_WIDGET (tree_view->priv->edited_column->editable_widget);
8863 adjust_allocation (widget, 0, dy);
8865 for (list = tree_view->priv->children; list; list = list->next)
8867 child = (PsppSheetViewChild *)list->data;
8868 if (child->widget == widget)
8876 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8878 if (tree_view->priv->dy != (int) tree_view->priv->vadjustment->value)
8880 /* update our dy and top_row */
8881 tree_view->priv->dy = (int) tree_view->priv->vadjustment->value;
8883 if (!tree_view->priv->in_top_row_to_dy)
8884 pspp_sheet_view_dy_to_top_row (tree_view);
8887 for (list = tree_view->priv->columns; list; list = list->next)
8889 PsppSheetViewColumn *column = list->data;
8890 GtkAllocation *allocation = &column->allocation;
8892 if (span_intersects (allocation->x, allocation->width,
8893 tree_view->priv->hadjustment->value,
8894 GTK_WIDGET (tree_view)->allocation.width))
8896 pspp_sheet_view_column_set_need_button (column, TRUE);
8897 if (!column->button)
8898 pspp_sheet_view_column_update_button (column);
8910 * pspp_sheet_view_new:
8912 * Creates a new #PsppSheetView widget.
8914 * Return value: A newly created #PsppSheetView widget.
8917 pspp_sheet_view_new (void)
8919 return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
8923 * pspp_sheet_view_new_with_model:
8924 * @model: the model.
8926 * Creates a new #PsppSheetView widget with the model initialized to @model.
8928 * Return value: A newly created #PsppSheetView widget.
8931 pspp_sheet_view_new_with_model (GtkTreeModel *model)
8933 return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
8940 * pspp_sheet_view_get_model:
8941 * @tree_view: a #PsppSheetView
8943 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
8946 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
8949 pspp_sheet_view_get_model (PsppSheetView *tree_view)
8951 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
8953 return tree_view->priv->model;
8957 * pspp_sheet_view_set_model:
8958 * @tree_view: A #GtkTreeNode.
8959 * @model: (allow-none): The model.
8961 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
8962 * set, it will remove it before setting the new model. If @model is %NULL,
8963 * then it will unset the old model.
8966 pspp_sheet_view_set_model (PsppSheetView *tree_view,
8967 GtkTreeModel *model)
8969 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
8970 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
8972 if (model == tree_view->priv->model)
8975 if (tree_view->priv->scroll_to_path)
8977 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
8978 tree_view->priv->scroll_to_path = NULL;
8981 if (tree_view->priv->model)
8983 GList *tmplist = tree_view->priv->columns;
8985 if (tree_view->priv->selected)
8986 range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
8987 pspp_sheet_view_stop_editing (tree_view, TRUE);
8989 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8990 pspp_sheet_view_row_changed,
8992 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8993 pspp_sheet_view_row_inserted,
8995 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8996 pspp_sheet_view_row_deleted,
8998 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
8999 pspp_sheet_view_rows_reordered,
9002 for (; tmplist; tmplist = tmplist->next)
9003 _pspp_sheet_view_column_unset_model (tmplist->data,
9004 tree_view->priv->model);
9006 tree_view->priv->prelight_node = -1;
9008 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
9009 tree_view->priv->drag_dest_row = NULL;
9010 gtk_tree_row_reference_free (tree_view->priv->cursor);
9011 tree_view->priv->cursor = NULL;
9012 gtk_tree_row_reference_free (tree_view->priv->anchor);
9013 tree_view->priv->anchor = NULL;
9014 gtk_tree_row_reference_free (tree_view->priv->top_row);
9015 tree_view->priv->top_row = NULL;
9016 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9017 tree_view->priv->scroll_to_path = NULL;
9019 tree_view->priv->scroll_to_column = NULL;
9021 g_object_unref (tree_view->priv->model);
9023 tree_view->priv->search_column = -1;
9024 tree_view->priv->fixed_height = -1;
9025 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
9026 tree_view->priv->last_button_x = -1;
9027 tree_view->priv->last_button_y = -1;
9030 tree_view->priv->model = model;
9032 if (tree_view->priv->model)
9036 if (tree_view->priv->search_column == -1)
9038 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
9040 GType type = gtk_tree_model_get_column_type (model, i);
9042 if (g_value_type_transformable (type, G_TYPE_STRING))
9044 tree_view->priv->search_column = i;
9050 g_object_ref (tree_view->priv->model);
9051 g_signal_connect (tree_view->priv->model,
9053 G_CALLBACK (pspp_sheet_view_row_changed),
9055 g_signal_connect (tree_view->priv->model,
9057 G_CALLBACK (pspp_sheet_view_row_inserted),
9059 g_signal_connect (tree_view->priv->model,
9061 G_CALLBACK (pspp_sheet_view_row_deleted),
9063 g_signal_connect (tree_view->priv->model,
9065 G_CALLBACK (pspp_sheet_view_rows_reordered),
9068 tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
9070 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
9071 install_presize_handler (tree_view);
9074 g_object_notify (G_OBJECT (tree_view), "model");
9076 if (tree_view->priv->selection)
9077 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
9079 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9080 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9084 * pspp_sheet_view_get_selection:
9085 * @tree_view: A #PsppSheetView.
9087 * Gets the #PsppSheetSelection associated with @tree_view.
9089 * Return value: A #PsppSheetSelection object.
9091 PsppSheetSelection *
9092 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
9094 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9096 return tree_view->priv->selection;
9100 * pspp_sheet_view_get_hadjustment:
9101 * @tree_view: A #PsppSheetView
9103 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
9105 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9109 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
9111 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9113 if (tree_view->priv->hadjustment == NULL)
9114 pspp_sheet_view_set_hadjustment (tree_view, NULL);
9116 return tree_view->priv->hadjustment;
9120 * pspp_sheet_view_set_hadjustment:
9121 * @tree_view: A #PsppSheetView
9122 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9124 * Sets the #GtkAdjustment for the current horizontal aspect.
9127 pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view,
9128 GtkAdjustment *adjustment)
9130 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9132 pspp_sheet_view_set_adjustments (tree_view,
9134 tree_view->priv->vadjustment);
9136 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9140 * pspp_sheet_view_get_vadjustment:
9141 * @tree_view: A #PsppSheetView
9143 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9145 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9149 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9151 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9153 if (tree_view->priv->vadjustment == NULL)
9154 pspp_sheet_view_set_vadjustment (tree_view, NULL);
9156 return tree_view->priv->vadjustment;
9160 * pspp_sheet_view_set_vadjustment:
9161 * @tree_view: A #PsppSheetView
9162 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9164 * Sets the #GtkAdjustment for the current vertical aspect.
9167 pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view,
9168 GtkAdjustment *adjustment)
9170 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9172 pspp_sheet_view_set_adjustments (tree_view,
9173 tree_view->priv->hadjustment,
9176 g_object_notify (G_OBJECT (tree_view), "vadjustment");
9179 /* Column and header operations */
9182 * pspp_sheet_view_get_headers_visible:
9183 * @tree_view: A #PsppSheetView.
9185 * Returns %TRUE if the headers on the @tree_view are visible.
9187 * Return value: Whether the headers are visible or not.
9190 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9192 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9194 return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9198 * pspp_sheet_view_set_headers_visible:
9199 * @tree_view: A #PsppSheetView.
9200 * @headers_visible: %TRUE if the headers are visible
9202 * Sets the visibility state of the headers.
9205 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9206 gboolean headers_visible)
9210 PsppSheetViewColumn *column;
9212 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9214 headers_visible = !! headers_visible;
9216 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9219 if (headers_visible)
9220 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9222 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9224 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9226 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9227 if (headers_visible)
9229 gdk_window_move_resize (tree_view->priv->bin_window, x, y + TREE_VIEW_HEADER_HEIGHT (tree_view), tree_view->priv->width, GTK_WIDGET (tree_view)->allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view));
9231 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9232 pspp_sheet_view_map_buttons (tree_view);
9236 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9238 for (list = tree_view->priv->columns; list; list = list->next)
9240 column = list->data;
9242 gtk_widget_unmap (column->button);
9244 gdk_window_hide (tree_view->priv->header_window);
9248 tree_view->priv->vadjustment->page_size = GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
9249 tree_view->priv->vadjustment->page_increment = (GTK_WIDGET (tree_view)->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2;
9250 tree_view->priv->vadjustment->lower = 0;
9251 tree_view->priv->vadjustment->upper = tree_view->priv->height;
9252 gtk_adjustment_changed (tree_view->priv->vadjustment);
9254 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9256 g_object_notify (G_OBJECT (tree_view), "headers-visible");
9260 * pspp_sheet_view_columns_autosize:
9261 * @tree_view: A #PsppSheetView.
9263 * Resizes all columns to their optimal width. Only works after the
9264 * treeview has been realized.
9267 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9269 gboolean dirty = FALSE;
9271 PsppSheetViewColumn *column;
9273 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9275 for (list = tree_view->priv->columns; list; list = list->next)
9277 column = list->data;
9278 _pspp_sheet_view_column_cell_set_dirty (column);
9283 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9287 * pspp_sheet_view_set_headers_clickable:
9288 * @tree_view: A #PsppSheetView.
9289 * @setting: %TRUE if the columns are clickable.
9291 * Allow the column title buttons to be clicked.
9294 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9299 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9301 for (list = tree_view->priv->columns; list; list = list->next)
9302 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9304 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9309 * pspp_sheet_view_get_headers_clickable:
9310 * @tree_view: A #PsppSheetView.
9312 * Returns whether all header columns are clickable.
9314 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9319 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9323 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9325 for (list = tree_view->priv->columns; list; list = list->next)
9326 if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9333 * pspp_sheet_view_set_rules_hint
9334 * @tree_view: a #PsppSheetView
9335 * @setting: %TRUE if the tree requires reading across rows
9337 * This function tells GTK+ that the user interface for your
9338 * application requires users to read across tree rows and associate
9339 * cells with one another. By default, GTK+ will then render the tree
9340 * with alternating row colors. Do <emphasis>not</emphasis> use it
9341 * just because you prefer the appearance of the ruled tree; that's a
9342 * question for the theme. Some themes will draw tree rows in
9343 * alternating colors even when rules are turned off, and users who
9344 * prefer that appearance all the time can choose those themes. You
9345 * should call this function only as a <emphasis>semantic</emphasis>
9346 * hint to the theme engine that your tree makes alternating colors
9347 * useful from a functional standpoint (since it has lots of columns,
9352 pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view,
9355 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9357 setting = setting != FALSE;
9359 if (tree_view->priv->has_rules != setting)
9361 tree_view->priv->has_rules = setting;
9362 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9365 g_object_notify (G_OBJECT (tree_view), "rules-hint");
9369 * pspp_sheet_view_get_rules_hint
9370 * @tree_view: a #PsppSheetView
9372 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9374 * Return value: %TRUE if rules are useful for the user of this tree
9377 pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view)
9379 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9381 return tree_view->priv->has_rules;
9384 /* Public Column functions
9388 * pspp_sheet_view_append_column:
9389 * @tree_view: A #PsppSheetView.
9390 * @column: The #PsppSheetViewColumn to add.
9392 * Appends @column to the list of columns.
9394 * Return value: The number of columns in @tree_view after appending.
9397 pspp_sheet_view_append_column (PsppSheetView *tree_view,
9398 PsppSheetViewColumn *column)
9400 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9401 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9402 g_return_val_if_fail (column->tree_view == NULL, -1);
9404 return pspp_sheet_view_insert_column (tree_view, column, -1);
9409 * pspp_sheet_view_remove_column:
9410 * @tree_view: A #PsppSheetView.
9411 * @column: The #PsppSheetViewColumn to remove.
9413 * Removes @column from @tree_view.
9415 * Return value: The number of columns in @tree_view after removing.
9418 pspp_sheet_view_remove_column (PsppSheetView *tree_view,
9419 PsppSheetViewColumn *column)
9421 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9422 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9423 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9425 if (tree_view->priv->focus_column == column)
9426 tree_view->priv->focus_column = NULL;
9428 if (tree_view->priv->edited_column == column)
9430 pspp_sheet_view_stop_editing (tree_view, TRUE);
9432 /* no need to, but just to be sure ... */
9433 tree_view->priv->edited_column = NULL;
9436 _pspp_sheet_view_column_unset_tree_view (column);
9438 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9439 tree_view->priv->n_columns--;
9441 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9445 _pspp_sheet_view_column_unrealize_button (column);
9446 for (list = tree_view->priv->columns; list; list = list->next)
9448 PsppSheetViewColumn *tmp_column;
9450 tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9451 if (tmp_column->visible)
9452 _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9455 if (tree_view->priv->n_columns == 0 &&
9456 pspp_sheet_view_get_headers_visible (tree_view) &&
9457 tree_view->priv->header_window)
9458 gdk_window_hide (tree_view->priv->header_window);
9460 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9463 g_object_unref (column);
9464 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9466 return tree_view->priv->n_columns;
9470 * pspp_sheet_view_insert_column:
9471 * @tree_view: A #PsppSheetView.
9472 * @column: The #PsppSheetViewColumn to be inserted.
9473 * @position: The position to insert @column in.
9475 * This inserts the @column into the @tree_view at @position. If @position is
9476 * -1, then the column is inserted at the end.
9478 * Return value: The number of columns in @tree_view after insertion.
9481 pspp_sheet_view_insert_column (PsppSheetView *tree_view,
9482 PsppSheetViewColumn *column,
9485 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9486 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9487 g_return_val_if_fail (column->tree_view == NULL, -1);
9489 g_object_ref_sink (column);
9491 if (tree_view->priv->n_columns == 0 &&
9492 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9493 pspp_sheet_view_get_headers_visible (tree_view))
9495 gdk_window_show (tree_view->priv->header_window);
9498 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9500 tree_view->priv->n_columns++;
9502 _pspp_sheet_view_column_set_tree_view (column, tree_view);
9504 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9508 _pspp_sheet_view_column_realize_button (column);
9510 for (list = tree_view->priv->columns; list; list = list->next)
9512 column = PSPP_SHEET_VIEW_COLUMN (list->data);
9513 if (column->visible)
9514 _pspp_sheet_view_column_cell_set_dirty (column);
9516 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9519 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9521 return tree_view->priv->n_columns;
9525 * pspp_sheet_view_insert_column_with_attributes:
9526 * @tree_view: A #PsppSheetView
9527 * @position: The position to insert the new column in.
9528 * @title: The title to set the header to.
9529 * @cell: The #GtkCellRenderer.
9530 * @Varargs: A %NULL-terminated list of attributes.
9532 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9533 * @position. If @position is -1, then the newly created column is inserted at
9534 * the end. The column is initialized with the attributes given.
9536 * Return value: The number of columns in @tree_view after insertion.
9539 pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view,
9542 GtkCellRenderer *cell,
9545 PsppSheetViewColumn *column;
9550 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9552 column = pspp_sheet_view_column_new ();
9553 pspp_sheet_view_column_set_title (column, title);
9554 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9556 va_start (args, cell);
9558 attribute = va_arg (args, gchar *);
9560 while (attribute != NULL)
9562 column_id = va_arg (args, gint);
9563 pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9564 attribute = va_arg (args, gchar *);
9569 pspp_sheet_view_insert_column (tree_view, column, position);
9571 return tree_view->priv->n_columns;
9575 * pspp_sheet_view_insert_column_with_data_func:
9576 * @tree_view: a #PsppSheetView
9577 * @position: Position to insert, -1 for append
9578 * @title: column title
9579 * @cell: cell renderer for column
9580 * @func: function to set attributes of cell renderer
9581 * @data: data for @func
9582 * @dnotify: destroy notifier for @data
9584 * Convenience function that inserts a new column into the #PsppSheetView
9585 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9586 * attributes (normally using data from the model). See also
9587 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9589 * Return value: number of columns in the tree view post-insert
9592 pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view,
9595 GtkCellRenderer *cell,
9596 PsppSheetCellDataFunc func,
9598 GDestroyNotify dnotify)
9600 PsppSheetViewColumn *column;
9602 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9604 column = pspp_sheet_view_column_new ();
9605 pspp_sheet_view_column_set_title (column, title);
9606 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9607 pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9609 pspp_sheet_view_insert_column (tree_view, column, position);
9611 return tree_view->priv->n_columns;
9615 * pspp_sheet_view_get_column:
9616 * @tree_view: A #PsppSheetView.
9617 * @n: The position of the column, counting from 0.
9619 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9621 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9624 PsppSheetViewColumn *
9625 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9628 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9630 if (n < 0 || n >= tree_view->priv->n_columns)
9633 if (tree_view->priv->columns == NULL)
9636 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9640 * pspp_sheet_view_get_columns:
9641 * @tree_view: A #PsppSheetView
9643 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9644 * The returned list must be freed with g_list_free ().
9646 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9649 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9651 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9653 return g_list_copy (tree_view->priv->columns);
9657 * pspp_sheet_view_move_column_after:
9658 * @tree_view: A #PsppSheetView
9659 * @column: The #PsppSheetViewColumn to be moved.
9660 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9662 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9663 * @column is placed in the first position.
9666 pspp_sheet_view_move_column_after (PsppSheetView *tree_view,
9667 PsppSheetViewColumn *column,
9668 PsppSheetViewColumn *base_column)
9670 GList *column_list_el, *base_el = NULL;
9672 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9674 column_list_el = g_list_find (tree_view->priv->columns, column);
9675 g_return_if_fail (column_list_el != NULL);
9679 base_el = g_list_find (tree_view->priv->columns, base_column);
9680 g_return_if_fail (base_el != NULL);
9683 if (column_list_el->prev == base_el)
9686 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9687 if (base_el == NULL)
9689 column_list_el->prev = NULL;
9690 column_list_el->next = tree_view->priv->columns;
9691 if (column_list_el->next)
9692 column_list_el->next->prev = column_list_el;
9693 tree_view->priv->columns = column_list_el;
9697 column_list_el->prev = base_el;
9698 column_list_el->next = base_el->next;
9699 if (column_list_el->next)
9700 column_list_el->next->prev = column_list_el;
9701 base_el->next = column_list_el;
9704 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9706 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9707 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9710 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9714 * pspp_sheet_view_set_column_drag_function:
9715 * @tree_view: A #PsppSheetView.
9716 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9717 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9718 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9720 * Sets a user function for determining where a column may be dropped when
9721 * dragged. This function is called on every column pair in turn at the
9722 * beginning of a column drag to determine where a drop can take place. The
9723 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9724 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9725 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9726 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9727 * @tree_view reverts to the default behavior of allowing all columns to be
9728 * dropped everywhere.
9731 pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view,
9732 PsppSheetViewColumnDropFunc func,
9734 GDestroyNotify destroy)
9736 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9738 if (tree_view->priv->column_drop_func_data_destroy)
9739 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9741 tree_view->priv->column_drop_func = func;
9742 tree_view->priv->column_drop_func_data = user_data;
9743 tree_view->priv->column_drop_func_data_destroy = destroy;
9747 * pspp_sheet_view_scroll_to_point:
9748 * @tree_view: a #PsppSheetView
9749 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9750 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9752 * Scrolls the tree view such that the top-left corner of the visible
9753 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9754 * in tree coordinates. The @tree_view must be realized before
9755 * this function is called. If it isn't, you probably want to be
9756 * using pspp_sheet_view_scroll_to_cell().
9758 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9761 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9765 GtkAdjustment *hadj;
9766 GtkAdjustment *vadj;
9768 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9769 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9771 hadj = tree_view->priv->hadjustment;
9772 vadj = tree_view->priv->vadjustment;
9775 gtk_adjustment_set_value (hadj, CLAMP (tree_x, hadj->lower, hadj->upper - hadj->page_size));
9777 gtk_adjustment_set_value (vadj, CLAMP (tree_y, vadj->lower, vadj->upper - vadj->page_size));
9781 * pspp_sheet_view_scroll_to_cell:
9782 * @tree_view: A #PsppSheetView.
9783 * @path: (allow-none): The path of the row to move to, or %NULL.
9784 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9785 * @use_align: whether to use alignment arguments, or %FALSE.
9786 * @row_align: The vertical alignment of the row specified by @path.
9787 * @col_align: The horizontal alignment of the column specified by @column.
9789 * Moves the alignments of @tree_view to the position specified by @column and
9790 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9791 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9792 * or @path need to be non-%NULL. @row_align determines where the row is
9793 * placed, and @col_align determines where @column is placed. Both are expected
9794 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9795 * right/bottom alignment, 0.5 means center.
9797 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9798 * tree does the minimum amount of work to scroll the cell onto the screen.
9799 * This means that the cell will be scrolled to the edge closest to its current
9800 * position. If the cell is currently visible on the screen, nothing is done.
9802 * This function only works if the model is set, and @path is a valid row on the
9803 * model. If the model changes before the @tree_view is realized, the centered
9804 * path will be modified to reflect this change.
9807 pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view,
9809 PsppSheetViewColumn *column,
9814 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9815 g_return_if_fail (tree_view->priv->model != NULL);
9816 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9817 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9818 g_return_if_fail (path != NULL || column != NULL);
9821 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9822 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9824 row_align = CLAMP (row_align, 0.0, 1.0);
9825 col_align = CLAMP (col_align, 0.0, 1.0);
9828 /* Note: Despite the benefits that come from having one code path for the
9829 * scrolling code, we short-circuit validate_visible_area's immplementation as
9830 * it is much slower than just going to the point.
9832 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9833 !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9834 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9836 if (tree_view->priv->scroll_to_path)
9837 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9839 tree_view->priv->scroll_to_path = NULL;
9840 tree_view->priv->scroll_to_column = NULL;
9843 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
9845 tree_view->priv->scroll_to_column = column;
9846 tree_view->priv->scroll_to_use_align = use_align;
9847 tree_view->priv->scroll_to_row_align = row_align;
9848 tree_view->priv->scroll_to_col_align = col_align;
9850 install_presize_handler (tree_view);
9854 GdkRectangle cell_rect;
9855 GdkRectangle vis_rect;
9856 gint dest_x, dest_y;
9858 pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
9859 pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
9861 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
9863 dest_x = vis_rect.x;
9864 dest_y = vis_rect.y;
9870 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
9874 if (cell_rect.x < vis_rect.x)
9875 dest_x = cell_rect.x;
9876 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
9877 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
9885 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
9886 dest_y = MAX (dest_y, 0);
9890 if (cell_rect.y < vis_rect.y)
9891 dest_y = cell_rect.y;
9892 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
9893 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
9897 pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
9902 * pspp_sheet_view_row_activated:
9903 * @tree_view: A #PsppSheetView
9904 * @path: The #GtkTreePath to be activated.
9905 * @column: The #PsppSheetViewColumn to be activated.
9907 * Activates the cell determined by @path and @column.
9910 pspp_sheet_view_row_activated (PsppSheetView *tree_view,
9912 PsppSheetViewColumn *column)
9914 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9916 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
9921 * pspp_sheet_view_get_reorderable:
9922 * @tree_view: a #PsppSheetView
9924 * Retrieves whether the user can reorder the tree via drag-and-drop. See
9925 * pspp_sheet_view_set_reorderable().
9927 * Return value: %TRUE if the tree can be reordered.
9930 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
9932 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9934 return tree_view->priv->reorderable;
9938 * pspp_sheet_view_set_reorderable:
9939 * @tree_view: A #PsppSheetView.
9940 * @reorderable: %TRUE, if the tree can be reordered.
9942 * This function is a convenience function to allow you to reorder
9943 * models that support the #GtkDragSourceIface and the
9944 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
9945 * these. If @reorderable is %TRUE, then the user can reorder the
9946 * model by dragging and dropping rows. The developer can listen to
9947 * these changes by connecting to the model's row_inserted and
9948 * row_deleted signals. The reordering is implemented by setting up
9949 * the tree view as a drag source and destination. Therefore, drag and
9950 * drop can not be used in a reorderable view for any other purpose.
9952 * This function does not give you any degree of control over the order -- any
9953 * reordering is allowed. If more control is needed, you should probably
9954 * handle drag and drop manually.
9957 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
9958 gboolean reorderable)
9960 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9962 reorderable = reorderable != FALSE;
9964 if (tree_view->priv->reorderable == reorderable)
9969 const GtkTargetEntry row_targets[] = {
9970 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
9973 pspp_sheet_view_enable_model_drag_source (tree_view,
9976 G_N_ELEMENTS (row_targets),
9978 pspp_sheet_view_enable_model_drag_dest (tree_view,
9980 G_N_ELEMENTS (row_targets),
9985 pspp_sheet_view_unset_rows_drag_source (tree_view);
9986 pspp_sheet_view_unset_rows_drag_dest (tree_view);
9989 tree_view->priv->reorderable = reorderable;
9991 g_object_notify (G_OBJECT (tree_view), "reorderable");
9994 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
9995 is pressed, other rows will be unselected.
9997 If CLAMP_NODE is true, then the sheetview will scroll to make the row
10000 pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
10002 gboolean clear_and_select,
10003 gboolean clamp_node)
10007 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10009 GtkTreePath *cursor_path;
10010 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10011 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
10012 gtk_tree_path_free (cursor_path);
10015 gtk_tree_row_reference_free (tree_view->priv->cursor);
10016 tree_view->priv->cursor = NULL;
10018 _pspp_sheet_view_find_node (tree_view, path, &node);
10019 tree_view->priv->cursor =
10020 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
10021 tree_view->priv->model,
10024 if (tree_view->priv->row_count > 0)
10028 if (clear_and_select && !tree_view->priv->ctrl_pressed)
10030 GtkTreeSelectMode mode = 0;
10032 if (tree_view->priv->ctrl_pressed)
10033 mode |= GTK_TREE_SELECT_MODE_TOGGLE;
10034 if (tree_view->priv->shift_pressed)
10035 mode |= GTK_TREE_SELECT_MODE_EXTEND;
10037 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
10042 /* We have to re-find tree and node here again, somebody might have
10043 * cleared the node or the whole tree in the PsppSheetSelection::changed
10044 * callback. If the nodes differ we bail out here.
10046 _pspp_sheet_view_find_node (tree_view, path, &new_node);
10048 if (node != new_node)
10053 pspp_sheet_view_clamp_node_visible (tree_view, node);
10054 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
10058 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
10062 * pspp_sheet_view_get_cursor:
10063 * @tree_view: A #PsppSheetView
10064 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
10065 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
10067 * Fills in @path and @focus_column with the current path and focus column. If
10068 * the cursor isn't currently set, then *@path will be %NULL. If no column
10069 * currently has focus, then *@focus_column will be %NULL.
10071 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
10072 * you are done with it.
10075 pspp_sheet_view_get_cursor (PsppSheetView *tree_view,
10076 GtkTreePath **path,
10077 PsppSheetViewColumn **focus_column)
10079 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10083 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10084 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10091 *focus_column = tree_view->priv->focus_column;
10096 * pspp_sheet_view_set_cursor:
10097 * @tree_view: A #PsppSheetView
10098 * @path: A #GtkTreePath
10099 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10100 * @start_editing: %TRUE if the specified cell should start being edited.
10102 * Sets the current keyboard focus to be at @path, and selects it. This is
10103 * useful when you want to focus the user's attention on a particular row. If
10104 * @focus_column is not %NULL, then focus is given to the column specified by
10105 * it. Additionally, if @focus_column is specified, and @start_editing is
10106 * %TRUE, then editing should be started in the specified cell.
10107 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10108 * in order to give keyboard focus to the widget. Please note that editing
10109 * can only happen when the widget is realized.
10111 * If @path is invalid for @model, the current cursor (if any) will be unset
10112 * and the function will return without failing.
10115 pspp_sheet_view_set_cursor (PsppSheetView *tree_view,
10117 PsppSheetViewColumn *focus_column,
10118 gboolean start_editing)
10120 pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10121 NULL, start_editing);
10125 * pspp_sheet_view_set_cursor_on_cell:
10126 * @tree_view: A #PsppSheetView
10127 * @path: A #GtkTreePath
10128 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10129 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10130 * @start_editing: %TRUE if the specified cell should start being edited.
10132 * Sets the current keyboard focus to be at @path, and selects it. This is
10133 * useful when you want to focus the user's attention on a particular row. If
10134 * @focus_column is not %NULL, then focus is given to the column specified by
10135 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10136 * contains 2 or more editable or activatable cells, then focus is given to
10137 * the cell specified by @focus_cell. Additionally, if @focus_column is
10138 * specified, and @start_editing is %TRUE, then editing should be started in
10139 * the specified cell. This function is often followed by
10140 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10141 * widget. Please note that editing can only happen when the widget is
10144 * If @path is invalid for @model, the current cursor (if any) will be unset
10145 * and the function will return without failing.
10150 pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view,
10152 PsppSheetViewColumn *focus_column,
10153 GtkCellRenderer *focus_cell,
10154 gboolean start_editing)
10156 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10157 g_return_if_fail (path != NULL);
10158 g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10160 if (!tree_view->priv->model)
10165 g_return_if_fail (focus_column);
10166 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10169 /* cancel the current editing, if it exists */
10170 if (tree_view->priv->edited_column &&
10171 tree_view->priv->edited_column->editable_widget)
10172 pspp_sheet_view_stop_editing (tree_view, TRUE);
10174 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE);
10176 if (focus_column && focus_column->visible)
10179 gboolean column_in_tree = FALSE;
10181 for (list = tree_view->priv->columns; list; list = list->next)
10182 if (list->data == focus_column)
10184 column_in_tree = TRUE;
10187 g_return_if_fail (column_in_tree);
10188 tree_view->priv->focus_column = focus_column;
10190 pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10192 pspp_sheet_view_start_editing (tree_view, path);
10194 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10195 pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10201 * pspp_sheet_view_get_bin_window:
10202 * @tree_view: A #PsppSheetView
10204 * Returns the window that @tree_view renders to. This is used primarily to
10205 * compare to <literal>event->window</literal> to confirm that the event on
10206 * @tree_view is on the right window.
10208 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10211 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10213 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10215 return tree_view->priv->bin_window;
10219 * pspp_sheet_view_get_path_at_pos:
10220 * @tree_view: A #PsppSheetView.
10221 * @x: The x position to be identified (relative to bin_window).
10222 * @y: The y position to be identified (relative to bin_window).
10223 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10224 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10225 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10226 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10228 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10229 * (please see pspp_sheet_view_get_bin_window()).
10230 * That is, @x and @y are relative to an events coordinates. @x and @y must
10231 * come from an event on the @tree_view only where <literal>event->window ==
10232 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10233 * things like popup menus. If @path is non-%NULL, then it will be filled
10234 * with the #GtkTreePath at that point. This path should be freed with
10235 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10236 * with the column at that point. @cell_x and @cell_y return the coordinates
10237 * relative to the cell background (i.e. the @background_area passed to
10238 * gtk_cell_renderer_render()). This function is only meaningful if
10239 * @tree_view is realized. Therefore this function will always return %FALSE
10240 * if @tree_view is not realized or does not have a model.
10242 * For converting widget coordinates (eg. the ones you get from
10243 * GtkWidget::query-tooltip), please see
10244 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10246 * Return value: %TRUE if a row exists at that coordinate.
10249 pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view,
10252 GtkTreePath **path,
10253 PsppSheetViewColumn **column,
10260 g_return_val_if_fail (tree_view != NULL, FALSE);
10267 if (tree_view->priv->bin_window == NULL)
10270 if (tree_view->priv->row_count == 0)
10273 if (x > tree_view->priv->hadjustment->upper)
10276 if (x < 0 || y < 0)
10279 if (column || cell_x)
10281 PsppSheetViewColumn *tmp_column;
10282 PsppSheetViewColumn *last_column = NULL;
10284 gint remaining_x = x;
10285 gboolean found = FALSE;
10288 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10289 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10291 list = (rtl ? list->prev : list->next))
10293 tmp_column = list->data;
10295 if (tmp_column->visible == FALSE)
10298 last_column = tmp_column;
10299 if (remaining_x <= tmp_column->width)
10304 *column = tmp_column;
10307 *cell_x = remaining_x;
10311 remaining_x -= tmp_column->width;
10314 /* If found is FALSE and there is a last_column, then it the remainder
10315 * space is in that area
10322 *column = last_column;
10325 *cell_x = last_column->width + remaining_x;
10334 y_offset = pspp_sheet_view_find_offset (tree_view,
10335 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10342 *cell_y = y_offset;
10345 *path = _pspp_sheet_view_find_path (tree_view, node);
10350 /* Computes 'cell_area' from 'background_area', which must be the background
10351 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10352 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10353 the cell area as passed to _pspp_sheet_view_column_cell_render().
10355 'column' is required to properly adjust 'cell_area->x' and
10356 'cell_area->width'. It may be set to NULL if these values are not of
10357 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10360 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
10361 PsppSheetViewColumn *column,
10362 const GdkRectangle *background_area,
10363 gboolean subtract_focus_rect,
10364 GdkRectangle *cell_area)
10366 gint vertical_separator;
10367 gint horizontal_separator;
10369 *cell_area = *background_area;
10371 gtk_widget_style_get (GTK_WIDGET (tree_view),
10372 "vertical-separator", &vertical_separator,
10373 "horizontal-separator", &horizontal_separator,
10375 cell_area->x += horizontal_separator / 2;
10376 cell_area->y += vertical_separator / 2;
10377 cell_area->width -= horizontal_separator;
10378 cell_area->height -= vertical_separator;
10380 if (subtract_focus_rect)
10382 int focus_line_width;
10384 gtk_widget_style_get (GTK_WIDGET (tree_view),
10385 "focus-line-width", &focus_line_width,
10387 cell_area->x += focus_line_width;
10388 cell_area->y += focus_line_width;
10389 cell_area->width -= 2 * focus_line_width;
10390 cell_area->height -= 2 * focus_line_width;
10393 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10395 gint grid_line_width;
10396 gtk_widget_style_get (GTK_WIDGET (tree_view),
10397 "grid-line-width", &grid_line_width,
10400 if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10401 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10404 PsppSheetViewColumn *first_column, *last_column;
10407 /* Find the last visible column. */
10408 last_column = NULL;
10409 for (list = g_list_last (tree_view->priv->columns);
10413 PsppSheetViewColumn *c = list->data;
10421 /* Find the first visible column. */
10422 first_column = NULL;
10423 for (list = g_list_first (tree_view->priv->columns);
10427 PsppSheetViewColumn *c = list->data;
10435 if (column == first_column)
10437 cell_area->width -= grid_line_width / 2;
10439 else if (column == last_column)
10441 cell_area->x += grid_line_width / 2;
10442 cell_area->width -= grid_line_width / 2;
10446 cell_area->x += grid_line_width / 2;
10447 cell_area->width -= grid_line_width;
10451 if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10452 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10454 cell_area->y += grid_line_width / 2;
10455 cell_area->height -= grid_line_width;
10459 if (column == NULL)
10462 cell_area->width = 0;
10467 * pspp_sheet_view_get_cell_area:
10468 * @tree_view: a #PsppSheetView
10469 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10470 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10471 * @rect: rectangle to fill with cell rect
10473 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10474 * row specified by @path and the column specified by @column. If @path is
10475 * %NULL, or points to a path not currently displayed, the @y and @height fields
10476 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10477 * fields will be filled with 0. The sum of all cell rects does not cover the
10478 * entire tree; there are extra pixels in between rows, for example. The
10479 * returned rectangle is equivalent to the @cell_area passed to
10480 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10484 pspp_sheet_view_get_cell_area (PsppSheetView *tree_view,
10486 PsppSheetViewColumn *column,
10487 GdkRectangle *rect)
10489 GdkRectangle background_area;
10491 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10492 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10493 g_return_if_fail (rect != NULL);
10494 g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10495 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10497 pspp_sheet_view_get_background_area (tree_view, path, column,
10499 pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10504 * pspp_sheet_view_get_background_area:
10505 * @tree_view: a #PsppSheetView
10506 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10507 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10508 * @rect: rectangle to fill with cell background rect
10510 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10511 * row specified by @path and the column specified by @column. If @path is
10512 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10513 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10514 * fields will be filled with 0. The returned rectangle is equivalent to the
10515 * @background_area passed to gtk_cell_renderer_render(). These background
10516 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10517 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10518 * itself, excluding surrounding borders.
10522 pspp_sheet_view_get_background_area (PsppSheetView *tree_view,
10524 PsppSheetViewColumn *column,
10525 GdkRectangle *rect)
10529 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10530 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10531 g_return_if_fail (rect != NULL);
10540 /* Get vertical coords */
10542 _pspp_sheet_view_find_node (tree_view, path, &node);
10546 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10548 rect->height = ROW_HEIGHT (tree_view);
10555 pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10556 rect->width = x2 - rect->x;
10561 * pspp_sheet_view_get_visible_rect:
10562 * @tree_view: a #PsppSheetView
10563 * @visible_rect: rectangle to fill
10565 * Fills @visible_rect with the currently-visible region of the
10566 * buffer, in tree coordinates. Convert to bin_window coordinates with
10567 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10568 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10569 * scrollable area of the tree.
10572 pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view,
10573 GdkRectangle *visible_rect)
10577 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10579 widget = GTK_WIDGET (tree_view);
10583 visible_rect->x = tree_view->priv->hadjustment->value;
10584 visible_rect->y = tree_view->priv->vadjustment->value;
10585 visible_rect->width = widget->allocation.width;
10586 visible_rect->height = widget->allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10591 * pspp_sheet_view_widget_to_tree_coords:
10592 * @tree_view: a #PsppSheetView
10593 * @wx: X coordinate relative to bin_window
10594 * @wy: Y coordinate relative to bin_window
10595 * @tx: return location for tree X coordinate
10596 * @ty: return location for tree Y coordinate
10598 * Converts bin_window coordinates to coordinates for the
10599 * tree (the full scrollable area of the tree).
10601 * Deprecated: 2.12: Due to historial reasons the name of this function is
10602 * incorrect. For converting coordinates relative to the widget to
10603 * bin_window coordinates, please see
10604 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10608 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10614 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10617 *tx = wx + tree_view->priv->hadjustment->value;
10619 *ty = wy + tree_view->priv->dy;
10623 * pspp_sheet_view_tree_to_widget_coords:
10624 * @tree_view: a #PsppSheetView
10625 * @tx: tree X coordinate
10626 * @ty: tree Y coordinate
10627 * @wx: return location for X coordinate relative to bin_window
10628 * @wy: return location for Y coordinate relative to bin_window
10630 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10631 * to bin_window coordinates.
10633 * Deprecated: 2.12: Due to historial reasons the name of this function is
10634 * incorrect. For converting bin_window coordinates to coordinates relative
10635 * to bin_window, please see
10636 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10640 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10646 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10649 *wx = tx - tree_view->priv->hadjustment->value;
10651 *wy = ty - tree_view->priv->dy;
10656 * pspp_sheet_view_convert_widget_to_tree_coords:
10657 * @tree_view: a #PsppSheetView
10658 * @wx: X coordinate relative to the widget
10659 * @wy: Y coordinate relative to the widget
10660 * @tx: return location for tree X coordinate
10661 * @ty: return location for tree Y coordinate
10663 * Converts widget coordinates to coordinates for the
10664 * tree (the full scrollable area of the tree).
10669 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10677 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10679 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10682 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10688 * pspp_sheet_view_convert_tree_to_widget_coords:
10689 * @tree_view: a #PsppSheetView
10690 * @tx: X coordinate relative to the tree
10691 * @ty: Y coordinate relative to the tree
10692 * @wx: return location for widget X coordinate
10693 * @wy: return location for widget Y coordinate
10695 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10696 * to widget coordinates.
10701 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10709 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10711 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10714 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10720 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10721 * @tree_view: a #PsppSheetView
10722 * @wx: X coordinate relative to the widget
10723 * @wy: Y coordinate relative to the widget
10724 * @bx: return location for bin_window X coordinate
10725 * @by: return location for bin_window Y coordinate
10727 * Converts widget coordinates to coordinates for the bin_window
10728 * (see pspp_sheet_view_get_bin_window()).
10733 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10739 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10742 *bx = wx + tree_view->priv->hadjustment->value;
10744 *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10748 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10749 * @tree_view: a #PsppSheetView
10750 * @bx: bin_window X coordinate
10751 * @by: bin_window Y coordinate
10752 * @wx: return location for widget X coordinate
10753 * @wy: return location for widget Y coordinate
10755 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10756 * to widget relative coordinates.
10761 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10767 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10770 *wx = bx - tree_view->priv->hadjustment->value;
10772 *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10776 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10777 * @tree_view: a #PsppSheetView
10778 * @tx: tree X coordinate
10779 * @ty: tree Y coordinate
10780 * @bx: return location for X coordinate relative to bin_window
10781 * @by: return location for Y coordinate relative to bin_window
10783 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10784 * to bin_window coordinates.
10789 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10795 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10800 *by = ty - tree_view->priv->dy;
10804 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10805 * @tree_view: a #PsppSheetView
10806 * @bx: X coordinate relative to bin_window
10807 * @by: Y coordinate relative to bin_window
10808 * @tx: return location for tree X coordinate
10809 * @ty: return location for tree Y coordinate
10811 * Converts bin_window coordinates to coordinates for the
10812 * tree (the full scrollable area of the tree).
10817 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10823 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10828 *ty = by + tree_view->priv->dy;
10834 * pspp_sheet_view_get_visible_range:
10835 * @tree_view: A #PsppSheetView
10836 * @start_path: (allow-none): Return location for start of region, or %NULL.
10837 * @end_path: (allow-none): Return location for end of region, or %NULL.
10839 * Sets @start_path and @end_path to be the first and last visible path.
10840 * Note that there may be invisible paths in between.
10842 * The paths should be freed with gtk_tree_path_free() after use.
10844 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
10849 pspp_sheet_view_get_visible_range (PsppSheetView *tree_view,
10850 GtkTreePath **start_path,
10851 GtkTreePath **end_path)
10856 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10858 if (!tree_view->priv->row_count)
10865 pspp_sheet_view_find_offset (tree_view,
10866 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
10869 *start_path = _pspp_sheet_view_find_path (tree_view, node);
10878 if (tree_view->priv->height < tree_view->priv->vadjustment->page_size)
10879 y = tree_view->priv->height - 1;
10881 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, tree_view->priv->vadjustment->page_size) - 1;
10883 pspp_sheet_view_find_offset (tree_view, y, &node);
10885 *end_path = _pspp_sheet_view_find_path (tree_view, node);
10894 unset_reorderable (PsppSheetView *tree_view)
10896 if (tree_view->priv->reorderable)
10898 tree_view->priv->reorderable = FALSE;
10899 g_object_notify (G_OBJECT (tree_view), "reorderable");
10904 * pspp_sheet_view_enable_model_drag_source:
10905 * @tree_view: a #PsppSheetView
10906 * @start_button_mask: Mask of allowed buttons to start drag
10907 * @targets: the table of targets that the drag will support
10908 * @n_targets: the number of items in @targets
10909 * @actions: the bitmask of possible actions for a drag from this
10912 * Turns @tree_view into a drag source for automatic DND. Calling this
10913 * method sets #PsppSheetView:reorderable to %FALSE.
10916 pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view,
10917 GdkModifierType start_button_mask,
10918 const GtkTargetEntry *targets,
10920 GdkDragAction actions)
10922 TreeViewDragInfo *di;
10924 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10926 gtk_drag_source_set (GTK_WIDGET (tree_view),
10932 di = ensure_info (tree_view);
10934 di->start_button_mask = start_button_mask;
10935 di->source_actions = actions;
10936 di->source_set = TRUE;
10938 unset_reorderable (tree_view);
10942 * pspp_sheet_view_enable_model_drag_dest:
10943 * @tree_view: a #PsppSheetView
10944 * @targets: the table of targets that the drag will support
10945 * @n_targets: the number of items in @targets
10946 * @actions: the bitmask of possible actions for a drag from this
10949 * Turns @tree_view into a drop destination for automatic DND. Calling
10950 * this method sets #PsppSheetView:reorderable to %FALSE.
10953 pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view,
10954 const GtkTargetEntry *targets,
10956 GdkDragAction actions)
10958 TreeViewDragInfo *di;
10960 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10962 gtk_drag_dest_set (GTK_WIDGET (tree_view),
10968 di = ensure_info (tree_view);
10969 di->dest_set = TRUE;
10971 unset_reorderable (tree_view);
10975 * pspp_sheet_view_unset_rows_drag_source:
10976 * @tree_view: a #PsppSheetView
10978 * Undoes the effect of
10979 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
10980 * #PsppSheetView:reorderable to %FALSE.
10983 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
10985 TreeViewDragInfo *di;
10987 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10989 di = get_info (tree_view);
10993 if (di->source_set)
10995 gtk_drag_source_unset (GTK_WIDGET (tree_view));
10996 di->source_set = FALSE;
10999 if (!di->dest_set && !di->source_set)
11000 remove_info (tree_view);
11003 unset_reorderable (tree_view);
11007 * pspp_sheet_view_unset_rows_drag_dest:
11008 * @tree_view: a #PsppSheetView
11010 * Undoes the effect of
11011 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
11012 * #PsppSheetView:reorderable to %FALSE.
11015 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
11017 TreeViewDragInfo *di;
11019 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11021 di = get_info (tree_view);
11027 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
11028 di->dest_set = FALSE;
11031 if (!di->dest_set && !di->source_set)
11032 remove_info (tree_view);
11035 unset_reorderable (tree_view);
11039 * pspp_sheet_view_set_drag_dest_row:
11040 * @tree_view: a #PsppSheetView
11041 * @path: (allow-none): The path of the row to highlight, or %NULL.
11042 * @pos: Specifies whether to drop before, after or into the row
11044 * Sets the row that is highlighted for feedback.
11047 pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view,
11049 PsppSheetViewDropPosition pos)
11051 GtkTreePath *current_dest;
11053 /* Note; this function is exported to allow a custom DND
11054 * implementation, so it can't touch TreeViewDragInfo
11057 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11059 current_dest = NULL;
11061 if (tree_view->priv->drag_dest_row)
11063 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11064 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
11067 /* special case a drop on an empty model */
11068 tree_view->priv->empty_view_drop = 0;
11070 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
11071 && gtk_tree_path_get_depth (path) == 1
11072 && gtk_tree_path_get_indices (path)[0] == 0)
11076 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
11080 tree_view->priv->empty_view_drop = 1;
11083 tree_view->priv->drag_dest_pos = pos;
11087 tree_view->priv->drag_dest_row =
11088 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
11089 pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
11092 tree_view->priv->drag_dest_row = NULL;
11096 int node, new_node;
11098 _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11099 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11103 new_node = pspp_sheet_view_node_next (tree_view, node);
11105 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11107 new_node = pspp_sheet_view_node_prev (tree_view, node);
11109 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11111 gtk_tree_path_free (current_dest);
11116 * pspp_sheet_view_get_drag_dest_row:
11117 * @tree_view: a #PsppSheetView
11118 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11119 * @pos: (allow-none): Return location for the drop position, or %NULL
11121 * Gets information about the row that is highlighted for feedback.
11124 pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view,
11125 GtkTreePath **path,
11126 PsppSheetViewDropPosition *pos)
11128 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11132 if (tree_view->priv->drag_dest_row)
11133 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11136 if (tree_view->priv->empty_view_drop)
11137 *path = gtk_tree_path_new_from_indices (0, -1);
11144 *pos = tree_view->priv->drag_dest_pos;
11148 * pspp_sheet_view_get_dest_row_at_pos:
11149 * @tree_view: a #PsppSheetView
11150 * @drag_x: the position to determine the destination row for
11151 * @drag_y: the position to determine the destination row for
11152 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11153 * @pos: (allow-none): Return location for the drop position, or %NULL
11155 * Determines the destination row for a given position. @drag_x and
11156 * @drag_y are expected to be in widget coordinates. This function is only
11157 * meaningful if @tree_view is realized. Therefore this function will always
11158 * return %FALSE if @tree_view is not realized or does not have a model.
11160 * Return value: whether there is a row at the given position, %TRUE if this
11161 * is indeed the case.
11164 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view,
11167 GtkTreePath **path,
11168 PsppSheetViewDropPosition *pos)
11172 gdouble offset_into_row;
11175 PsppSheetViewColumn *column = NULL;
11176 GtkTreePath *tmp_path = NULL;
11178 /* Note; this function is exported to allow a custom DND
11179 * implementation, so it can't touch TreeViewDragInfo
11182 g_return_val_if_fail (tree_view != NULL, FALSE);
11183 g_return_val_if_fail (drag_x >= 0, FALSE);
11184 g_return_val_if_fail (drag_y >= 0, FALSE);
11189 if (tree_view->priv->bin_window == NULL)
11192 if (tree_view->priv->row_count == 0)
11195 /* If in the top third of a row, we drop before that row; if
11196 * in the bottom third, drop after that row; if in the middle,
11197 * and the row has children, drop into the row.
11199 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11202 if (!pspp_sheet_view_get_path_at_pos (tree_view,
11211 pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11214 offset_into_row = cell_y;
11219 gtk_tree_path_free (tmp_path);
11223 third = cell.height / 3.0;
11227 if (offset_into_row < third)
11229 *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11231 else if (offset_into_row < (cell.height / 2.0))
11233 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11235 else if (offset_into_row < third * 2.0)
11237 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11241 *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11250 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11252 * pspp_sheet_view_create_row_drag_icon:
11253 * @tree_view: a #PsppSheetView
11254 * @path: a #GtkTreePath in @tree_view
11256 * Creates a #GdkPixmap representation of the row at @path.
11257 * This image is used for a drag icon.
11259 * Return value: a newly-allocated pixmap of the drag icon.
11262 pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view,
11269 GdkRectangle background_area;
11270 GdkRectangle expose_area;
11272 /* start drawing inside the black outline */
11274 GdkDrawable *drawable;
11275 gint bin_window_width;
11278 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11279 g_return_val_if_fail (path != NULL, NULL);
11281 widget = GTK_WIDGET (tree_view);
11283 if (!gtk_widget_get_realized (widget))
11286 _pspp_sheet_view_find_node (tree_view,
11293 if (!gtk_tree_model_get_iter (tree_view->priv->model,
11300 background_area.y = y;
11301 background_area.height = ROW_HEIGHT (tree_view);
11303 gdk_drawable_get_size (tree_view->priv->bin_window,
11304 &bin_window_width, NULL);
11306 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11307 bin_window_width + 2,
11308 background_area.height + 2,
11313 expose_area.width = bin_window_width + 2;
11314 expose_area.height = background_area.height + 2;
11316 gdk_draw_rectangle (drawable,
11317 widget->style->base_gc [gtk_widget_get_state (widget)],
11320 bin_window_width + 2,
11321 background_area.height + 2);
11323 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11325 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11327 list = (rtl ? list->prev : list->next))
11329 PsppSheetViewColumn *column = list->data;
11330 GdkRectangle cell_area;
11331 gint vertical_separator;
11333 if (!column->visible)
11336 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11338 background_area.x = cell_offset;
11339 background_area.width = column->width;
11341 gtk_widget_style_get (widget,
11342 "vertical-separator", &vertical_separator,
11345 cell_area = background_area;
11347 cell_area.y += vertical_separator / 2;
11348 cell_area.height -= vertical_separator;
11350 if (pspp_sheet_view_column_cell_is_visible (column))
11351 _pspp_sheet_view_column_cell_render (column,
11357 cell_offset += column->width;
11360 gdk_draw_rectangle (drawable,
11361 widget->style->black_gc,
11364 bin_window_width + 1,
11365 background_area.height + 1);
11372 * pspp_sheet_view_set_destroy_count_func:
11373 * @tree_view: A #PsppSheetView
11374 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11375 * @data: (allow-none): User data to be passed to @func, or %NULL
11376 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11378 * This function should almost never be used. It is meant for private use by
11379 * ATK for determining the number of visible children that are removed when a row is deleted.
11382 pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view,
11383 PsppSheetDestroyCountFunc func,
11385 GDestroyNotify destroy)
11387 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11389 if (tree_view->priv->destroy_count_destroy)
11390 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11392 tree_view->priv->destroy_count_func = func;
11393 tree_view->priv->destroy_count_data = data;
11394 tree_view->priv->destroy_count_destroy = destroy;
11399 * Interactive search
11403 * pspp_sheet_view_set_enable_search:
11404 * @tree_view: A #PsppSheetView
11405 * @enable_search: %TRUE, if the user can search interactively
11407 * If @enable_search is set, then the user can type in text to search through
11408 * the tree interactively (this is sometimes called "typeahead find").
11410 * Note that even if this is %FALSE, the user can still initiate a search
11411 * using the "start-interactive-search" key binding.
11414 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11415 gboolean enable_search)
11417 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11419 enable_search = !!enable_search;
11421 if (tree_view->priv->enable_search != enable_search)
11423 tree_view->priv->enable_search = enable_search;
11424 g_object_notify (G_OBJECT (tree_view), "enable-search");
11429 * pspp_sheet_view_get_enable_search:
11430 * @tree_view: A #PsppSheetView
11432 * Returns whether or not the tree allows to start interactive searching
11433 * by typing in text.
11435 * Return value: whether or not to let the user search interactively
11438 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11440 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11442 return tree_view->priv->enable_search;
11447 * pspp_sheet_view_get_search_column:
11448 * @tree_view: A #PsppSheetView
11450 * Gets the column searched on by the interactive search code.
11452 * Return value: the column the interactive search code searches in.
11455 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11457 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11459 return (tree_view->priv->search_column);
11463 * pspp_sheet_view_set_search_column:
11464 * @tree_view: A #PsppSheetView
11465 * @column: the column of the model to search in, or -1 to disable searching
11467 * Sets @column as the column where the interactive search code should
11468 * search in for the current model.
11470 * If the search column is set, users can use the "start-interactive-search"
11471 * key binding to bring up search popup. The enable-search property controls
11472 * whether simply typing text will also start an interactive search.
11474 * Note that @column refers to a column of the current model. The search
11475 * column is reset to -1 when the model is changed.
11478 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11481 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11482 g_return_if_fail (column >= -1);
11484 if (tree_view->priv->search_column == column)
11487 tree_view->priv->search_column = column;
11488 g_object_notify (G_OBJECT (tree_view), "search-column");
11492 * pspp_sheet_view_get_search_equal_func:
11493 * @tree_view: A #PsppSheetView
11495 * Returns the compare function currently in use.
11497 * Return value: the currently used compare function for the search code.
11500 PsppSheetViewSearchEqualFunc
11501 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11503 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11505 return tree_view->priv->search_equal_func;
11509 * pspp_sheet_view_set_search_equal_func:
11510 * @tree_view: A #PsppSheetView
11511 * @search_equal_func: the compare function to use during the search
11512 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11513 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11515 * Sets the compare function for the interactive search capabilities; note
11516 * that somewhat like strcmp() returning 0 for equality
11517 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11520 pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view,
11521 PsppSheetViewSearchEqualFunc search_equal_func,
11522 gpointer search_user_data,
11523 GDestroyNotify search_destroy)
11525 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11526 g_return_if_fail (search_equal_func != NULL);
11528 if (tree_view->priv->search_destroy)
11529 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11531 tree_view->priv->search_equal_func = search_equal_func;
11532 tree_view->priv->search_user_data = search_user_data;
11533 tree_view->priv->search_destroy = search_destroy;
11534 if (tree_view->priv->search_equal_func == NULL)
11535 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11539 * pspp_sheet_view_get_search_entry:
11540 * @tree_view: A #PsppSheetView
11542 * Returns the #GtkEntry which is currently in use as interactive search
11543 * entry for @tree_view. In case the built-in entry is being used, %NULL
11544 * will be returned.
11546 * Return value: the entry currently in use as search entry.
11551 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11553 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11555 if (tree_view->priv->search_custom_entry_set)
11556 return GTK_ENTRY (tree_view->priv->search_entry);
11562 * pspp_sheet_view_set_search_entry:
11563 * @tree_view: A #PsppSheetView
11564 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11566 * Sets the entry which the interactive search code will use for this
11567 * @tree_view. This is useful when you want to provide a search entry
11568 * in our interface at all time at a fixed position. Passing %NULL for
11569 * @entry will make the interactive search code use the built-in popup
11575 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11578 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11579 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11581 if (tree_view->priv->search_custom_entry_set)
11583 if (tree_view->priv->search_entry_changed_id)
11585 g_signal_handler_disconnect (tree_view->priv->search_entry,
11586 tree_view->priv->search_entry_changed_id);
11587 tree_view->priv->search_entry_changed_id = 0;
11589 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11590 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11593 g_object_unref (tree_view->priv->search_entry);
11595 else if (tree_view->priv->search_window)
11597 gtk_widget_destroy (tree_view->priv->search_window);
11599 tree_view->priv->search_window = NULL;
11604 tree_view->priv->search_entry = g_object_ref (entry);
11605 tree_view->priv->search_custom_entry_set = TRUE;
11607 if (tree_view->priv->search_entry_changed_id == 0)
11609 tree_view->priv->search_entry_changed_id =
11610 g_signal_connect (tree_view->priv->search_entry, "changed",
11611 G_CALLBACK (pspp_sheet_view_search_init),
11615 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11616 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11619 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11623 tree_view->priv->search_entry = NULL;
11624 tree_view->priv->search_custom_entry_set = FALSE;
11629 * pspp_sheet_view_set_search_position_func:
11630 * @tree_view: A #PsppSheetView
11631 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11632 * to use the default search position function
11633 * @data: (allow-none): user data to pass to @func, or %NULL
11634 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11636 * Sets the function to use when positioning the search dialog.
11641 pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view,
11642 PsppSheetViewSearchPositionFunc func,
11643 gpointer user_data,
11644 GDestroyNotify destroy)
11646 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11648 if (tree_view->priv->search_position_destroy)
11649 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11651 tree_view->priv->search_position_func = func;
11652 tree_view->priv->search_position_user_data = user_data;
11653 tree_view->priv->search_position_destroy = destroy;
11654 if (tree_view->priv->search_position_func == NULL)
11655 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11659 * pspp_sheet_view_get_search_position_func:
11660 * @tree_view: A #PsppSheetView
11662 * Returns the positioning function currently in use.
11664 * Return value: the currently used function for positioning the search dialog.
11668 PsppSheetViewSearchPositionFunc
11669 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11671 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11673 return tree_view->priv->search_position_func;
11678 pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
11679 PsppSheetView *tree_view)
11681 if (tree_view->priv->disable_popdown)
11684 if (tree_view->priv->search_entry_changed_id)
11686 g_signal_handler_disconnect (tree_view->priv->search_entry,
11687 tree_view->priv->search_entry_changed_id);
11688 tree_view->priv->search_entry_changed_id = 0;
11690 if (tree_view->priv->typeselect_flush_timeout)
11692 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11693 tree_view->priv->typeselect_flush_timeout = 0;
11696 if (gtk_widget_get_visible (search_dialog))
11698 /* send focus-in event */
11699 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11700 gtk_widget_hide (search_dialog);
11701 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11702 send_focus_change (GTK_WIDGET (tree_view), TRUE);
11707 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11708 GtkWidget *search_dialog,
11709 gpointer user_data)
11712 gint tree_x, tree_y;
11713 gint tree_width, tree_height;
11714 GdkWindow *tree_window = GTK_WIDGET (tree_view)->window;
11715 GdkScreen *screen = gdk_drawable_get_screen (tree_window);
11716 GtkRequisition requisition;
11718 GdkRectangle monitor;
11720 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11721 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11723 gtk_widget_realize (search_dialog);
11725 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11726 gdk_drawable_get_size (tree_window,
11729 gtk_widget_size_request (search_dialog, &requisition);
11731 if (tree_x + tree_width > gdk_screen_get_width (screen))
11732 x = gdk_screen_get_width (screen) - requisition.width;
11733 else if (tree_x + tree_width - requisition.width < 0)
11736 x = tree_x + tree_width - requisition.width;
11738 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11739 y = gdk_screen_get_height (screen) - requisition.height;
11740 else if (tree_y + tree_height < 0) /* isn't really possible ... */
11743 y = tree_y + tree_height;
11745 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11749 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11753 PsppSheetView *tree_view = (PsppSheetView *)data;
11755 tree_view->priv->disable_popdown = 1;
11756 g_signal_connect (menu, "hide",
11757 G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11760 /* Because we're visible but offscreen, we just set a flag in the preedit
11764 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11765 PsppSheetView *tree_view)
11767 tree_view->priv->imcontext_changed = 1;
11768 if (tree_view->priv->typeselect_flush_timeout)
11770 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11771 tree_view->priv->typeselect_flush_timeout =
11772 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11773 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11780 pspp_sheet_view_search_activate (GtkEntry *entry,
11781 PsppSheetView *tree_view)
11786 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11789 /* If we have a row selected and it's the cursor row, we activate
11791 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11793 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11795 _pspp_sheet_view_find_node (tree_view, path, &node);
11797 if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11798 pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11800 gtk_tree_path_free (path);
11805 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11807 PsppSheetView *tree_view = (PsppSheetView *)data;
11809 tree_view->priv->disable_popdown = 0;
11815 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11818 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11822 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11823 GdkEventAny *event,
11824 PsppSheetView *tree_view)
11826 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11828 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11834 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11835 GdkEventButton *event,
11836 PsppSheetView *tree_view)
11838 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11840 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11842 if (event->window == tree_view->priv->bin_window)
11843 pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
11849 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
11850 GdkEventScroll *event,
11851 PsppSheetView *tree_view)
11853 gboolean retval = FALSE;
11855 if (event->direction == GDK_SCROLL_UP)
11857 pspp_sheet_view_search_move (widget, tree_view, TRUE);
11860 else if (event->direction == GDK_SCROLL_DOWN)
11862 pspp_sheet_view_search_move (widget, tree_view, FALSE);
11866 /* renew the flush timeout */
11867 if (retval && tree_view->priv->typeselect_flush_timeout
11868 && !tree_view->priv->search_custom_entry_set)
11870 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11871 tree_view->priv->typeselect_flush_timeout =
11872 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11873 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11881 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
11882 GdkEventKey *event,
11883 PsppSheetView *tree_view)
11885 gboolean retval = FALSE;
11887 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11888 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11890 /* close window and cancel the search */
11891 if (!tree_view->priv->search_custom_entry_set
11892 && (event->keyval == GDK_Escape ||
11893 event->keyval == GDK_Tab ||
11894 event->keyval == GDK_KP_Tab ||
11895 event->keyval == GDK_ISO_Left_Tab))
11897 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11901 /* select previous matching iter */
11902 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
11904 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11905 gtk_widget_error_bell (widget);
11910 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
11911 && (event->keyval == GDK_g || event->keyval == GDK_G))
11913 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
11914 gtk_widget_error_bell (widget);
11919 /* select next matching iter */
11920 if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
11922 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11923 gtk_widget_error_bell (widget);
11928 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
11929 && (event->keyval == GDK_g || event->keyval == GDK_G))
11931 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
11932 gtk_widget_error_bell (widget);
11937 /* renew the flush timeout */
11938 if (retval && tree_view->priv->typeselect_flush_timeout
11939 && !tree_view->priv->search_custom_entry_set)
11941 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11942 tree_view->priv->typeselect_flush_timeout =
11943 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11944 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11951 /* this function returns FALSE if there is a search string but
11952 * nothing was found, and TRUE otherwise.
11955 pspp_sheet_view_search_move (GtkWidget *window,
11956 PsppSheetView *tree_view,
11964 GtkTreeModel *model;
11965 PsppSheetSelection *selection;
11967 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
11969 g_return_val_if_fail (text != NULL, FALSE);
11971 len = strlen (text);
11973 if (up && tree_view->priv->selected_iter == 1)
11974 return strlen (text) < 1;
11976 len = strlen (text);
11981 model = pspp_sheet_view_get_model (tree_view);
11982 selection = pspp_sheet_view_get_selection (tree_view);
11985 pspp_sheet_selection_unselect_all (selection);
11986 if (!gtk_tree_model_get_iter_first (model, &iter))
11989 ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
11990 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
11995 tree_view->priv->selected_iter += up?(-1):(1);
12000 /* return to old iter */
12002 gtk_tree_model_get_iter_first (model, &iter);
12003 pspp_sheet_view_search_iter (model, selection,
12005 &count, tree_view->priv->selected_iter);
12011 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
12015 gpointer search_data)
12017 gboolean retval = TRUE;
12019 gchar *normalized_string;
12020 gchar *normalized_key;
12021 gchar *case_normalized_string = NULL;
12022 gchar *case_normalized_key = NULL;
12023 GValue value = {0,};
12024 GValue transformed = {0,};
12026 gtk_tree_model_get_value (model, iter, column, &value);
12028 g_value_init (&transformed, G_TYPE_STRING);
12030 if (!g_value_transform (&value, &transformed))
12032 g_value_unset (&value);
12036 g_value_unset (&value);
12038 str = g_value_get_string (&transformed);
12041 g_value_unset (&transformed);
12045 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
12046 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
12048 if (normalized_string && normalized_key)
12050 case_normalized_string = g_utf8_casefold (normalized_string, -1);
12051 case_normalized_key = g_utf8_casefold (normalized_key, -1);
12053 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
12057 g_value_unset (&transformed);
12058 g_free (normalized_key);
12059 g_free (normalized_string);
12060 g_free (case_normalized_key);
12061 g_free (case_normalized_string);
12067 pspp_sheet_view_search_iter (GtkTreeModel *model,
12068 PsppSheetSelection *selection,
12077 PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
12079 path = gtk_tree_model_get_path (model, iter);
12080 _pspp_sheet_view_find_node (tree_view, path, &node);
12084 gboolean done = FALSE;
12086 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
12091 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
12093 pspp_sheet_selection_select_iter (selection, iter);
12094 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE);
12097 gtk_tree_path_free (path);
12106 node = pspp_sheet_view_node_next (tree_view, node);
12112 has_next = gtk_tree_model_iter_next (model, iter);
12115 gtk_tree_path_next (path);
12118 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12123 gtk_tree_path_free (path);
12125 /* we've run out of tree, done with this func */
12137 pspp_sheet_view_search_init (GtkWidget *entry,
12138 PsppSheetView *tree_view)
12144 GtkTreeModel *model;
12145 PsppSheetSelection *selection;
12147 g_return_if_fail (GTK_IS_ENTRY (entry));
12148 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12150 text = gtk_entry_get_text (GTK_ENTRY (entry));
12152 model = pspp_sheet_view_get_model (tree_view);
12153 selection = pspp_sheet_view_get_selection (tree_view);
12156 pspp_sheet_selection_unselect_all (selection);
12157 if (tree_view->priv->typeselect_flush_timeout
12158 && !tree_view->priv->search_custom_entry_set)
12160 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12161 tree_view->priv->typeselect_flush_timeout =
12162 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12163 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12170 if (!gtk_tree_model_get_iter_first (model, &iter))
12173 ret = pspp_sheet_view_search_iter (model, selection,
12178 tree_view->priv->selected_iter = 1;
12182 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12183 PsppSheetView *tree_view)
12185 if (tree_view->priv->edited_column == NULL)
12188 _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12189 tree_view->priv->edited_column = NULL;
12191 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12192 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12194 g_signal_handlers_disconnect_by_func (cell_editable,
12195 pspp_sheet_view_remove_widget,
12197 g_signal_handlers_disconnect_by_func (cell_editable,
12198 pspp_sheet_view_editable_button_press_event,
12200 g_signal_handlers_disconnect_by_func (cell_editable,
12201 pspp_sheet_view_editable_clicked,
12204 gtk_container_remove (GTK_CONTAINER (tree_view),
12205 GTK_WIDGET (cell_editable));
12207 /* FIXME should only redraw a single node */
12208 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12212 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12213 GtkTreePath *cursor_path)
12216 GdkRectangle background_area;
12217 GdkRectangle cell_area;
12218 GtkCellEditable *editable_widget = NULL;
12219 gchar *path_string;
12220 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12221 gint retval = FALSE;
12224 g_assert (tree_view->priv->focus_column);
12226 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12229 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12230 if (cursor_node < 0)
12233 path_string = gtk_tree_path_to_string (cursor_path);
12234 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12236 pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12237 tree_view->priv->model,
12239 pspp_sheet_view_get_background_area (tree_view,
12241 tree_view->priv->focus_column,
12243 pspp_sheet_view_get_cell_area (tree_view,
12245 tree_view->priv->focus_column,
12248 if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12257 if (editable_widget != NULL)
12261 GtkCellRenderer *cell;
12264 cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12266 _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12269 area.width -= right + left;
12271 pspp_sheet_view_real_start_editing (tree_view,
12272 tree_view->priv->focus_column,
12281 g_free (path_string);
12286 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12287 GdkEventButton *event,
12288 PsppSheetView *sheet_view)
12292 node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12293 "pspp-sheet-view-node"));
12294 return pspp_sheet_view_row_head_clicked (sheet_view,
12296 sheet_view->priv->edited_column,
12301 pspp_sheet_view_editable_clicked (GtkButton *button,
12302 PsppSheetView *sheet_view)
12304 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12309 is_all_selected (GtkWidget *widget)
12311 GtkEntryBuffer *buffer;
12312 gint start_pos, end_pos;
12314 if (!GTK_IS_ENTRY (widget))
12317 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12318 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12319 &start_pos, &end_pos)
12321 && end_pos == gtk_entry_buffer_get_length (buffer));
12325 is_at_left (GtkWidget *widget)
12327 return (GTK_IS_ENTRY (widget)
12328 && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12332 is_at_right (GtkWidget *widget)
12334 GtkEntryBuffer *buffer;
12337 if (!GTK_IS_ENTRY (widget))
12340 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12341 length = gtk_entry_buffer_get_length (buffer);
12342 return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12346 pspp_sheet_view_event (GtkWidget *widget,
12347 GdkEventKey *event,
12348 PsppSheetView *tree_view)
12350 PsppSheetViewColumn *column;
12357 /* Intercept only key press events.
12358 It would make sense to use "key-press-event" instead of "event", but
12359 GtkEntry attaches its own signal handler to "key-press-event" that runs
12360 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12362 if (event->type != GDK_KEY_PRESS)
12365 keyval = event->keyval;
12367 switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12370 switch (event->keyval)
12372 case GDK_Left: case GDK_KP_Left:
12373 case GDK_Home: case GDK_KP_Home:
12374 if (!is_all_selected (widget) && !is_at_left (widget))
12378 case GDK_Right: case GDK_KP_Right:
12379 case GDK_End: case GDK_KP_End:
12380 if (!is_all_selected (widget) && !is_at_right (widget))
12384 case GDK_Up: case GDK_KP_Up:
12385 case GDK_Down: case GDK_KP_Down:
12388 case GDK_Page_Up: case GDK_KP_Page_Up:
12389 case GDK_Page_Down: case GDK_KP_Page_Down:
12400 case GDK_Tab: case GDK_KP_Tab:
12401 case GDK_ISO_Left_Tab:
12410 case GDK_SHIFT_MASK:
12411 switch (event->keyval)
12414 case GDK_ISO_Left_Tab:
12423 case GDK_CONTROL_MASK:
12424 switch (event->keyval)
12426 case GDK_Left: case GDK_KP_Left:
12427 if (!is_all_selected (widget) && !is_at_left (widget))
12431 case GDK_Right: case GDK_KP_Right:
12432 if (!is_all_selected (widget) && !is_at_right (widget))
12445 row = tree_view->priv->edited_row;
12446 column = tree_view->priv->edited_column;
12447 path = gtk_tree_path_new_from_indices (row, -1);
12449 pspp_sheet_view_stop_editing (tree_view, cancel);
12450 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12452 pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12453 gtk_tree_path_free (path);
12455 handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12456 GTK_OBJECT (tree_view));
12458 g_signal_stop_emission_by_name (widget, "event");
12460 pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12461 pspp_sheet_view_start_editing (tree_view, path);
12462 gtk_tree_path_free (path);
12468 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12471 PsppSheetView *sheet_view = data;
12473 g_signal_connect (widget, "event",
12474 G_CALLBACK (pspp_sheet_view_event),
12477 if (GTK_IS_CONTAINER (widget))
12478 gtk_container_foreach (GTK_CONTAINER (widget),
12479 pspp_sheet_view_override_cell_keypresses,
12484 pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
12485 PsppSheetViewColumn *column,
12487 GtkCellEditable *cell_editable,
12488 GdkRectangle *cell_area,
12492 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12493 gint pre_val = tree_view->priv->vadjustment->value;
12494 GtkRequisition requisition;
12497 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12499 tree_view->priv->edited_column = column;
12500 _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12502 row = gtk_tree_path_get_indices (path)[0];
12503 tree_view->priv->edited_row = row;
12504 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE);
12505 cell_area->y += pre_val - (int)tree_view->priv->vadjustment->value;
12507 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12508 pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12509 tree_view->priv->anchor_column = column;
12511 gtk_widget_size_request (GTK_WIDGET (cell_editable), &requisition);
12513 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12515 if (requisition.height < cell_area->height)
12517 gint diff = cell_area->height - requisition.height;
12518 pspp_sheet_view_put (tree_view,
12519 GTK_WIDGET (cell_editable),
12520 cell_area->x, cell_area->y + diff/2,
12521 cell_area->width, requisition.height);
12525 pspp_sheet_view_put (tree_view,
12526 GTK_WIDGET (cell_editable),
12527 cell_area->x, cell_area->y,
12528 cell_area->width, cell_area->height);
12531 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12532 (GdkEvent *)event);
12534 gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12535 g_signal_connect (cell_editable, "remove-widget",
12536 G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12537 if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12538 GTK_IS_BUTTON (cell_editable))
12540 g_signal_connect (cell_editable, "button-press-event",
12541 G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12543 g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12544 GINT_TO_POINTER (row));
12545 g_signal_connect (cell_editable, "clicked",
12546 G_CALLBACK (pspp_sheet_view_editable_clicked),
12550 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12555 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12556 gboolean cancel_editing)
12558 PsppSheetViewColumn *column;
12559 GtkCellRenderer *cell;
12561 if (tree_view->priv->edited_column == NULL)
12565 * This is very evil. We need to do this, because
12566 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12567 * later on. If pspp_sheet_view_row_changed notices
12568 * tree_view->priv->edited_column != NULL, it'll call
12569 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12571 * Please read that again if you intend to modify anything here.
12574 column = tree_view->priv->edited_column;
12575 tree_view->priv->edited_column = NULL;
12577 cell = _pspp_sheet_view_column_get_edited_cell (column);
12578 gtk_cell_renderer_stop_editing (cell, cancel_editing);
12580 if (!cancel_editing)
12581 gtk_cell_editable_editing_done (column->editable_widget);
12583 tree_view->priv->edited_column = column;
12585 gtk_cell_editable_remove_widget (column->editable_widget);
12590 * pspp_sheet_view_set_hover_selection:
12591 * @tree_view: a #PsppSheetView
12592 * @hover: %TRUE to enable hover selection mode
12594 * Enables of disables the hover selection mode of @tree_view.
12595 * Hover selection makes the selected row follow the pointer.
12596 * Currently, this works only for the selection modes
12597 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12602 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12605 hover = hover != FALSE;
12607 if (hover != tree_view->priv->hover_selection)
12609 tree_view->priv->hover_selection = hover;
12611 g_object_notify (G_OBJECT (tree_view), "hover-selection");
12616 * pspp_sheet_view_get_hover_selection:
12617 * @tree_view: a #PsppSheetView
12619 * Returns whether hover selection mode is turned on for @tree_view.
12621 * Return value: %TRUE if @tree_view is in hover selection mode
12626 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12628 return tree_view->priv->hover_selection;
12632 * pspp_sheet_view_set_rubber_banding:
12633 * @tree_view: a #PsppSheetView
12634 * @enable: %TRUE to enable rubber banding
12636 * Enables or disables rubber banding in @tree_view. If the selection mode is
12637 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12638 * banding will allow the user to select multiple rows by dragging the mouse.
12643 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12646 enable = enable != FALSE;
12648 if (enable != tree_view->priv->rubber_banding_enable)
12650 tree_view->priv->rubber_banding_enable = enable;
12652 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12657 * pspp_sheet_view_get_rubber_banding:
12658 * @tree_view: a #PsppSheetView
12660 * Returns whether rubber banding is turned on for @tree_view. If the
12661 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12662 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12663 * select multiple rows by dragging the mouse.
12665 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12670 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12672 return tree_view->priv->rubber_banding_enable;
12676 * pspp_sheet_view_is_rubber_banding_active:
12677 * @tree_view: a #PsppSheetView
12679 * Returns whether a rubber banding operation is currently being done
12682 * Return value: %TRUE if a rubber banding operation is currently being
12683 * done in @tree_view.
12688 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12690 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12692 if (tree_view->priv->rubber_banding_enable
12693 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12700 pspp_sheet_view_grab_notify (GtkWidget *widget,
12701 gboolean was_grabbed)
12703 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12705 tree_view->priv->in_grab = !was_grabbed;
12709 tree_view->priv->pressed_button = -1;
12711 if (tree_view->priv->rubber_band_status)
12712 pspp_sheet_view_stop_rubber_band (tree_view);
12717 pspp_sheet_view_state_changed (GtkWidget *widget,
12718 GtkStateType previous_state)
12720 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12722 if (gtk_widget_get_realized (widget))
12724 gdk_window_set_back_pixmap (widget->window, NULL, FALSE);
12725 gdk_window_set_background (tree_view->priv->bin_window, &widget->style->base[widget->state]);
12728 gtk_widget_queue_draw (widget);
12732 * pspp_sheet_view_get_grid_lines:
12733 * @tree_view: a #PsppSheetView
12735 * Returns which grid lines are enabled in @tree_view.
12737 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12742 PsppSheetViewGridLines
12743 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12745 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12747 return tree_view->priv->grid_lines;
12751 * pspp_sheet_view_set_grid_lines:
12752 * @tree_view: a #PsppSheetView
12753 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12756 * Sets which grid lines to draw in @tree_view.
12761 pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view,
12762 PsppSheetViewGridLines grid_lines)
12764 PsppSheetViewPrivate *priv;
12765 PsppSheetViewGridLines old_grid_lines;
12767 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12769 priv = tree_view->priv;
12771 old_grid_lines = priv->grid_lines;
12772 priv->grid_lines = grid_lines;
12774 if (old_grid_lines != grid_lines)
12776 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12778 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12783 * pspp_sheet_view_get_special_cells:
12784 * @tree_view: a #PsppSheetView
12786 * Returns which grid lines are enabled in @tree_view.
12788 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12789 * the sheet view contain special cells.
12791 PsppSheetViewSpecialCells
12792 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12794 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12796 return tree_view->priv->special_cells;
12800 * pspp_sheet_view_set_special_cells:
12801 * @tree_view: a #PsppSheetView
12802 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12803 * the sheet view contain special cells.
12805 * Sets whether rows in the sheet view contain special cells, controlling the
12806 * rendering of row selections.
12809 pspp_sheet_view_set_special_cells (PsppSheetView *tree_view,
12810 PsppSheetViewSpecialCells special_cells)
12812 PsppSheetViewPrivate *priv;
12814 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12816 priv = tree_view->priv;
12818 if (priv->special_cells != special_cells)
12820 priv->special_cells = special_cells;
12821 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12822 g_object_notify (G_OBJECT (tree_view), "special-cells");
12827 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12829 /* XXX (re)calculate fixed_height if necessary */
12830 return tree_view->priv->fixed_height;
12834 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
12837 g_return_if_fail (fixed_height > 0);
12839 if (tree_view->priv->fixed_height != fixed_height)
12841 tree_view->priv->fixed_height = fixed_height;
12842 g_object_notify (G_OBJECT (tree_view), "fixed-height");
12844 if (!tree_view->priv->fixed_height_set)
12846 tree_view->priv->fixed_height_set = TRUE;
12847 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
12852 * pspp_sheet_view_set_tooltip_row:
12853 * @tree_view: a #PsppSheetView
12854 * @tooltip: a #GtkTooltip
12855 * @path: a #GtkTreePath
12857 * Sets the tip area of @tooltip to be the area covered by the row at @path.
12858 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12859 * See also gtk_tooltip_set_tip_area().
12864 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
12865 GtkTooltip *tooltip,
12868 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12869 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12871 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
12875 * pspp_sheet_view_set_tooltip_cell:
12876 * @tree_view: a #PsppSheetView
12877 * @tooltip: a #GtkTooltip
12878 * @path: (allow-none): a #GtkTreePath or %NULL
12879 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
12880 * @cell: (allow-none): a #GtkCellRenderer or %NULL
12882 * Sets the tip area of @tooltip to the area @path, @column and @cell have
12883 * in common. For example if @path is %NULL and @column is set, the tip
12884 * area will be set to the full area covered by @column. See also
12885 * gtk_tooltip_set_tip_area().
12887 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
12892 pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view,
12893 GtkTooltip *tooltip,
12895 PsppSheetViewColumn *column,
12896 GtkCellRenderer *cell)
12900 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12901 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
12902 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
12903 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
12905 /* Determine x values. */
12906 if (column && cell)
12911 pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
12912 pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
12914 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12917 rect.width = width;
12923 pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
12924 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12927 rect.width = tmp.width;
12932 rect.width = GTK_WIDGET (tree_view)->allocation.width;
12935 /* Determine y values. */
12940 pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
12941 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
12944 rect.height = tmp.height;
12949 rect.height = tree_view->priv->vadjustment->page_size;
12952 gtk_tooltip_set_tip_area (tooltip, &rect);
12956 * pspp_sheet_view_get_tooltip_context:
12957 * @tree_view: a #PsppSheetView
12958 * @x: the x coordinate (relative to widget coordinates)
12959 * @y: the y coordinate (relative to widget coordinates)
12960 * @keyboard_tip: whether this is a keyboard tooltip or not
12961 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
12962 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
12963 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
12965 * This function is supposed to be used in a #GtkWidget::query-tooltip
12966 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
12967 * which are received in the signal handler, should be passed to this
12968 * function without modification.
12970 * The return value indicates whether there is a tree view row at the given
12971 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
12972 * tooltips the row returned will be the cursor row. When %TRUE, then any of
12973 * @model, @path and @iter which have been provided will be set to point to
12974 * that row and the corresponding model. @x and @y will always be converted
12975 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
12977 * Return value: whether or not the given tooltip context points to a row.
12982 pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view,
12985 gboolean keyboard_tip,
12986 GtkTreeModel **model,
12987 GtkTreePath **path,
12990 GtkTreePath *tmppath = NULL;
12992 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12993 g_return_val_if_fail (x != NULL, FALSE);
12994 g_return_val_if_fail (y != NULL, FALSE);
12998 pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
13005 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
13008 if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
13009 &tmppath, NULL, NULL, NULL))
13014 *model = pspp_sheet_view_get_model (tree_view);
13017 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
13023 gtk_tree_path_free (tmppath);
13029 pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget,
13032 gboolean keyboard_tip,
13033 GtkTooltip *tooltip,
13036 GValue value = { 0, };
13037 GValue transformed = { 0, };
13040 GtkTreeModel *model;
13041 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
13043 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
13046 &model, &path, &iter))
13049 gtk_tree_model_get_value (model, &iter,
13050 tree_view->priv->tooltip_column, &value);
13052 g_value_init (&transformed, G_TYPE_STRING);
13054 if (!g_value_transform (&value, &transformed))
13056 g_value_unset (&value);
13057 gtk_tree_path_free (path);
13062 g_value_unset (&value);
13064 if (!g_value_get_string (&transformed))
13066 g_value_unset (&transformed);
13067 gtk_tree_path_free (path);
13072 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
13073 pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
13075 gtk_tree_path_free (path);
13076 g_value_unset (&transformed);
13082 * pspp_sheet_view_set_tooltip_column:
13083 * @tree_view: a #PsppSheetView
13084 * @column: an integer, which is a valid column number for @tree_view's model
13086 * If you only plan to have simple (text-only) tooltips on full rows, you
13087 * can use this function to have #PsppSheetView handle these automatically
13088 * for you. @column should be set to the column in @tree_view's model
13089 * containing the tooltip texts, or -1 to disable this feature.
13091 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
13092 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
13094 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
13095 * so &, <, etc have to be escaped in the text.
13100 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
13103 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13105 if (column == tree_view->priv->tooltip_column)
13110 g_signal_handlers_disconnect_by_func (tree_view,
13111 pspp_sheet_view_set_tooltip_query_cb,
13113 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13117 if (tree_view->priv->tooltip_column == -1)
13119 g_signal_connect (tree_view, "query-tooltip",
13120 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13121 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13125 tree_view->priv->tooltip_column = column;
13126 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13130 * pspp_sheet_view_get_tooltip_column:
13131 * @tree_view: a #PsppSheetView
13133 * Returns the column of @tree_view's model which is being used for
13134 * displaying tooltips on @tree_view's rows.
13136 * Return value: the index of the tooltip column that is currently being
13137 * used, or -1 if this is disabled.
13142 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13144 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13146 return tree_view->priv->tooltip_column;
13150 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13151 GValue *return_accu,
13152 const GValue *handler_return,
13155 gboolean continue_emission;
13156 gboolean signal_handled;
13158 signal_handled = g_value_get_boolean (handler_return);
13159 g_value_set_boolean (return_accu, signal_handled);
13160 continue_emission = !signal_handled;
13162 return continue_emission;
13166 pspp_sheet_view_grid_lines_get_type (void)
13168 static GType etype = 0;
13169 if (G_UNLIKELY(etype == 0)) {
13170 static const GEnumValue values[] = {
13171 { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13172 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13173 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13174 { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13177 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13183 pspp_sheet_view_special_cells_get_type (void)
13185 static GType etype = 0;
13186 if (G_UNLIKELY(etype == 0)) {
13187 static const GEnumValue values[] = {
13188 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13189 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13190 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13193 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);