1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012, 2013 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Library General Public
22 * License as published by the Free Software Foundation; either
23 * version 2 of the License, or (at your option) any later version.
25 * This library is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Library General Public License for more details.
30 * You should have received a copy of the GNU Library General Public
31 * License along with this library; if not, write to the
32 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
33 * Boston, MA 02111-1307, USA.
38 #include "ui/gui/pspp-sheet-private.h"
42 #include <gdk/gdkkeysyms.h>
43 #include <gdk/gdkkeysyms-compat.h>
46 #include "ui/gui/psppire-marshal.h"
47 #include "ui/gui/pspp-sheet-selection.h"
49 #define P_(STRING) STRING
50 #define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
51 #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
53 /* Many keyboard shortcuts for Mac are the same as for X
54 * except they use Command key instead of Control (e.g. Cut,
55 * Copy, Paste). This symbol is for those simple cases. */
56 #ifndef GDK_WINDOWING_QUARTZ
57 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_CONTROL_MASK
59 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_META_MASK
62 #define PSPP_SHEET_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
63 #define PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC (PSPP_SHEET_VIEW_PRIORITY_VALIDATE + 2)
64 #define PSPP_SHEET_VIEW_TIME_MS_PER_IDLE 30
65 #define SCROLL_EDGE_SIZE 15
66 #define EXPANDER_EXTRA_PADDING 4
67 #define PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT 5000
69 /* The "background" areas of all rows/cells add up to cover the entire tree.
70 * The background includes all inter-row and inter-cell spacing.
71 * The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
72 * i.e. just the cells, no spacing.
75 #define BACKGROUND_HEIGHT(tree_view) (tree_view->priv->fixed_height)
76 #define CELL_HEIGHT(tree_view, separator) ((BACKGROUND_HEIGHT (tree_view)) - (separator))
78 /* Translate from bin_window coordinates to rbtree (tree coordinates) and
81 #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->priv->dy)
82 #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->priv->dy)
84 /* This is in bin_window coordinates */
85 #define BACKGROUND_FIRST_PIXEL(tree_view,node) (RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, pspp_sheet_view_node_find_offset (tree_view, (node))))
86 #define CELL_FIRST_PIXEL(tree_view,node,separator) (BACKGROUND_FIRST_PIXEL (tree_view,node) + separator/2)
88 #define ROW_HEIGHT(tree_view) \
89 ((tree_view->priv->fixed_height > 0) ? (tree_view->priv->fixed_height) : (tree_view)->priv->expander_size)
92 typedef struct _PsppSheetViewChild PsppSheetViewChild;
93 struct _PsppSheetViewChild
103 typedef struct _TreeViewDragInfo TreeViewDragInfo;
104 struct _TreeViewDragInfo
106 GdkModifierType start_button_mask;
107 GtkTargetList *_unused_source_target_list;
108 GdkDragAction source_actions;
110 GtkTargetList *_unused_dest_target_list;
112 guint source_set : 1;
128 START_INTERACTIVE_SEARCH,
140 PROP_HEADERS_VISIBLE,
141 PROP_HEADERS_CLICKABLE,
146 PROP_HOVER_SELECTION,
148 PROP_ENABLE_GRID_LINES,
152 PROP_FIXED_HEIGHT_SET
156 static void pspp_sheet_view_finalize (GObject *object);
157 static void pspp_sheet_view_set_property (GObject *object,
161 static void pspp_sheet_view_get_property (GObject *object,
166 static void pspp_sheet_view_dispose (GObject *object);
168 /* gtkwidget signals */
169 static void pspp_sheet_view_realize (GtkWidget *widget);
170 static void pspp_sheet_view_unrealize (GtkWidget *widget);
171 static void pspp_sheet_view_map (GtkWidget *widget);
172 static void pspp_sheet_view_size_request (GtkWidget *widget,
173 GtkRequisition *requisition);
174 static void pspp_sheet_view_size_allocate (GtkWidget *widget,
175 GtkAllocation *allocation);
176 static gboolean pspp_sheet_view_draw (GtkWidget *widget,
178 static gboolean pspp_sheet_view_key_press (GtkWidget *widget,
180 static gboolean pspp_sheet_view_key_release (GtkWidget *widget,
182 static gboolean pspp_sheet_view_motion (GtkWidget *widget,
183 GdkEventMotion *event);
184 static gboolean pspp_sheet_view_enter_notify (GtkWidget *widget,
185 GdkEventCrossing *event);
186 static gboolean pspp_sheet_view_leave_notify (GtkWidget *widget,
187 GdkEventCrossing *event);
188 static gboolean pspp_sheet_view_button_press (GtkWidget *widget,
189 GdkEventButton *event);
190 static gboolean pspp_sheet_view_button_release (GtkWidget *widget,
191 GdkEventButton *event);
192 static gboolean pspp_sheet_view_grab_broken (GtkWidget *widget,
193 GdkEventGrabBroken *event);
195 static void pspp_sheet_view_set_focus_child (GtkContainer *container,
197 static gint pspp_sheet_view_focus_out (GtkWidget *widget,
198 GdkEventFocus *event);
199 static gint pspp_sheet_view_focus (GtkWidget *widget,
200 GtkDirectionType direction);
201 static void pspp_sheet_view_grab_focus (GtkWidget *widget);
202 static void pspp_sheet_view_style_updated (GtkWidget *widget);
203 static void pspp_sheet_view_grab_notify (GtkWidget *widget,
204 gboolean was_grabbed);
205 static void pspp_sheet_view_state_changed (GtkWidget *widget,
206 GtkStateType previous_state);
208 /* container signals */
209 static void pspp_sheet_view_remove (GtkContainer *container,
211 static void pspp_sheet_view_forall (GtkContainer *container,
212 gboolean include_internals,
213 GtkCallback callback,
214 gpointer callback_data);
216 /* Source side drag signals */
217 static void pspp_sheet_view_drag_begin (GtkWidget *widget,
218 GdkDragContext *context);
219 static void pspp_sheet_view_drag_end (GtkWidget *widget,
220 GdkDragContext *context);
221 static void pspp_sheet_view_drag_data_get (GtkWidget *widget,
222 GdkDragContext *context,
223 GtkSelectionData *selection_data,
226 static void pspp_sheet_view_drag_data_delete (GtkWidget *widget,
227 GdkDragContext *context);
229 /* Target side drag signals */
230 static void pspp_sheet_view_drag_leave (GtkWidget *widget,
231 GdkDragContext *context,
233 static gboolean pspp_sheet_view_drag_motion (GtkWidget *widget,
234 GdkDragContext *context,
238 static gboolean pspp_sheet_view_drag_drop (GtkWidget *widget,
239 GdkDragContext *context,
243 static void pspp_sheet_view_drag_data_received (GtkWidget *widget,
244 GdkDragContext *context,
247 GtkSelectionData *selection_data,
251 /* tree_model signals */
252 static void pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
254 GtkAdjustment *vadj);
255 static gboolean pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
256 GtkMovementStep step,
258 static gboolean pspp_sheet_view_real_select_all (PsppSheetView *tree_view);
259 static gboolean pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view);
260 static gboolean pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
261 gboolean start_editing,
262 PsppSheetSelectMode mode);
263 static gboolean pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view);
264 static void pspp_sheet_view_row_changed (GtkTreeModel *model,
268 static void pspp_sheet_view_row_inserted (GtkTreeModel *model,
272 static void pspp_sheet_view_row_deleted (GtkTreeModel *model,
275 static void pspp_sheet_view_rows_reordered (GtkTreeModel *model,
281 /* Incremental reflow */
282 static gint validate_row (PsppSheetView *tree_view,
286 static void validate_visible_area (PsppSheetView *tree_view);
287 static gboolean validate_rows_handler (PsppSheetView *tree_view);
288 static gboolean presize_handler_callback (gpointer data);
289 static void install_presize_handler (PsppSheetView *tree_view);
290 static void install_scroll_sync_handler (PsppSheetView *tree_view);
291 static void pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
294 static void pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view);
295 static void pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view);
296 static void invalidate_empty_focus (PsppSheetView *tree_view);
298 /* Internal functions */
299 static GtkAdjustment *pspp_sheet_view_do_get_hadjustment (PsppSheetView *);
300 static GtkAdjustment *pspp_sheet_view_do_get_vadjustment (PsppSheetView *);
301 static void pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
302 GtkAdjustment *adjustment);
303 static void pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
304 GtkAdjustment *adjustment);
305 static void pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
308 gboolean add_shifted_binding,
309 GtkMovementStep step,
311 static void pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
313 const GdkRectangle *clip_rect);
314 static gint pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
317 static void pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
318 PsppSheetView *tree_view);
319 static void pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
321 static void pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
322 PsppSheetViewColumn *column,
323 gboolean focus_to_cell);
324 static gboolean pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
325 GdkEventMotion *event);
326 static void pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view);
327 static gboolean pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
329 PsppSheetSelectMode mode);
330 static void pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
332 PsppSheetSelectMode mode);
333 static void pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
335 PsppSheetSelectMode mode);
336 static void pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
338 PsppSheetSelectMode mode);
339 static void pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
341 static void pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
343 PsppSheetSelectMode mode);
344 static void pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
346 gboolean clear_and_select,
348 PsppSheetSelectMode mode);
349 static gboolean pspp_sheet_view_has_special_cell (PsppSheetView *tree_view);
350 static void pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view);
351 static void update_prelight (PsppSheetView *tree_view,
354 static void initialize_fixed_height_mode (PsppSheetView *tree_view);
356 /* interactive search */
357 static void pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view);
358 static void pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
359 PsppSheetView *tree_view);
360 static void pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
361 GtkWidget *search_dialog,
363 static void pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
367 static void pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
368 PsppSheetView *tree_view);
370 static void pspp_sheet_view_search_activate (GtkEntry *entry,
371 PsppSheetView *tree_view);
372 static gboolean pspp_sheet_view_real_search_enable_popdown(gpointer data);
373 static void pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
375 static gboolean pspp_sheet_view_search_delete_event (GtkWidget *widget,
377 PsppSheetView *tree_view);
378 static gboolean pspp_sheet_view_search_button_press_event (GtkWidget *widget,
379 GdkEventButton *event,
380 PsppSheetView *tree_view);
381 static gboolean pspp_sheet_view_search_scroll_event (GtkWidget *entry,
382 GdkEventScroll *event,
383 PsppSheetView *tree_view);
384 static gboolean pspp_sheet_view_search_key_press_event (GtkWidget *entry,
386 PsppSheetView *tree_view);
387 static gboolean pspp_sheet_view_search_move (GtkWidget *window,
388 PsppSheetView *tree_view,
390 static gboolean pspp_sheet_view_search_equal_func (GtkTreeModel *model,
394 gpointer search_data);
395 static gboolean pspp_sheet_view_search_iter (GtkTreeModel *model,
396 PsppSheetSelection *selection,
401 static void pspp_sheet_view_search_init (GtkWidget *entry,
402 PsppSheetView *tree_view);
403 static void pspp_sheet_view_put (PsppSheetView *tree_view,
404 GtkWidget *child_widget,
409 static gboolean pspp_sheet_view_start_editing (PsppSheetView *tree_view,
410 GtkTreePath *cursor_path);
411 static gboolean pspp_sheet_view_editable_button_press_event (GtkWidget *,
414 static void pspp_sheet_view_editable_clicked (GtkButton *, PsppSheetView *);
415 static void pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
416 PsppSheetViewColumn *column,
418 GtkCellEditable *cell_editable,
419 GdkRectangle *cell_area,
422 static gboolean pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
423 gboolean keybinding);
424 static gboolean pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view);
425 static PsppSheetViewColumn *pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
426 PsppSheetViewColumn *column,
429 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
430 PsppSheetViewColumn *column,
431 const GdkRectangle *background_area,
432 gboolean subtract_focus_rect,
433 GdkRectangle *cell_area);
434 static gint pspp_sheet_view_find_offset (PsppSheetView *tree_view,
439 static void pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
443 static void pspp_sheet_view_buildable_init (GtkBuildableIface *iface);
446 static gboolean scroll_row_timeout (gpointer data);
447 static void add_scroll_timeout (PsppSheetView *tree_view);
448 static void remove_scroll_timeout (PsppSheetView *tree_view);
450 static guint tree_view_signals [LAST_SIGNAL] = { 0 };
452 static GtkBindingSet *edit_bindings;
459 G_DEFINE_TYPE_WITH_CODE (PsppSheetView, pspp_sheet_view, GTK_TYPE_CONTAINER,
460 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
461 pspp_sheet_view_buildable_init)
462 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
465 pspp_sheet_view_get_preferred_width (GtkWidget *widget,
469 GtkRequisition requisition;
471 pspp_sheet_view_size_request (widget, &requisition);
473 *minimal_width = *natural_width = requisition.width;
477 pspp_sheet_view_get_preferred_height (GtkWidget *widget,
478 gint *minimal_height,
479 gint *natural_height)
481 GtkRequisition requisition;
483 pspp_sheet_view_size_request (widget, &requisition);
485 *minimal_height = *natural_height = requisition.height;
489 pspp_sheet_view_class_init (PsppSheetViewClass *class)
491 GObjectClass *o_class;
492 GtkWidgetClass *widget_class;
493 GtkContainerClass *container_class;
494 GtkBindingSet *binding_set[2];
497 binding_set[0] = gtk_binding_set_by_class (class);
499 binding_set[1] = gtk_binding_set_new ("PsppSheetViewEditing");
500 edit_bindings = binding_set[1];
502 o_class = (GObjectClass *) class;
503 widget_class = (GtkWidgetClass *) class;
504 container_class = (GtkContainerClass *) class;
506 /* GObject signals */
507 o_class->set_property = pspp_sheet_view_set_property;
508 o_class->get_property = pspp_sheet_view_get_property;
509 o_class->finalize = pspp_sheet_view_finalize;
510 o_class->dispose = pspp_sheet_view_dispose;
512 /* GtkWidget signals */
513 widget_class->map = pspp_sheet_view_map;
514 widget_class->realize = pspp_sheet_view_realize;
515 widget_class->unrealize = pspp_sheet_view_unrealize;
516 widget_class->get_preferred_width = pspp_sheet_view_get_preferred_width;
517 widget_class->get_preferred_height = pspp_sheet_view_get_preferred_height;
518 widget_class->size_allocate = pspp_sheet_view_size_allocate;
519 widget_class->button_press_event = pspp_sheet_view_button_press;
520 widget_class->button_release_event = pspp_sheet_view_button_release;
521 widget_class->grab_broken_event = pspp_sheet_view_grab_broken;
522 /*widget_class->configure_event = pspp_sheet_view_configure;*/
523 widget_class->motion_notify_event = pspp_sheet_view_motion;
524 widget_class->draw = pspp_sheet_view_draw;
525 widget_class->key_press_event = pspp_sheet_view_key_press;
526 widget_class->key_release_event = pspp_sheet_view_key_release;
527 widget_class->enter_notify_event = pspp_sheet_view_enter_notify;
528 widget_class->leave_notify_event = pspp_sheet_view_leave_notify;
529 widget_class->focus_out_event = pspp_sheet_view_focus_out;
530 widget_class->drag_begin = pspp_sheet_view_drag_begin;
531 widget_class->drag_end = pspp_sheet_view_drag_end;
532 widget_class->drag_data_get = pspp_sheet_view_drag_data_get;
533 widget_class->drag_data_delete = pspp_sheet_view_drag_data_delete;
534 widget_class->drag_leave = pspp_sheet_view_drag_leave;
535 widget_class->drag_motion = pspp_sheet_view_drag_motion;
536 widget_class->drag_drop = pspp_sheet_view_drag_drop;
537 widget_class->drag_data_received = pspp_sheet_view_drag_data_received;
538 widget_class->focus = pspp_sheet_view_focus;
539 widget_class->grab_focus = pspp_sheet_view_grab_focus;
540 widget_class->style_updated = pspp_sheet_view_style_updated;
541 widget_class->grab_notify = pspp_sheet_view_grab_notify;
542 widget_class->state_changed = pspp_sheet_view_state_changed;
544 /* GtkContainer signals */
545 container_class->remove = pspp_sheet_view_remove;
546 container_class->forall = pspp_sheet_view_forall;
547 container_class->set_focus_child = pspp_sheet_view_set_focus_child;
549 class->set_scroll_adjustments = pspp_sheet_view_set_adjustments;
550 class->move_cursor = pspp_sheet_view_real_move_cursor;
551 class->select_all = pspp_sheet_view_real_select_all;
552 class->unselect_all = pspp_sheet_view_real_unselect_all;
553 class->select_cursor_row = pspp_sheet_view_real_select_cursor_row;
554 class->toggle_cursor_row = pspp_sheet_view_real_toggle_cursor_row;
555 class->start_interactive_search = pspp_sheet_view_start_interactive_search;
559 g_object_class_install_property (o_class,
561 g_param_spec_object ("model",
562 P_("TreeView Model"),
563 P_("The model for the tree view"),
565 GTK_PARAM_READWRITE));
567 g_object_class_override_property (o_class, PROP_HADJUSTMENT, "hadjustment");
568 g_object_class_override_property (o_class, PROP_VADJUSTMENT, "vadjustment");
569 g_object_class_override_property (o_class, PROP_HSCROLL_POLICY, "hscroll-policy");
570 g_object_class_override_property (o_class, PROP_VSCROLL_POLICY, "vscroll-policy");
572 g_object_class_install_property (o_class,
573 PROP_HEADERS_VISIBLE,
574 g_param_spec_boolean ("headers-visible",
575 P_("Headers Visible"),
576 P_("Show the column header buttons"),
578 GTK_PARAM_READWRITE));
580 g_object_class_install_property (o_class,
581 PROP_HEADERS_CLICKABLE,
582 g_param_spec_boolean ("headers-clickable",
583 P_("Headers Clickable"),
584 P_("Column headers respond to click events"),
586 GTK_PARAM_READWRITE));
588 g_object_class_install_property (o_class,
590 g_param_spec_boolean ("reorderable",
592 P_("View is reorderable"),
594 GTK_PARAM_READWRITE));
596 g_object_class_install_property (o_class,
598 g_param_spec_boolean ("rules-hint",
600 P_("Set a hint to the theme engine to draw rows in alternating colors"),
602 GTK_PARAM_READWRITE));
604 g_object_class_install_property (o_class,
606 g_param_spec_boolean ("enable-search",
608 P_("View allows user to search through columns interactively"),
610 GTK_PARAM_READWRITE));
612 g_object_class_install_property (o_class,
614 g_param_spec_int ("search-column",
616 P_("Model column to search through during interactive search"),
620 GTK_PARAM_READWRITE));
623 * PsppSheetView:hover-selection:
625 * Enables of disables the hover selection mode of @tree_view.
626 * Hover selection makes the selected row follow the pointer.
627 * Currently, this works only for the selection modes
628 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
630 * This mode is primarily intended for treeviews in popups, e.g.
631 * in #GtkComboBox or #GtkEntryCompletion.
635 g_object_class_install_property (o_class,
636 PROP_HOVER_SELECTION,
637 g_param_spec_boolean ("hover-selection",
638 P_("Hover Selection"),
639 P_("Whether the selection should follow the pointer"),
641 GTK_PARAM_READWRITE));
643 g_object_class_install_property (o_class,
645 g_param_spec_boolean ("rubber-banding",
646 P_("Rubber Banding"),
647 P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
649 GTK_PARAM_READWRITE));
651 g_object_class_install_property (o_class,
652 PROP_ENABLE_GRID_LINES,
653 g_param_spec_enum ("enable-grid-lines",
654 P_("Enable Grid Lines"),
655 P_("Whether grid lines should be drawn in the tree view"),
656 PSPP_TYPE_SHEET_VIEW_GRID_LINES,
657 PSPP_SHEET_VIEW_GRID_LINES_NONE,
658 GTK_PARAM_READWRITE));
660 g_object_class_install_property (o_class,
662 g_param_spec_int ("tooltip-column",
663 P_("Tooltip Column"),
664 P_("The column in the model containing the tooltip texts for the rows"),
668 GTK_PARAM_READWRITE));
670 g_object_class_install_property (o_class,
672 g_param_spec_enum ("special-cells",
674 P_("Whether rows have special cells."),
675 PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS,
676 PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT,
677 GTK_PARAM_READWRITE));
679 g_object_class_install_property (o_class,
681 g_param_spec_int ("fixed-height",
683 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."),
687 GTK_PARAM_READWRITE));
689 g_object_class_install_property (o_class,
690 PROP_FIXED_HEIGHT_SET,
691 g_param_spec_boolean ("fixed-height-set",
692 P_("Fixed Height Set"),
693 P_("Whether fixed-height was set externally."),
695 GTK_PARAM_READWRITE));
697 /* Style properties */
698 #define _TREE_VIEW_EXPANDER_SIZE 12
699 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
700 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
702 gtk_widget_class_install_style_property (widget_class,
703 g_param_spec_int ("expander-size",
705 P_("Size of the expander arrow"),
708 _TREE_VIEW_EXPANDER_SIZE,
709 GTK_PARAM_READABLE));
711 gtk_widget_class_install_style_property (widget_class,
712 g_param_spec_int ("vertical-separator",
713 P_("Vertical Separator Width"),
714 P_("Vertical space between cells. Must be an even number"),
717 _TREE_VIEW_VERTICAL_SEPARATOR,
718 GTK_PARAM_READABLE));
720 gtk_widget_class_install_style_property (widget_class,
721 g_param_spec_int ("horizontal-separator",
722 P_("Horizontal Separator Width"),
723 P_("Horizontal space between cells. Must be an even number"),
726 _TREE_VIEW_HORIZONTAL_SEPARATOR,
727 GTK_PARAM_READABLE));
729 gtk_widget_class_install_style_property (widget_class,
730 g_param_spec_boolean ("allow-rules",
732 P_("Allow drawing of alternating color rows"),
734 GTK_PARAM_READABLE));
736 gtk_widget_class_install_style_property (widget_class,
737 g_param_spec_boxed ("even-row-color",
738 P_("Even Row Color"),
739 P_("Color to use for even rows"),
741 GTK_PARAM_READABLE));
743 gtk_widget_class_install_style_property (widget_class,
744 g_param_spec_boxed ("odd-row-color",
746 P_("Color to use for odd rows"),
748 GTK_PARAM_READABLE));
750 gtk_widget_class_install_style_property (widget_class,
751 g_param_spec_boolean ("row-ending-details",
752 P_("Row Ending details"),
753 P_("Enable extended row background theming"),
755 GTK_PARAM_READABLE));
757 gtk_widget_class_install_style_property (widget_class,
758 g_param_spec_int ("grid-line-width",
759 P_("Grid line width"),
760 P_("Width, in pixels, of the tree view grid lines"),
762 GTK_PARAM_READABLE));
764 gtk_widget_class_install_style_property (widget_class,
765 g_param_spec_int ("tree-line-width",
766 P_("Tree line width"),
767 P_("Width, in pixels, of the tree view lines"),
769 GTK_PARAM_READABLE));
771 gtk_widget_class_install_style_property (widget_class,
772 g_param_spec_string ("tree-line-pattern",
773 P_("Tree line pattern"),
774 P_("Dash pattern used to draw the tree view lines"),
776 GTK_PARAM_READABLE));
781 * PsppSheetView::set-scroll-adjustments
782 * @horizontal: the horizontal #GtkAdjustment
783 * @vertical: the vertical #GtkAdjustment
785 * Set the scroll adjustments for the tree view. Usually scrolled containers
786 * like #GtkScrolledWindow will emit this signal to connect two instances
787 * of #GtkScrollbar to the scroll directions of the #PsppSheetView.
789 widget_class->set_scroll_adjustments_signal =
790 g_signal_new ("set-scroll-adjustments",
791 G_TYPE_FROM_CLASS (o_class),
792 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
793 G_STRUCT_OFFSET (PsppSheetViewClass, set_scroll_adjustments),
795 psppire_marshal_VOID__OBJECT_OBJECT,
798 GTK_TYPE_ADJUSTMENT);
802 * PsppSheetView::row-activated:
803 * @tree_view: the object on which the signal is emitted
804 * @path: the #GtkTreePath for the activated row
805 * @column: the #PsppSheetViewColumn in which the activation occurred
807 * The "row-activated" signal is emitted when the method
808 * pspp_sheet_view_row_activated() is called or the user double clicks
809 * a treeview row. It is also emitted when a non-editable row is
810 * selected and one of the keys: Space, Shift+Space, Return or
813 * For selection handling refer to the <link linkend="TreeWidget">tree
814 * widget conceptual overview</link> as well as #PsppSheetSelection.
816 tree_view_signals[ROW_ACTIVATED] =
817 g_signal_new ("row-activated",
818 G_TYPE_FROM_CLASS (o_class),
819 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
820 G_STRUCT_OFFSET (PsppSheetViewClass, row_activated),
822 psppire_marshal_VOID__BOXED_OBJECT,
825 PSPP_TYPE_SHEET_VIEW_COLUMN);
828 * PsppSheetView::columns-changed:
829 * @tree_view: the object on which the signal is emitted
831 * The number of columns of the treeview has changed.
833 tree_view_signals[COLUMNS_CHANGED] =
834 g_signal_new ("columns-changed",
835 G_TYPE_FROM_CLASS (o_class),
837 G_STRUCT_OFFSET (PsppSheetViewClass, columns_changed),
839 g_cclosure_marshal_VOID__VOID,
843 * PsppSheetView::cursor-changed:
844 * @tree_view: the object on which the signal is emitted
846 * The position of the cursor (focused cell) has changed.
848 tree_view_signals[CURSOR_CHANGED] =
849 g_signal_new ("cursor-changed",
850 G_TYPE_FROM_CLASS (o_class),
852 G_STRUCT_OFFSET (PsppSheetViewClass, cursor_changed),
854 g_cclosure_marshal_VOID__VOID,
857 tree_view_signals[MOVE_CURSOR] =
858 g_signal_new ("move-cursor",
859 G_TYPE_FROM_CLASS (o_class),
860 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
861 G_STRUCT_OFFSET (PsppSheetViewClass, move_cursor),
863 psppire_marshal_BOOLEAN__ENUM_INT,
865 GTK_TYPE_MOVEMENT_STEP,
868 tree_view_signals[SELECT_ALL] =
869 g_signal_new ("select-all",
870 G_TYPE_FROM_CLASS (o_class),
871 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
872 G_STRUCT_OFFSET (PsppSheetViewClass, select_all),
874 psppire_marshal_BOOLEAN__VOID,
877 tree_view_signals[UNSELECT_ALL] =
878 g_signal_new ("unselect-all",
879 G_TYPE_FROM_CLASS (o_class),
880 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
881 G_STRUCT_OFFSET (PsppSheetViewClass, unselect_all),
883 psppire_marshal_BOOLEAN__VOID,
886 tree_view_signals[SELECT_CURSOR_ROW] =
887 g_signal_new ("select-cursor-row",
888 G_TYPE_FROM_CLASS (o_class),
889 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
890 G_STRUCT_OFFSET (PsppSheetViewClass, select_cursor_row),
892 psppire_marshal_BOOLEAN__BOOLEAN,
894 G_TYPE_BOOLEAN, G_TYPE_INT);
896 tree_view_signals[TOGGLE_CURSOR_ROW] =
897 g_signal_new ("toggle-cursor-row",
898 G_TYPE_FROM_CLASS (o_class),
899 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
900 G_STRUCT_OFFSET (PsppSheetViewClass, toggle_cursor_row),
902 psppire_marshal_BOOLEAN__VOID,
905 tree_view_signals[START_INTERACTIVE_SEARCH] =
906 g_signal_new ("start-interactive-search",
907 G_TYPE_FROM_CLASS (o_class),
908 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
909 G_STRUCT_OFFSET (PsppSheetViewClass, start_interactive_search),
911 psppire_marshal_BOOLEAN__VOID,
915 for (i = 0; i < 2; i++)
917 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Up, 0, TRUE,
918 GTK_MOVEMENT_DISPLAY_LINES, -1);
919 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Up, 0, TRUE,
920 GTK_MOVEMENT_DISPLAY_LINES, -1);
922 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Down, 0, TRUE,
923 GTK_MOVEMENT_DISPLAY_LINES, 1);
924 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Down, 0, TRUE,
925 GTK_MOVEMENT_DISPLAY_LINES, 1);
927 pspp_sheet_view_add_move_binding (binding_set[i], GDK_p, GDK_CONTROL_MASK, FALSE,
928 GTK_MOVEMENT_DISPLAY_LINES, -1);
930 pspp_sheet_view_add_move_binding (binding_set[i], GDK_n, GDK_CONTROL_MASK, FALSE,
931 GTK_MOVEMENT_DISPLAY_LINES, 1);
933 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Home, 0, TRUE,
934 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
935 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Home, 0, TRUE,
936 GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
938 pspp_sheet_view_add_move_binding (binding_set[i], GDK_End, 0, TRUE,
939 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
940 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_End, 0, TRUE,
941 GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
943 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Up, 0, TRUE,
944 GTK_MOVEMENT_PAGES, -1);
945 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Up, 0, TRUE,
946 GTK_MOVEMENT_PAGES, -1);
948 pspp_sheet_view_add_move_binding (binding_set[i], GDK_Page_Down, 0, TRUE,
949 GTK_MOVEMENT_PAGES, 1);
950 pspp_sheet_view_add_move_binding (binding_set[i], GDK_KP_Page_Down, 0, TRUE,
951 GTK_MOVEMENT_PAGES, 1);
954 gtk_binding_entry_add_signal (binding_set[i], GDK_Up, GDK_CONTROL_MASK, "move-cursor", 2,
955 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
958 gtk_binding_entry_add_signal (binding_set[i], GDK_Down, GDK_CONTROL_MASK, "move-cursor", 2,
959 G_TYPE_ENUM, GTK_MOVEMENT_BUFFER_ENDS,
962 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, 0, "move-cursor", 2,
963 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
966 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, 0, "move-cursor", 2,
967 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
970 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, 0, "move-cursor", 2,
971 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
974 gtk_binding_entry_add_signal (binding_set[i], GDK_Tab, GDK_SHIFT_MASK, "move-cursor", 2,
975 G_TYPE_ENUM, GTK_MOVEMENT_LOGICAL_POSITIONS,
978 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, 0, "move-cursor", 2,
979 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
982 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, 0, "move-cursor", 2,
983 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
986 gtk_binding_entry_add_signal (binding_set[i], GDK_Right, GDK_CONTROL_MASK,
988 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
991 gtk_binding_entry_add_signal (binding_set[i], GDK_Left, GDK_CONTROL_MASK,
993 G_TYPE_ENUM, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
996 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Right, GDK_CONTROL_MASK,
998 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1001 gtk_binding_entry_add_signal (binding_set[i], GDK_KP_Left, GDK_CONTROL_MASK,
1003 G_TYPE_ENUM, GTK_MOVEMENT_VISUAL_POSITIONS,
1006 gtk_binding_entry_add_signal (binding_set[i], GDK_f, GDK_CONTROL_MASK, "start-interactive-search", 0);
1008 gtk_binding_entry_add_signal (binding_set[i], GDK_F, GDK_CONTROL_MASK, "start-interactive-search", 0);
1011 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1012 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_CONTROL_MASK, "toggle-cursor-row", 0);
1014 gtk_binding_entry_add_signal (binding_set[0], GDK_a, GDK_CONTROL_MASK, "select-all", 0);
1015 gtk_binding_entry_add_signal (binding_set[0], GDK_slash, GDK_CONTROL_MASK, "select-all", 0);
1017 gtk_binding_entry_add_signal (binding_set[0], GDK_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "unselect-all", 0);
1018 gtk_binding_entry_add_signal (binding_set[0], GDK_backslash, GDK_CONTROL_MASK, "unselect-all", 0);
1020 gtk_binding_entry_add_signal (binding_set[0], GDK_space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1021 G_TYPE_BOOLEAN, TRUE,
1022 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1023 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, GDK_SHIFT_MASK, "select-cursor-row", 1,
1024 G_TYPE_BOOLEAN, TRUE,
1025 G_TYPE_INT, PSPP_SHEET_SELECT_MODE_EXTEND);
1027 gtk_binding_entry_add_signal (binding_set[0], GDK_space, 0, "select-cursor-row", 1,
1028 G_TYPE_BOOLEAN, TRUE,
1030 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Space, 0, "select-cursor-row", 1,
1031 G_TYPE_BOOLEAN, TRUE,
1033 gtk_binding_entry_add_signal (binding_set[0], GDK_Return, 0, "select-cursor-row", 1,
1034 G_TYPE_BOOLEAN, TRUE,
1036 gtk_binding_entry_add_signal (binding_set[0], GDK_ISO_Enter, 0, "select-cursor-row", 1,
1037 G_TYPE_BOOLEAN, TRUE,
1039 gtk_binding_entry_add_signal (binding_set[0], GDK_KP_Enter, 0, "select-cursor-row", 1,
1040 G_TYPE_BOOLEAN, TRUE,
1043 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, 0, "select-cursor-parent", 0);
1044 gtk_binding_entry_add_signal (binding_set[0], GDK_BackSpace, GDK_CONTROL_MASK, "select-cursor-parent", 0);
1046 g_type_class_add_private (o_class, sizeof (PsppSheetViewPrivate));
1050 pspp_sheet_view_buildable_init (GtkBuildableIface *iface)
1052 iface->add_child = pspp_sheet_view_buildable_add_child;
1056 pspp_sheet_view_init (PsppSheetView *tree_view)
1058 tree_view->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree_view, PSPP_TYPE_SHEET_VIEW, PsppSheetViewPrivate);
1060 gtk_widget_set_can_focus (GTK_WIDGET (tree_view), TRUE);
1061 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view), FALSE);
1063 tree_view->priv->flags = PSPP_SHEET_VIEW_DRAW_KEYFOCUS
1064 | PSPP_SHEET_VIEW_HEADERS_VISIBLE;
1066 /* We need some padding */
1067 tree_view->priv->selected = range_tower_create ();
1068 tree_view->priv->dy = 0;
1069 tree_view->priv->cursor_offset = 0;
1070 tree_view->priv->n_columns = 0;
1071 tree_view->priv->header_height = 1;
1072 tree_view->priv->x_drag = 0;
1073 tree_view->priv->drag_pos = -1;
1074 tree_view->priv->header_has_focus = FALSE;
1075 tree_view->priv->pressed_button = -1;
1076 tree_view->priv->press_start_x = -1;
1077 tree_view->priv->press_start_y = -1;
1078 tree_view->priv->reorderable = FALSE;
1079 tree_view->priv->presize_handler_timer = 0;
1080 tree_view->priv->scroll_sync_timer = 0;
1081 tree_view->priv->fixed_height = -1;
1082 tree_view->priv->fixed_height_set = FALSE;
1083 pspp_sheet_view_set_adjustments (tree_view, NULL, NULL);
1084 tree_view->priv->selection = _pspp_sheet_selection_new_with_tree_view (tree_view);
1085 tree_view->priv->enable_search = TRUE;
1086 tree_view->priv->search_column = -1;
1087 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
1088 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
1089 tree_view->priv->search_custom_entry_set = FALSE;
1090 tree_view->priv->typeselect_flush_timeout = 0;
1091 tree_view->priv->init_hadjust_value = TRUE;
1092 tree_view->priv->width = 0;
1094 tree_view->priv->hover_selection = FALSE;
1096 tree_view->priv->rubber_banding_enable = FALSE;
1098 tree_view->priv->grid_lines = PSPP_SHEET_VIEW_GRID_LINES_NONE;
1100 tree_view->priv->tooltip_column = -1;
1102 tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT;
1104 tree_view->priv->post_validation_flag = FALSE;
1106 tree_view->priv->last_button_x = -1;
1107 tree_view->priv->last_button_y = -1;
1109 tree_view->priv->event_last_x = -10000;
1110 tree_view->priv->event_last_y = -10000;
1112 tree_view->priv->prelight_node = -1;
1113 tree_view->priv->rubber_band_start_node = -1;
1114 tree_view->priv->rubber_band_end_node = -1;
1116 tree_view->priv->anchor_column = NULL;
1118 tree_view->priv->button_style = NULL;
1120 tree_view->dispose_has_run = FALSE;
1122 pspp_sheet_view_do_set_vadjustment (tree_view, NULL);
1123 pspp_sheet_view_do_set_hadjustment (tree_view, NULL);
1124 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (tree_view)),
1125 GTK_STYLE_CLASS_VIEW);
1134 pspp_sheet_view_set_property (GObject *object,
1136 const GValue *value,
1139 PsppSheetView *tree_view;
1141 tree_view = PSPP_SHEET_VIEW (object);
1146 pspp_sheet_view_set_model (tree_view, g_value_get_object (value));
1148 case PROP_HADJUSTMENT:
1149 pspp_sheet_view_do_set_hadjustment (tree_view, g_value_get_object (value));
1151 case PROP_VADJUSTMENT:
1152 pspp_sheet_view_do_set_vadjustment (tree_view, g_value_get_object (value));
1154 case PROP_HSCROLL_POLICY:
1155 tree_view->priv->hscroll_policy = g_value_get_enum (value);
1156 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1158 case PROP_VSCROLL_POLICY:
1159 tree_view->priv->vscroll_policy = g_value_get_enum (value);
1160 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
1162 case PROP_HEADERS_VISIBLE:
1163 pspp_sheet_view_set_headers_visible (tree_view, g_value_get_boolean (value));
1165 case PROP_HEADERS_CLICKABLE:
1166 pspp_sheet_view_set_headers_clickable (tree_view, g_value_get_boolean (value));
1168 case PROP_REORDERABLE:
1169 pspp_sheet_view_set_reorderable (tree_view, g_value_get_boolean (value));
1171 case PROP_RULES_HINT:
1172 pspp_sheet_view_set_rules_hint (tree_view, g_value_get_boolean (value));
1174 case PROP_ENABLE_SEARCH:
1175 pspp_sheet_view_set_enable_search (tree_view, g_value_get_boolean (value));
1177 case PROP_SEARCH_COLUMN:
1178 pspp_sheet_view_set_search_column (tree_view, g_value_get_int (value));
1180 case PROP_HOVER_SELECTION:
1181 tree_view->priv->hover_selection = g_value_get_boolean (value);
1183 case PROP_RUBBER_BANDING:
1184 tree_view->priv->rubber_banding_enable = g_value_get_boolean (value);
1186 case PROP_ENABLE_GRID_LINES:
1187 pspp_sheet_view_set_grid_lines (tree_view, g_value_get_enum (value));
1189 case PROP_TOOLTIP_COLUMN:
1190 pspp_sheet_view_set_tooltip_column (tree_view, g_value_get_int (value));
1192 case PROP_SPECIAL_CELLS:
1193 pspp_sheet_view_set_special_cells (tree_view, g_value_get_enum (value));
1195 case PROP_FIXED_HEIGHT:
1196 pspp_sheet_view_set_fixed_height (tree_view, g_value_get_int (value));
1198 case PROP_FIXED_HEIGHT_SET:
1199 if (g_value_get_boolean (value))
1201 if (!tree_view->priv->fixed_height_set
1202 && tree_view->priv->fixed_height >= 0)
1204 tree_view->priv->fixed_height_set = true;
1205 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1210 if (tree_view->priv->fixed_height_set)
1212 tree_view->priv->fixed_height_set = false;
1213 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
1214 install_presize_handler (tree_view);
1219 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1225 pspp_sheet_view_get_property (GObject *object,
1230 PsppSheetView *tree_view;
1232 tree_view = PSPP_SHEET_VIEW (object);
1237 g_value_set_object (value, tree_view->priv->model);
1239 case PROP_HADJUSTMENT:
1240 g_value_set_object (value, tree_view->priv->hadjustment);
1242 case PROP_VADJUSTMENT:
1243 g_value_set_object (value, tree_view->priv->vadjustment);
1245 case PROP_HSCROLL_POLICY:
1246 g_value_set_enum (value, tree_view->priv->hscroll_policy);
1248 case PROP_VSCROLL_POLICY:
1249 g_value_set_enum (value, tree_view->priv->vscroll_policy);
1251 case PROP_HEADERS_VISIBLE:
1252 g_value_set_boolean (value, pspp_sheet_view_get_headers_visible (tree_view));
1254 case PROP_HEADERS_CLICKABLE:
1255 g_value_set_boolean (value, pspp_sheet_view_get_headers_clickable (tree_view));
1257 case PROP_REORDERABLE:
1258 g_value_set_boolean (value, tree_view->priv->reorderable);
1260 case PROP_RULES_HINT:
1261 g_value_set_boolean (value, tree_view->priv->has_rules);
1263 case PROP_ENABLE_SEARCH:
1264 g_value_set_boolean (value, tree_view->priv->enable_search);
1266 case PROP_SEARCH_COLUMN:
1267 g_value_set_int (value, tree_view->priv->search_column);
1269 case PROP_HOVER_SELECTION:
1270 g_value_set_boolean (value, tree_view->priv->hover_selection);
1272 case PROP_RUBBER_BANDING:
1273 g_value_set_boolean (value, tree_view->priv->rubber_banding_enable);
1275 case PROP_ENABLE_GRID_LINES:
1276 g_value_set_enum (value, tree_view->priv->grid_lines);
1278 case PROP_TOOLTIP_COLUMN:
1279 g_value_set_int (value, tree_view->priv->tooltip_column);
1281 case PROP_SPECIAL_CELLS:
1282 g_value_set_enum (value, tree_view->priv->special_cells);
1284 case PROP_FIXED_HEIGHT:
1285 g_value_set_int (value, pspp_sheet_view_get_fixed_height (tree_view));
1287 case PROP_FIXED_HEIGHT_SET:
1288 g_value_set_boolean (value, tree_view->priv->fixed_height_set);
1291 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1297 pspp_sheet_view_dispose (GObject *object)
1299 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1301 if (tree_view->dispose_has_run)
1304 tree_view->dispose_has_run = TRUE;
1306 if (tree_view->priv->selection != NULL)
1308 _pspp_sheet_selection_set_tree_view (tree_view->priv->selection, NULL);
1309 g_object_unref (tree_view->priv->selection);
1310 tree_view->priv->selection = NULL;
1313 if (tree_view->priv->hadjustment)
1315 g_object_unref (tree_view->priv->hadjustment);
1316 tree_view->priv->hadjustment = NULL;
1318 if (tree_view->priv->vadjustment)
1320 g_object_unref (tree_view->priv->vadjustment);
1321 tree_view->priv->vadjustment = NULL;
1324 if (tree_view->priv->button_style)
1326 g_object_unref (tree_view->priv->button_style);
1327 tree_view->priv->button_style = NULL;
1331 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->dispose (object);
1337 pspp_sheet_view_buildable_add_child (GtkBuildable *tree_view,
1338 GtkBuilder *builder,
1342 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view), PSPP_SHEET_VIEW_COLUMN (child));
1346 pspp_sheet_view_finalize (GObject *object)
1348 PsppSheetView *tree_view = PSPP_SHEET_VIEW (object);
1350 pspp_sheet_view_stop_editing (tree_view, TRUE);
1352 if (tree_view->priv->selected != NULL)
1354 range_tower_destroy (tree_view->priv->selected);
1355 tree_view->priv->selected = NULL;
1359 tree_view->priv->prelight_node = -1;
1362 if (tree_view->priv->scroll_to_path != NULL)
1364 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
1365 tree_view->priv->scroll_to_path = NULL;
1368 if (tree_view->priv->drag_dest_row != NULL)
1370 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
1371 tree_view->priv->drag_dest_row = NULL;
1374 if (tree_view->priv->top_row != NULL)
1376 gtk_tree_row_reference_free (tree_view->priv->top_row);
1377 tree_view->priv->top_row = NULL;
1380 if (tree_view->priv->column_drop_func_data &&
1381 tree_view->priv->column_drop_func_data_destroy)
1383 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
1384 tree_view->priv->column_drop_func_data = NULL;
1387 if (tree_view->priv->destroy_count_destroy &&
1388 tree_view->priv->destroy_count_data)
1390 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
1391 tree_view->priv->destroy_count_data = NULL;
1394 gtk_tree_row_reference_free (tree_view->priv->cursor);
1395 tree_view->priv->cursor = NULL;
1397 gtk_tree_row_reference_free (tree_view->priv->anchor);
1398 tree_view->priv->anchor = NULL;
1400 /* destroy interactive search dialog */
1401 if (tree_view->priv->search_window)
1403 gtk_widget_destroy (tree_view->priv->search_window);
1404 tree_view->priv->search_window = NULL;
1405 tree_view->priv->search_entry = NULL;
1406 if (tree_view->priv->typeselect_flush_timeout)
1408 g_source_remove (tree_view->priv->typeselect_flush_timeout);
1409 tree_view->priv->typeselect_flush_timeout = 0;
1413 if (tree_view->priv->search_destroy && tree_view->priv->search_user_data)
1415 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
1416 tree_view->priv->search_user_data = NULL;
1419 if (tree_view->priv->search_position_destroy && tree_view->priv->search_position_user_data)
1421 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
1422 tree_view->priv->search_position_user_data = NULL;
1425 pspp_sheet_view_set_model (tree_view, NULL);
1428 G_OBJECT_CLASS (pspp_sheet_view_parent_class)->finalize (object);
1433 /* GtkWidget Methods
1436 /* GtkWidget::map helper */
1438 pspp_sheet_view_map_buttons (PsppSheetView *tree_view)
1442 g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view)));
1444 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
1446 PsppSheetViewColumn *column;
1448 for (list = tree_view->priv->columns; list; list = list->next)
1450 column = list->data;
1451 if (column->button != NULL &&
1452 gtk_widget_get_visible (column->button) &&
1453 !gtk_widget_get_mapped (column->button))
1454 gtk_widget_map (column->button);
1456 for (list = tree_view->priv->columns; list; list = list->next)
1458 column = list->data;
1459 if (column->visible == FALSE || column->window == NULL)
1461 if (column->resizable)
1463 gdk_window_raise (column->window);
1464 gdk_window_show (column->window);
1467 gdk_window_hide (column->window);
1469 gdk_window_show (tree_view->priv->header_window);
1474 pspp_sheet_view_map (GtkWidget *widget)
1476 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1479 gtk_widget_set_mapped (widget, TRUE);
1481 tmp_list = tree_view->priv->children;
1484 PsppSheetViewChild *child = tmp_list->data;
1485 tmp_list = tmp_list->next;
1487 if (gtk_widget_get_visible (child->widget))
1489 if (!gtk_widget_get_mapped (child->widget))
1490 gtk_widget_map (child->widget);
1493 gdk_window_show (tree_view->priv->bin_window);
1495 pspp_sheet_view_map_buttons (tree_view);
1497 gdk_window_show (gtk_widget_get_window (widget));
1501 pspp_sheet_view_realize (GtkWidget *widget)
1503 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1506 GdkWindowAttr attributes;
1507 gint attributes_mask;
1508 GtkAllocation allocation;
1510 gtk_widget_set_realized (widget, TRUE);
1512 gtk_widget_get_allocation (widget, &allocation);
1514 /* Make the main, clipping window */
1515 attributes.window_type = GDK_WINDOW_CHILD;
1516 attributes.x = allocation.x;
1517 attributes.y = allocation.y;
1518 attributes.width = allocation.width;
1519 attributes.height = allocation.height;
1520 attributes.wclass = GDK_INPUT_OUTPUT;
1521 attributes.visual = gtk_widget_get_visual (widget);
1522 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
1524 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
1526 window = gdk_window_new (gtk_widget_get_parent_window (widget),
1527 &attributes, attributes_mask);
1528 gtk_widget_set_window (widget, window);
1530 #if GTK_CHECK_VERSION(3,8,0)
1531 gtk_widget_register_window (widget, window);
1533 gdk_window_set_user_data (window, widget);
1535 gtk_widget_get_allocation (widget, &allocation);
1537 /* Make the window for the tree */
1539 attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
1540 attributes.width = MAX (tree_view->priv->width, allocation.width);
1541 attributes.height = allocation.height;
1542 attributes.event_mask = (GDK_EXPOSURE_MASK |
1544 GDK_POINTER_MOTION_MASK |
1545 GDK_ENTER_NOTIFY_MASK |
1546 GDK_LEAVE_NOTIFY_MASK |
1547 GDK_BUTTON_PRESS_MASK |
1548 GDK_BUTTON_RELEASE_MASK |
1549 gtk_widget_get_events (widget));
1551 tree_view->priv->bin_window = gdk_window_new (window,
1552 &attributes, attributes_mask);
1553 #if GTK_CHECK_VERSION(3,8,0)
1554 gtk_widget_register_window (widget, tree_view->priv->bin_window);
1556 gdk_window_set_user_data (tree_view->priv->bin_window, widget);
1558 gtk_widget_get_allocation (widget, &allocation);
1560 /* Make the column header window */
1563 attributes.width = MAX (tree_view->priv->width, allocation.width);
1564 attributes.height = tree_view->priv->header_height;
1565 attributes.event_mask = (GDK_EXPOSURE_MASK |
1567 GDK_BUTTON_PRESS_MASK |
1568 GDK_BUTTON_RELEASE_MASK |
1569 GDK_KEY_PRESS_MASK |
1570 GDK_KEY_RELEASE_MASK |
1571 gtk_widget_get_events (widget));
1573 tree_view->priv->header_window = gdk_window_new (window,
1574 &attributes, attributes_mask);
1575 #if GTK_CHECK_VERSION(3,8,0)
1576 gtk_widget_register_window (widget, tree_view->priv->header_window);
1578 gdk_window_set_user_data (tree_view->priv->header_window, widget);
1581 { /* Ensure Background */
1582 GtkStyleContext *context;
1584 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
1586 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
1587 gtk_style_context_set_background (context, tree_view->priv->header_window);
1590 tmp_list = tree_view->priv->children;
1593 PsppSheetViewChild *child = tmp_list->data;
1594 tmp_list = tmp_list->next;
1596 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
1599 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
1600 _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list->data));
1602 /* Need to call those here, since they create GCs */
1603 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
1605 install_presize_handler (tree_view);
1609 pspp_sheet_view_unrealize (GtkWidget *widget)
1611 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1612 PsppSheetViewPrivate *priv = tree_view->priv;
1615 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget);
1617 if (priv->scroll_timeout != 0)
1619 g_source_remove (priv->scroll_timeout);
1620 priv->scroll_timeout = 0;
1623 if (priv->open_dest_timeout != 0)
1625 g_source_remove (priv->open_dest_timeout);
1626 priv->open_dest_timeout = 0;
1629 if (priv->presize_handler_timer != 0)
1631 g_source_remove (priv->presize_handler_timer);
1632 priv->presize_handler_timer = 0;
1635 if (priv->validate_rows_timer != 0)
1637 g_source_remove (priv->validate_rows_timer);
1638 priv->validate_rows_timer = 0;
1641 if (priv->scroll_sync_timer != 0)
1643 g_source_remove (priv->scroll_sync_timer);
1644 priv->scroll_sync_timer = 0;
1647 if (priv->typeselect_flush_timeout)
1649 g_source_remove (priv->typeselect_flush_timeout);
1650 priv->typeselect_flush_timeout = 0;
1653 for (list = priv->columns; list; list = list->next)
1654 _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list->data));
1656 gdk_window_set_user_data (priv->bin_window, NULL);
1657 gdk_window_destroy (priv->bin_window);
1658 priv->bin_window = NULL;
1660 gdk_window_set_user_data (priv->header_window, NULL);
1661 gdk_window_destroy (priv->header_window);
1662 priv->header_window = NULL;
1664 if (priv->drag_window)
1666 gdk_window_set_user_data (priv->drag_window, NULL);
1667 gdk_window_destroy (priv->drag_window);
1668 priv->drag_window = NULL;
1671 if (priv->drag_highlight_window)
1673 gdk_window_set_user_data (priv->drag_highlight_window, NULL);
1674 gdk_window_destroy (priv->drag_highlight_window);
1675 priv->drag_highlight_window = NULL;
1678 if (tree_view->priv->columns != NULL)
1680 list = tree_view->priv->columns;
1683 PsppSheetViewColumn *column;
1684 column = PSPP_SHEET_VIEW_COLUMN (list->data);
1686 pspp_sheet_view_remove_column (tree_view, column);
1688 tree_view->priv->columns = NULL;
1692 /* GtkWidget::size_request helper */
1694 pspp_sheet_view_size_request_columns (PsppSheetView *tree_view)
1698 tree_view->priv->header_height = 0;
1700 if (tree_view->priv->model)
1702 for (list = tree_view->priv->columns; list; list = list->next)
1704 GtkRequisition requisition;
1705 PsppSheetViewColumn *column = list->data;
1707 pspp_sheet_view_column_size_request (column, &requisition);
1708 column->button_request = requisition.width;
1709 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
1715 /* Called only by ::size_request */
1717 pspp_sheet_view_update_size (PsppSheetView *tree_view)
1720 PsppSheetViewColumn *column;
1723 if (tree_view->priv->model == NULL)
1725 tree_view->priv->width = 0;
1726 tree_view->priv->prev_width = 0;
1727 tree_view->priv->height = 0;
1731 tree_view->priv->prev_width = tree_view->priv->width;
1732 tree_view->priv->width = 0;
1734 /* keep this in sync with size_allocate below */
1735 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
1737 gint real_requested_width = 0;
1738 column = list->data;
1739 if (!column->visible)
1742 if (column->use_resized_width)
1744 real_requested_width = column->resized_width;
1748 real_requested_width = column->fixed_width;
1751 if (column->min_width != -1)
1752 real_requested_width = MAX (real_requested_width, column->min_width);
1753 if (column->max_width != -1)
1754 real_requested_width = MIN (real_requested_width, column->max_width);
1756 tree_view->priv->width += real_requested_width;
1759 tree_view->priv->height = tree_view->priv->fixed_height * tree_view->priv->row_count;
1763 pspp_sheet_view_size_request (GtkWidget *widget,
1764 GtkRequisition *requisition)
1766 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1769 /* we validate some rows initially just to make sure we have some size.
1770 * In practice, with a lot of static lists, this should get a good width.
1772 initialize_fixed_height_mode (tree_view);
1773 pspp_sheet_view_size_request_columns (tree_view);
1774 pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget));
1776 requisition->width = tree_view->priv->width;
1777 requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
1779 tmp_list = tree_view->priv->children;
1783 PsppSheetViewChild *child = tmp_list->data;
1784 GtkRequisition child_requisition;
1786 tmp_list = tmp_list->next;
1788 if (gtk_widget_get_visible (child->widget))
1789 gtk_widget_size_request (child->widget, &child_requisition);
1794 invalidate_column (PsppSheetView *tree_view,
1795 PsppSheetViewColumn *column)
1797 gint column_offset = 0;
1799 GtkWidget *widget = GTK_WIDGET (tree_view);
1802 if (!gtk_widget_get_realized (widget))
1805 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1806 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
1808 list = (rtl ? list->prev : list->next))
1810 PsppSheetViewColumn *tmpcolumn = list->data;
1811 if (tmpcolumn == column)
1813 GdkRectangle invalid_rect;
1814 GtkAllocation allocation;
1816 gtk_widget_get_allocation (widget, &allocation);
1817 invalid_rect.x = column_offset;
1819 invalid_rect.width = column->width;
1820 invalid_rect.height = allocation.height;
1822 gdk_window_invalidate_rect (gtk_widget_get_window (widget), &invalid_rect, TRUE);
1826 column_offset += tmpcolumn->width;
1831 invalidate_last_column (PsppSheetView *tree_view)
1836 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1838 for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
1840 last_column = (rtl ? last_column->next : last_column->prev))
1842 if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible)
1844 invalidate_column (tree_view, last_column->data);
1851 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_view,
1852 PsppSheetViewColumn *column)
1854 gint real_requested_width;
1856 if (column->use_resized_width)
1858 real_requested_width = column->resized_width;
1862 real_requested_width = column->fixed_width;
1865 if (column->min_width != -1)
1866 real_requested_width = MAX (real_requested_width, column->min_width);
1867 if (column->max_width != -1)
1868 real_requested_width = MIN (real_requested_width, column->max_width);
1870 return real_requested_width;
1874 span_intersects (int a0, int a_width,
1875 int b0, int b_width)
1877 int a1 = a0 + a_width;
1878 int b1 = b0 + b_width;
1879 return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1);
1882 /* GtkWidget::size_allocate helper */
1884 pspp_sheet_view_size_allocate_columns (GtkWidget *widget,
1885 gboolean *width_changed)
1887 PsppSheetView *tree_view;
1888 GList *list, *first_column, *last_column;
1889 PsppSheetViewColumn *column;
1890 GtkAllocation col_allocation;
1891 GtkAllocation allocation;
1893 gint extra, extra_per_column;
1894 gint full_requested_width = 0;
1895 gint number_of_expand_columns = 0;
1896 gboolean column_changed = FALSE;
1899 tree_view = PSPP_SHEET_VIEW (widget);
1901 for (last_column = g_list_last (tree_view->priv->columns);
1902 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
1903 last_column = last_column->prev)
1906 if (last_column == NULL)
1909 for (first_column = g_list_first (tree_view->priv->columns);
1910 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
1911 first_column = first_column->next)
1914 col_allocation.y = 0;
1915 col_allocation.height = tree_view->priv->header_height;
1917 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1919 /* find out how many extra space and expandable columns we have */
1920 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
1922 column = (PsppSheetViewColumn *)list->data;
1924 if (!column->visible)
1927 full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1930 number_of_expand_columns++;
1933 gtk_widget_get_allocation (widget, &allocation);
1934 extra = MAX (allocation.width - full_requested_width, 0);
1935 if (number_of_expand_columns > 0)
1936 extra_per_column = extra/number_of_expand_columns;
1938 extra_per_column = 0;
1940 for (list = (rtl ? last_column : first_column);
1941 list != (rtl ? first_column->prev : last_column->next);
1942 list = (rtl ? list->prev : list->next))
1944 gint real_requested_width = 0;
1947 column = list->data;
1948 old_width = column->width;
1950 if (!column->visible)
1953 /* We need to handle the dragged button specially.
1955 if (column == tree_view->priv->drag_column)
1957 GtkAllocation drag_allocation;
1958 drag_allocation.width = gdk_window_get_width (tree_view->priv->drag_window);
1959 drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window);
1960 drag_allocation.x = 0;
1961 drag_allocation.y = 0;
1962 pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column,
1964 width += drag_allocation.width;
1968 real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1970 col_allocation.x = width;
1971 column->width = real_requested_width;
1975 if (number_of_expand_columns == 1)
1977 /* We add the remander to the last column as
1979 column->width += extra;
1983 column->width += extra_per_column;
1984 extra -= extra_per_column;
1985 number_of_expand_columns --;
1989 if (column->width != old_width)
1990 g_object_notify (G_OBJECT (column), "width");
1992 col_allocation.width = column->width;
1993 width += column->width;
1995 if (column->width > old_width)
1996 column_changed = TRUE;
1998 pspp_sheet_view_column_size_allocate (column, &col_allocation);
2001 gdk_window_move_resize (column->window,
2002 col_allocation.x + (rtl ? 0 : col_allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
2004 TREE_VIEW_DRAG_WIDTH, col_allocation.height);
2007 /* We change the width here. The user might have been resizing columns,
2008 * so the total width of the tree view changes.
2010 tree_view->priv->width = width;
2012 *width_changed = TRUE;
2015 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2019 pspp_sheet_view_size_allocate (GtkWidget *widget,
2020 GtkAllocation *allocation)
2022 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2024 gboolean width_changed = FALSE;
2025 GtkAllocation old_allocation;
2026 gtk_widget_get_allocation (widget, &old_allocation);
2028 if (allocation->width != old_allocation.width)
2029 width_changed = TRUE;
2032 gtk_widget_set_allocation (widget, allocation);
2034 tmp_list = tree_view->priv->children;
2038 GtkAllocation allocation;
2040 PsppSheetViewChild *child = tmp_list->data;
2041 tmp_list = tmp_list->next;
2043 /* totally ignore our child's requisition */
2044 allocation.x = child->x;
2045 allocation.y = child->y;
2046 allocation.width = child->width;
2047 allocation.height = child->height;
2048 gtk_widget_size_allocate (child->widget, &allocation);
2051 /* We size-allocate the columns first because the width of the
2052 * tree view (used in updating the adjustments below) might change.
2054 pspp_sheet_view_size_allocate_columns (widget, &width_changed);
2056 gtk_adjustment_set_page_size (tree_view->priv->hadjustment, allocation->width);
2057 gtk_adjustment_set_page_increment (tree_view->priv->hadjustment, allocation->width * 0.9);
2058 gtk_adjustment_set_step_increment (tree_view->priv->hadjustment, allocation->width * 0.1);
2059 gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0);
2060 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->hadjustment), tree_view->priv->width));
2062 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2064 if (allocation->width < tree_view->priv->width)
2066 if (tree_view->priv->init_hadjust_value)
2068 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2069 tree_view->priv->init_hadjust_value = FALSE;
2071 else if (allocation->width != old_allocation.width)
2073 gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) - allocation->width + old_allocation.width, 0, tree_view->priv->width - allocation->width));
2076 gtk_adjustment_set_value (tree_view->priv->hadjustment, CLAMP (tree_view->priv->width - (tree_view->priv->prev_width - gtk_adjustment_get_value (tree_view->priv->hadjustment)), 0, tree_view->priv->width - allocation->width));
2080 gtk_adjustment_set_value (tree_view->priv->hadjustment, 0);
2081 tree_view->priv->init_hadjust_value = TRUE;
2085 if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + allocation->width > tree_view->priv->width)
2086 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2088 gtk_adjustment_changed (tree_view->priv->hadjustment);
2090 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2091 gtk_adjustment_set_step_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.1);
2092 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.9);
2093 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
2094 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->vadjustment), tree_view->priv->height));
2096 gtk_adjustment_changed (tree_view->priv->vadjustment);
2098 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2099 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
2100 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2101 else if (gtk_adjustment_get_value (tree_view->priv->vadjustment) + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
2102 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2103 tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
2104 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2105 pspp_sheet_view_top_row_to_dy (tree_view);
2107 pspp_sheet_view_dy_to_top_row (tree_view);
2109 if (gtk_widget_get_realized (widget))
2111 gdk_window_move_resize (gtk_widget_get_window (widget),
2112 allocation->x, allocation->y,
2113 allocation->width, allocation->height);
2114 gdk_window_move_resize (tree_view->priv->header_window,
2115 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2117 MAX (tree_view->priv->width, allocation->width),
2118 tree_view->priv->header_height);
2119 gdk_window_move_resize (tree_view->priv->bin_window,
2120 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2121 TREE_VIEW_HEADER_HEIGHT (tree_view),
2122 MAX (tree_view->priv->width, allocation->width),
2123 allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2126 if (tree_view->priv->row_count == 0)
2127 invalidate_empty_focus (tree_view);
2129 if (gtk_widget_get_realized (widget))
2131 gboolean has_expand_column = FALSE;
2132 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2134 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2136 has_expand_column = TRUE;
2141 /* This little hack only works if we have an LTR locale, and no column has the */
2144 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2145 ! has_expand_column)
2146 invalidate_last_column (tree_view);
2148 gtk_widget_queue_draw (widget);
2153 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2155 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2157 GtkWidget *widget = GTK_WIDGET (tree_view);
2159 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2160 gtk_widget_grab_focus (widget);
2161 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2165 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2168 return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2172 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2175 range_tower_set1 (tree_view->priv->selected, node, 1);
2179 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2182 range_tower_set0 (tree_view->priv->selected, node, 1);
2186 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2189 return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2193 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2196 return node > 0 ? node - 1 : -1;
2200 all_columns_selected (PsppSheetView *tree_view)
2204 for (list = tree_view->priv->columns; list; list = list->next)
2206 PsppSheetViewColumn *column = list->data;
2207 if (column->selectable && !column->selected)
2215 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2217 PsppSheetViewColumn *column,
2218 GdkEventButton *event)
2220 PsppSheetSelection *selection;
2221 PsppSheetSelectionMode mode;
2223 gboolean update_anchor;
2227 g_return_val_if_fail (tree_view != NULL, FALSE);
2228 g_return_val_if_fail (column != NULL, FALSE);
2230 selection = tree_view->priv->selection;
2231 mode = pspp_sheet_selection_get_mode (selection);
2232 if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2235 if (!column->row_head)
2240 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2241 if (event->type != GDK_BUTTON_PRESS
2242 || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2248 path = gtk_tree_path_new_from_indices (node, -1);
2251 pspp_sheet_selection_unselect_all (selection);
2252 pspp_sheet_selection_select_path (selection, path);
2253 pspp_sheet_selection_select_all_columns (selection);
2254 update_anchor = TRUE;
2257 else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2259 if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2260 || !all_columns_selected (tree_view))
2262 pspp_sheet_selection_unselect_all (selection);
2263 pspp_sheet_selection_select_path (selection, path);
2264 pspp_sheet_selection_select_all_columns (selection);
2265 update_anchor = TRUE;
2269 update_anchor = handled = FALSE;
2271 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2272 && modifiers == GDK_CONTROL_MASK)
2274 if (!all_columns_selected (tree_view))
2276 pspp_sheet_selection_unselect_all (selection);
2277 pspp_sheet_selection_select_all_columns (selection);
2280 if (pspp_sheet_selection_path_is_selected (selection, path))
2281 pspp_sheet_selection_unselect_path (selection, path);
2283 pspp_sheet_selection_select_path (selection, path);
2284 update_anchor = TRUE;
2287 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2288 && modifiers == GDK_SHIFT_MASK)
2290 GtkTreeRowReference *anchor = tree_view->priv->anchor;
2291 GtkTreePath *anchor_path;
2293 if (all_columns_selected (tree_view)
2294 && gtk_tree_row_reference_valid (anchor))
2296 update_anchor = FALSE;
2297 anchor_path = gtk_tree_row_reference_get_path (anchor);
2301 update_anchor = TRUE;
2302 anchor_path = gtk_tree_path_copy (path);
2305 pspp_sheet_selection_unselect_all (selection);
2306 pspp_sheet_selection_select_range (selection, anchor_path, path);
2307 pspp_sheet_selection_select_all_columns (selection);
2309 gtk_tree_path_free (anchor_path);
2314 update_anchor = handled = FALSE;
2318 if (tree_view->priv->anchor)
2319 gtk_tree_row_reference_free (tree_view->priv->anchor);
2320 tree_view->priv->anchor =
2321 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2322 tree_view->priv->model,
2326 gtk_tree_path_free (path);
2331 find_click (PsppSheetView *tree_view,
2334 PsppSheetViewColumn **column,
2335 GdkRectangle *background_area,
2336 GdkRectangle *cell_area)
2343 /* find the node that was clicked */
2344 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2347 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2352 background_area->y = y_offset + y;
2353 background_area->height = ROW_HEIGHT (tree_view);
2354 background_area->x = 0;
2356 /* Let the column have a chance at selecting it. */
2357 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2358 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2359 list; list = (rtl ? list->prev : list->next))
2361 PsppSheetViewColumn *candidate = list->data;
2363 if (!candidate->visible)
2366 background_area->width = candidate->width;
2367 if ((background_area->x > x) ||
2368 (background_area->x + background_area->width <= x))
2370 background_area->x += background_area->width;
2374 /* we found the focus column */
2376 pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2378 *column = candidate;
2386 pspp_sheet_view_button_press (GtkWidget *widget,
2387 GdkEventButton *event)
2389 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2391 PsppSheetViewColumn *column = NULL;
2393 GdkRectangle background_area;
2394 GdkRectangle cell_area;
2397 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2398 pspp_sheet_view_stop_editing (tree_view, FALSE);
2401 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2402 * we're done handling the button press.
2405 if (event->window == tree_view->priv->bin_window)
2410 gint pre_val, aft_val;
2411 PsppSheetViewColumn *column = NULL;
2412 GtkCellRenderer *focus_cell = NULL;
2413 gboolean row_double_click = FALSE;
2416 if (tree_view->priv->row_count == 0)
2418 grab_focus_and_unset_draw_keyfocus (tree_view);
2422 if (!find_click (tree_view, event->x, event->y, &node, &column,
2423 &background_area, &cell_area))
2425 grab_focus_and_unset_draw_keyfocus (tree_view);
2429 tree_view->priv->focus_column = column;
2431 if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2435 pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2437 path = _pspp_sheet_view_find_path (tree_view, node);
2439 /* we only handle selection modifications on the first button press
2441 if (event->type == GDK_BUTTON_PRESS)
2443 PsppSheetSelectionMode mode = 0;
2445 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2446 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
2447 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2448 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
2450 focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2452 pspp_sheet_view_column_focus_cell (column, focus_cell);
2454 if (event->state & GDK_CONTROL_MASK)
2456 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, mode);
2457 pspp_sheet_view_real_toggle_cursor_row (tree_view);
2459 else if (event->state & GDK_SHIFT_MASK)
2461 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
2462 pspp_sheet_view_real_select_cursor_row (tree_view, FALSE, mode);
2466 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
2469 if (tree_view->priv->anchor_column == NULL ||
2470 !(event->state & GDK_SHIFT_MASK))
2471 tree_view->priv->anchor_column = column;
2472 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2473 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2474 tree_view->priv->anchor_column,
2478 /* the treeview may have been scrolled because of _set_cursor,
2482 aft_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2483 dval = pre_val - aft_val;
2485 cell_area.y += dval;
2486 background_area.y += dval;
2488 /* Save press to possibly begin a drag
2490 if (!tree_view->priv->in_grab &&
2491 tree_view->priv->pressed_button < 0)
2493 tree_view->priv->pressed_button = event->button;
2494 tree_view->priv->press_start_x = event->x;
2495 tree_view->priv->press_start_y = event->y;
2496 tree_view->priv->press_start_node = node;
2498 if (tree_view->priv->rubber_banding_enable
2499 && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2500 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2502 tree_view->priv->press_start_y += tree_view->priv->dy;
2503 tree_view->priv->rubber_band_x = event->x;
2504 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2505 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2507 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2508 tree_view->priv->rubber_band_ctrl = TRUE;
2509 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2510 tree_view->priv->rubber_band_shift = TRUE;
2515 /* Test if a double click happened on the same row. */
2516 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2518 int double_click_time, double_click_distance;
2520 g_object_get (gtk_settings_get_for_screen (
2521 gtk_widget_get_screen (widget)),
2522 "gtk-double-click-time", &double_click_time,
2523 "gtk-double-click-distance", &double_click_distance,
2526 /* Same conditions as _gdk_event_button_generate */
2527 if (tree_view->priv->last_button_x != -1 &&
2528 (event->time < tree_view->priv->last_button_time + double_click_time) &&
2529 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2530 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2532 /* We do no longer compare paths of this row and the
2533 * row clicked previously. We use the double click
2534 * distance to decide whether this is a valid click,
2535 * allowing the mouse to slightly move over another row.
2537 row_double_click = TRUE;
2539 tree_view->priv->last_button_time = 0;
2540 tree_view->priv->last_button_x = -1;
2541 tree_view->priv->last_button_y = -1;
2545 tree_view->priv->last_button_time = event->time;
2546 tree_view->priv->last_button_x = event->x;
2547 tree_view->priv->last_button_y = event->y;
2551 if (row_double_click)
2553 gtk_grab_remove (widget);
2554 pspp_sheet_view_row_activated (tree_view, path, column);
2556 if (tree_view->priv->pressed_button == event->button)
2557 tree_view->priv->pressed_button = -1;
2560 gtk_tree_path_free (path);
2562 /* If we activated the row through a double click we don't want to grab
2563 * focus back, as moving focus to another widget is pretty common.
2565 if (!row_double_click)
2566 grab_focus_and_unset_draw_keyfocus (tree_view);
2571 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2573 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2575 column = list->data;
2576 if (event->window == column->window &&
2577 column->resizable &&
2582 if (gdk_pointer_grab (column->window, FALSE,
2583 GDK_POINTER_MOTION_HINT_MASK |
2584 GDK_BUTTON1_MOTION_MASK |
2585 GDK_BUTTON_RELEASE_MASK,
2586 NULL, NULL, event->time))
2589 gtk_grab_add (widget);
2590 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2591 column->resized_width = column->width;
2593 /* block attached dnd signal handler */
2594 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2596 g_signal_handlers_block_matched (widget,
2597 G_SIGNAL_MATCH_DATA,
2601 tree_view->priv->drag_pos = i;
2602 tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2604 if (!gtk_widget_has_focus (widget))
2605 gtk_widget_grab_focus (widget);
2613 /* GtkWidget::button_release_event helper */
2615 pspp_sheet_view_button_release_drag_column (GtkWidget *widget,
2616 GdkEventButton *event)
2618 PsppSheetView *tree_view;
2622 tree_view = PSPP_SHEET_VIEW (widget);
2624 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2625 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2626 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2628 /* Move the button back */
2629 g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2631 g_object_ref (tree_view->priv->drag_column->button);
2632 gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2633 gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2634 gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2635 g_object_unref (tree_view->priv->drag_column->button);
2636 gtk_widget_queue_resize (widget);
2637 if (tree_view->priv->drag_column->resizable)
2639 gdk_window_raise (tree_view->priv->drag_column->window);
2640 gdk_window_show (tree_view->priv->drag_column->window);
2643 gdk_window_hide (tree_view->priv->drag_column->window);
2645 gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2649 if (tree_view->priv->cur_reorder &&
2650 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2651 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2652 tree_view->priv->cur_reorder->right_column);
2656 if (tree_view->priv->cur_reorder &&
2657 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2658 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2659 tree_view->priv->cur_reorder->left_column);
2661 tree_view->priv->drag_column = NULL;
2662 gdk_window_hide (tree_view->priv->drag_window);
2664 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2665 g_slice_free (PsppSheetViewColumnReorder, l->data);
2666 g_list_free (tree_view->priv->column_drag_info);
2667 tree_view->priv->column_drag_info = NULL;
2668 tree_view->priv->cur_reorder = NULL;
2670 if (tree_view->priv->drag_highlight_window)
2671 gdk_window_hide (tree_view->priv->drag_highlight_window);
2673 /* Reset our flags */
2674 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2675 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2680 /* GtkWidget::button_release_event helper */
2682 pspp_sheet_view_button_release_column_resize (GtkWidget *widget,
2683 GdkEventButton *event)
2685 PsppSheetView *tree_view;
2688 tree_view = PSPP_SHEET_VIEW (widget);
2690 tree_view->priv->drag_pos = -1;
2692 /* unblock attached dnd signal handler */
2693 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2695 g_signal_handlers_unblock_matched (widget,
2696 G_SIGNAL_MATCH_DATA,
2700 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2701 gtk_grab_remove (widget);
2702 gdk_display_pointer_ungrab (gdk_window_get_display (event->window),
2708 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2709 GdkEventButton *event)
2711 GtkCellEditable *cell_editable;
2716 PsppSheetViewColumn *column;
2717 GdkRectangle background_area;
2718 GdkRectangle cell_area;
2724 if (event->window != tree_view->priv->bin_window)
2727 /* Ignore a released button, if that button wasn't depressed */
2728 if (tree_view->priv->pressed_button != event->button)
2731 if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2735 /* decide if we edit */
2736 path = _pspp_sheet_view_find_path (tree_view, node);
2737 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2738 if (event->button != 1 || modifiers)
2741 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2742 pspp_sheet_view_column_cell_set_cell_data (column,
2743 tree_view->priv->model,
2746 if (!pspp_sheet_view_column_get_quick_edit (column)
2747 && _pspp_sheet_view_column_has_editable_cell (column))
2750 flags = 0; /* FIXME: get the right flags */
2751 path_string = gtk_tree_path_to_string (path);
2753 if (!_pspp_sheet_view_column_cell_event (column,
2761 if (cell_editable == NULL)
2764 pspp_sheet_view_real_set_cursor (tree_view, path,
2765 TRUE, TRUE, 0); /* XXX mode? */
2766 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2769 _pspp_sheet_view_column_get_neighbor_sizes (
2770 column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2773 area.width -= right + left;
2775 pspp_sheet_view_real_start_editing (tree_view,
2782 g_free (path_string);
2783 gtk_tree_path_free (path);
2788 pspp_sheet_view_button_release (GtkWidget *widget,
2789 GdkEventButton *event)
2791 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2793 pspp_sheet_view_stop_editing (tree_view, FALSE);
2794 if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2795 && pspp_sheet_view_button_release_edit (tree_view, event))
2797 if (tree_view->priv->pressed_button == event->button)
2798 tree_view->priv->pressed_button = -1;
2800 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2804 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2805 return pspp_sheet_view_button_release_drag_column (widget, event);
2807 if (tree_view->priv->rubber_band_status)
2808 pspp_sheet_view_stop_rubber_band (tree_view);
2810 if (tree_view->priv->pressed_button == event->button)
2811 tree_view->priv->pressed_button = -1;
2813 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2814 return pspp_sheet_view_button_release_column_resize (widget, event);
2820 pspp_sheet_view_grab_broken (GtkWidget *widget,
2821 GdkEventGrabBroken *event)
2823 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2825 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2826 pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2828 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2829 pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2834 /* GtkWidget::motion_event function set.
2838 do_prelight (PsppSheetView *tree_view,
2840 /* these are in bin_window coords */
2844 int prev_node = tree_view->priv->prelight_node;
2846 if (prev_node != node)
2848 tree_view->priv->prelight_node = node;
2851 _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2854 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2860 prelight_or_select (PsppSheetView *tree_view,
2862 /* these are in bin_window coords */
2866 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2868 if (tree_view->priv->hover_selection &&
2869 (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2870 !(tree_view->priv->edited_column &&
2871 tree_view->priv->edited_column->editable_widget))
2875 if (!pspp_sheet_view_node_is_selected (tree_view, node))
2879 path = _pspp_sheet_view_find_path (tree_view, node);
2880 pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2881 if (pspp_sheet_view_node_is_selected (tree_view, node))
2883 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2884 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE, 0); /* XXX mode? */
2886 gtk_tree_path_free (path);
2890 else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2891 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2894 do_prelight (tree_view, node, x, y);
2898 ensure_unprelighted (PsppSheetView *tree_view)
2900 do_prelight (tree_view,
2902 -1000, -1000); /* coords not possibly over an arrow */
2904 g_assert (tree_view->priv->prelight_node < 0);
2908 update_prelight (PsppSheetView *tree_view,
2915 if (tree_view->priv->row_count == 0)
2920 ensure_unprelighted (tree_view);
2924 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2928 pspp_sheet_view_find_offset (tree_view, new_y, &node);
2931 prelight_or_select (tree_view, node, x, y);
2937 /* Our motion arrow is either a box (in the case of the original spot)
2938 * or an arrow. It is expander_size wide.
2961 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2964 PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2965 GtkWidget *widget = GTK_WIDGET (tree_view);
2966 GdkBitmap *mask = NULL;
2971 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2972 GdkWindowAttr attributes;
2973 guint attributes_mask;
2976 reorder->left_column == tree_view->priv->drag_column ||
2977 reorder->right_column == tree_view->priv->drag_column)
2978 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2979 else if (reorder->left_column || reorder->right_column)
2981 GdkRectangle visible_rect;
2982 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2983 if (reorder->left_column)
2984 x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2986 x = reorder->right_column->allocation.x;
2988 if (x < visible_rect.x)
2989 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2990 else if (x > visible_rect.x + visible_rect.width)
2991 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2993 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2996 /* We want to draw the rectangle over the initial location. */
2997 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
3002 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
3004 if (tree_view->priv->drag_highlight_window)
3006 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3008 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3011 attributes.window_type = GDK_WINDOW_CHILD;
3012 attributes.wclass = GDK_INPUT_OUTPUT;
3013 attributes.x = tree_view->priv->drag_column_x;
3015 width = attributes.width = tree_view->priv->drag_column->allocation.width;
3016 height = attributes.height = tree_view->priv->drag_column->allocation.height;
3017 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3018 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3019 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3020 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3021 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
3022 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3024 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3025 gc = gdk_gc_new (mask);
3027 gdk_gc_set_foreground (gc, &col);
3028 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3030 gdk_gc_set_foreground(gc, &col);
3031 gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
3032 g_object_unref (gc);
3034 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3036 if (mask) g_object_unref (mask);
3037 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3040 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
3046 width = tree_view->priv->expander_size;
3048 /* Get x, y, width, height of arrow */
3049 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
3050 if (reorder->left_column)
3052 x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
3053 height = reorder->left_column->allocation.height;
3057 x += reorder->right_column->allocation.x - width/2;
3058 height = reorder->right_column->allocation.height;
3060 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3061 height += tree_view->priv->expander_size;
3063 /* Create the new window */
3064 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3066 if (tree_view->priv->drag_highlight_window)
3068 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3070 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3073 attributes.window_type = GDK_WINDOW_TEMP;
3074 attributes.wclass = GDK_INPUT_OUTPUT;
3075 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3076 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3077 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3078 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3081 attributes.width = width;
3082 attributes.height = height;
3083 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3084 &attributes, attributes_mask);
3085 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3087 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3088 gc = gdk_gc_new (mask);
3090 gdk_gc_set_foreground (gc, &col);
3091 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3093 /* Draw the 2 arrows as per above */
3095 gdk_gc_set_foreground (gc, &col);
3096 for (i = 0; i < width; i ++)
3098 if (i == (width/2 - 1))
3100 gdk_draw_line (mask, gc, i, j, i, height - j);
3101 if (i < (width/2 - 1))
3106 g_object_unref (gc);
3107 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3109 if (mask) g_object_unref (mask);
3112 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3113 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3115 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3116 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3122 width = tree_view->priv->expander_size;
3124 /* Get x, y, width, height of arrow */
3125 width = width/2; /* remember, the arrow only takes half the available width */
3126 gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
3127 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3128 x += widget->allocation.width - width;
3130 if (reorder->left_column)
3131 height = reorder->left_column->allocation.height;
3133 height = reorder->right_column->allocation.height;
3135 y -= tree_view->priv->expander_size;
3136 height += 2*tree_view->priv->expander_size;
3138 /* Create the new window */
3139 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3140 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3142 if (tree_view->priv->drag_highlight_window)
3144 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3146 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3149 attributes.window_type = GDK_WINDOW_TEMP;
3150 attributes.wclass = GDK_INPUT_OUTPUT;
3151 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3152 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3153 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3154 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3157 attributes.width = width;
3158 attributes.height = height;
3159 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3160 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3162 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3163 gc = gdk_gc_new (mask);
3165 gdk_gc_set_foreground (gc, &col);
3166 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3168 /* Draw the 2 arrows as per above */
3170 gdk_gc_set_foreground (gc, &col);
3171 j = tree_view->priv->expander_size;
3172 for (i = 0; i < width; i ++)
3175 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3179 gdk_draw_line (mask, gc, k, j, k, height - j);
3180 gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3181 gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3184 g_object_unref (gc);
3185 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3187 if (mask) g_object_unref (mask);
3190 tree_view->priv->drag_column_window_state = arrow_type;
3191 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3195 g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3196 gdk_window_hide (tree_view->priv->drag_highlight_window);
3200 gdk_window_show (tree_view->priv->drag_highlight_window);
3201 gdk_window_raise (tree_view->priv->drag_highlight_window);
3206 pspp_sheet_view_motion_resize_column (GtkWidget *widget,
3207 GdkEventMotion *event)
3211 PsppSheetViewColumn *column;
3212 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3214 column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3216 if (event->is_hint || event->window != gtk_widget_get_window (widget))
3217 gtk_widget_get_pointer (widget, &x, NULL);
3221 if (tree_view->priv->hadjustment)
3222 x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
3224 new_width = pspp_sheet_view_new_column_width (tree_view,
3225 tree_view->priv->drag_pos, &x);
3226 if (x != tree_view->priv->x_drag &&
3227 (new_width != column->fixed_width))
3229 column->use_resized_width = TRUE;
3230 column->resized_width = new_width;
3233 column->resized_width -= tree_view->priv->last_extra_space_per_column;
3235 gtk_widget_queue_resize (widget);
3243 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3245 PsppSheetViewColumnReorder *reorder = NULL;
3249 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3250 for (list = tree_view->priv->column_drag_info; list; list = list->next)
3252 reorder = (PsppSheetViewColumnReorder *) list->data;
3253 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3258 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3261 tree_view->priv->cur_reorder = reorder;
3262 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3266 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3268 GdkRectangle visible_rect;
3273 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3274 y += tree_view->priv->dy;
3276 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3278 /* see if we are near the edge. */
3279 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3282 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3287 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->vadjustment) + offset, 0.0,
3288 gtk_adjustment_get_upper (tree_view->priv->vadjustment) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
3289 gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3293 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3295 GdkRectangle visible_rect;
3300 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3302 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3304 /* See if we are near the edge. */
3305 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3308 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3314 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) + offset,
3315 0.0, gtk_adjustment_get_upper (tree_view->priv->hadjustment) - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
3316 gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3323 pspp_sheet_view_motion_drag_column (GtkWidget *widget,
3324 GdkEventMotion *event)
3326 PsppSheetView *tree_view = (PsppSheetView *) widget;
3327 PsppSheetViewColumn *column = tree_view->priv->drag_column;
3329 GtkAllocation allocation;
3332 if ((column == NULL) ||
3333 (event->window != tree_view->priv->drag_window))
3336 /* Handle moving the header */
3337 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3338 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
3339 x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3340 MAX (tree_view->priv->width, allocation.width) - column->allocation.width);
3341 gdk_window_move (tree_view->priv->drag_window, x, y);
3343 /* autoscroll, if needed */
3344 pspp_sheet_view_horizontal_autoscroll (tree_view);
3345 /* Update the current reorder position and arrow; */
3346 pspp_sheet_view_update_current_reorder (tree_view);
3352 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3354 remove_scroll_timeout (tree_view);
3355 gtk_grab_remove (GTK_WIDGET (tree_view));
3357 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3359 GtkTreePath *tmp_path;
3361 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3363 /* The anchor path should be set to the start path */
3364 tmp_path = _pspp_sheet_view_find_path (tree_view,
3365 tree_view->priv->rubber_band_start_node);
3367 if (tree_view->priv->anchor)
3368 gtk_tree_row_reference_free (tree_view->priv->anchor);
3370 tree_view->priv->anchor =
3371 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3372 tree_view->priv->model,
3375 gtk_tree_path_free (tmp_path);
3377 /* ... and the cursor to the end path */
3378 tmp_path = _pspp_sheet_view_find_path (tree_view,
3379 tree_view->priv->rubber_band_end_node);
3380 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE, 0); /* XXX mode? */
3381 gtk_tree_path_free (tmp_path);
3383 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3386 /* Clear status variables */
3387 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3388 tree_view->priv->rubber_band_shift = 0;
3389 tree_view->priv->rubber_band_ctrl = 0;
3391 tree_view->priv->rubber_band_start_node = -1;
3392 tree_view->priv->rubber_band_end_node = -1;
3396 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3400 gboolean skip_start,
3403 if (start_node == end_node)
3406 /* We skip the first node and jump inside the loop */
3412 /* Small optimization by assuming insensitive nodes are never
3417 if (tree_view->priv->rubber_band_shift)
3418 pspp_sheet_view_node_select (tree_view, start_node);
3419 else if (tree_view->priv->rubber_band_ctrl)
3421 /* Toggle the selection state */
3422 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3423 pspp_sheet_view_node_unselect (tree_view, start_node);
3425 pspp_sheet_view_node_select (tree_view, start_node);
3428 pspp_sheet_view_node_select (tree_view, start_node);
3432 /* Mirror the above */
3433 if (tree_view->priv->rubber_band_shift)
3434 pspp_sheet_view_node_unselect (tree_view, start_node);
3435 else if (tree_view->priv->rubber_band_ctrl)
3437 /* Toggle the selection state */
3438 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3439 pspp_sheet_view_node_unselect (tree_view, start_node);
3441 pspp_sheet_view_node_select (tree_view, start_node);
3444 pspp_sheet_view_node_unselect (tree_view, start_node);
3447 _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3449 if (start_node == end_node)
3454 start_node = pspp_sheet_view_node_next (tree_view, start_node);
3457 /* Ran out of tree */
3460 if (skip_end && start_node == end_node)
3467 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3470 return node * tree_view->priv->fixed_height;
3474 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3478 int fixed_height = tree_view->priv->fixed_height;
3479 if (fixed_height <= 0
3481 || height >= tree_view->priv->row_count * fixed_height)
3488 *new_node = height / fixed_height;
3489 return height % fixed_height;
3494 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3499 pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3500 pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3502 /* Handle the start area first */
3503 if (tree_view->priv->rubber_band_start_node < 0)
3505 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3512 else if (start_node < tree_view->priv->rubber_band_start_node)
3514 /* New node is above the old one; selection became bigger */
3515 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3517 tree_view->priv->rubber_band_start_node,
3522 else if (start_node > tree_view->priv->rubber_band_start_node)
3524 /* New node is below the old one; selection became smaller */
3525 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3526 tree_view->priv->rubber_band_start_node,
3533 tree_view->priv->rubber_band_start_node = start_node;
3535 /* Next, handle the end area */
3536 if (tree_view->priv->rubber_band_end_node < 0)
3538 /* In the event this happens, start_node was also -1; this case is
3542 else if (end_node < 0)
3544 /* Find the last node in the tree */
3545 pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3548 /* Selection reached end of the tree */
3549 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3550 tree_view->priv->rubber_band_end_node,
3556 else if (end_node > tree_view->priv->rubber_band_end_node)
3558 /* New node is below the old one; selection became bigger */
3559 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3560 tree_view->priv->rubber_band_end_node,
3566 else if (end_node < tree_view->priv->rubber_band_end_node)
3568 /* New node is above the old one; selection became smaller */
3569 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3571 tree_view->priv->rubber_band_end_node,
3577 tree_view->priv->rubber_band_end_node = end_node;
3580 #define GDK_RECTANGLE_PTR(X) ((GdkRectangle *)(X))
3583 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3586 cairo_rectangle_int_t old_area;
3587 cairo_rectangle_int_t new_area;
3588 cairo_rectangle_int_t common;
3589 cairo_region_t *invalid_region;
3590 PsppSheetViewColumn *column;
3592 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3593 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3594 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3595 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3597 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3600 y = MAX (y, 0) + tree_view->priv->dy;
3602 new_area.x = MIN (tree_view->priv->press_start_x, x);
3603 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3604 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3605 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3607 invalid_region = cairo_region_create_rectangle (&old_area);
3608 cairo_region_union_rectangle (invalid_region, &new_area);
3610 gdk_rectangle_intersect (GDK_RECTANGLE_PTR (&old_area),
3611 GDK_RECTANGLE_PTR (&new_area), GDK_RECTANGLE_PTR (&common));
3612 if (common.width > 2 && common.height > 2)
3614 cairo_region_t *common_region;
3616 /* make sure the border is invalidated */
3622 common_region = cairo_region_create_rectangle (&common);
3624 cairo_region_subtract (invalid_region, common_region);
3625 cairo_region_destroy (common_region);
3628 #if GTK_MAJOR_VERSION == 3
3629 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
3632 cairo_rectangle_int_t extents;
3634 cairo_region_get_extents (invalid_region, &extents);
3635 ereg = gdk_region_rectangle (GDK_RECTANGLE_PTR (&extents));
3636 gdk_window_invalidate_region (tree_view->priv->bin_window, ereg, TRUE);
3637 gdk_region_destroy (ereg);
3641 cairo_region_destroy (invalid_region);
3643 tree_view->priv->rubber_band_x = x;
3644 tree_view->priv->rubber_band_y = y;
3645 pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3647 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3648 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3649 tree_view->priv->anchor_column,
3652 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3654 pspp_sheet_view_update_rubber_band_selection (tree_view);
3659 pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view,
3664 GdkRectangle rubber_rect;
3668 rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3669 rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3670 rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3671 rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3673 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3676 cr = gdk_cairo_create (tree_view->priv->bin_window);
3677 cairo_set_line_width (cr, 1.0);
3679 style = gtk_widget_get_style (GTK_WIDGET (tree_view));
3680 cairo_set_source_rgba (cr,
3681 style->fg[GTK_STATE_NORMAL].red / 65535.,
3682 style->fg[GTK_STATE_NORMAL].green / 65535.,
3683 style->fg[GTK_STATE_NORMAL].blue / 65535.,
3686 gdk_cairo_rectangle (cr, &rect);
3690 cairo_set_source_rgb (cr,
3691 style->fg[GTK_STATE_NORMAL].red / 65535.,
3692 style->fg[GTK_STATE_NORMAL].green / 65535.,
3693 style->fg[GTK_STATE_NORMAL].blue / 65535.);
3695 cairo_rectangle (cr,
3696 rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3697 rubber_rect.width - 1, rubber_rect.height - 1);
3706 pspp_sheet_view_motion_bin_window (GtkWidget *widget,
3707 GdkEventMotion *event)
3709 PsppSheetView *tree_view;
3713 tree_view = (PsppSheetView *) widget;
3715 if (tree_view->priv->row_count == 0)
3718 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3720 GdkRectangle background_area, cell_area;
3721 PsppSheetViewColumn *column;
3723 if (find_click (tree_view, event->x, event->y, &node, &column,
3724 &background_area, &cell_area)
3725 && tree_view->priv->focus_column == column
3726 && tree_view->priv->press_start_node == node)
3729 gtk_grab_add (GTK_WIDGET (tree_view));
3730 pspp_sheet_view_update_rubber_band (tree_view);
3732 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3734 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3736 pspp_sheet_view_update_rubber_band (tree_view);
3738 add_scroll_timeout (tree_view);
3741 /* only check for an initiated drag when a button is pressed */
3742 if (tree_view->priv->pressed_button >= 0
3743 && !tree_view->priv->rubber_band_status)
3744 pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3746 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3750 pspp_sheet_view_find_offset (tree_view, new_y, &node);
3752 tree_view->priv->event_last_x = event->x;
3753 tree_view->priv->event_last_y = event->y;
3755 prelight_or_select (tree_view, node, event->x, event->y);
3761 pspp_sheet_view_motion (GtkWidget *widget,
3762 GdkEventMotion *event)
3764 PsppSheetView *tree_view;
3766 tree_view = (PsppSheetView *) widget;
3768 /* Resizing a column */
3769 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3770 return pspp_sheet_view_motion_resize_column (widget, event);
3773 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3774 return pspp_sheet_view_motion_drag_column (widget, event);
3776 /* Sanity check it */
3777 if (event->window == tree_view->priv->bin_window)
3778 return pspp_sheet_view_motion_bin_window (widget, event);
3783 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3784 * the tree is empty.
3787 invalidate_empty_focus (PsppSheetView *tree_view)
3791 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3796 area.width = gdk_window_get_width (tree_view->priv->bin_window);
3797 area.height = gdk_window_get_height (tree_view->priv->bin_window);
3798 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3801 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3805 draw_empty_focus (PsppSheetView *tree_view)
3807 GtkWidget *widget = GTK_WIDGET (tree_view);
3809 cairo_t *cr = gdk_cairo_create (tree_view->priv->bin_window);
3811 if (!gtk_widget_has_focus (widget))
3814 w = gdk_window_get_width (tree_view->priv->bin_window);
3815 h = gdk_window_get_height (tree_view->priv->bin_window);
3821 gtk_paint_focus (gtk_widget_get_style (widget),
3823 gtk_widget_get_state (widget),
3831 pspp_sheet_view_draw_vertical_grid_lines (PsppSheetView *tree_view,
3833 gint n_visible_columns,
3837 GList *list = tree_view->priv->columns;
3841 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3842 && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3845 /* Only draw the lines for visible rows and columns */
3846 for (list = tree_view->priv->columns; list; list = list->next, i++)
3848 PsppSheetViewColumn *column = list->data;
3851 if (! column->visible)
3854 current_x += column->width;
3856 /* Generally the grid lines should fit within the column, but for the
3857 last visible column we put it just past the end of the column.
3858 (Otherwise horizontal grid lines sometimes stick out by one pixel.) */
3860 if (i != n_visible_columns - 1)
3863 cairo_set_line_width (cr, 1.0);
3864 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
3865 cairo_move_to (cr, x + 0.5, min_y);
3866 cairo_line_to (cr, x + 0.5, max_y - min_y);
3871 /* Warning: Very scary function.
3872 * Modify at your own risk
3874 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3875 * FIXME: It's not...
3878 pspp_sheet_view_draw_bin (GtkWidget *widget,
3881 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3886 int drag_highlight = -1;
3889 gint y_offset, cell_offset;
3891 GdkRectangle background_area;
3892 GdkRectangle cell_area;
3894 gint bin_window_width;
3895 gint bin_window_height;
3896 GtkTreePath *cursor_path;
3897 GtkTreePath *drag_dest_path;
3898 GList *first_column, *last_column;
3899 gint vertical_separator;
3900 gint horizontal_separator;
3901 gint focus_line_width;
3902 gboolean allow_rules;
3903 gboolean has_special_cell;
3905 gint n_visible_columns;
3906 gint grid_line_width;
3907 gboolean row_ending_details;
3908 gboolean draw_vgrid_lines, draw_hgrid_lines;
3910 GtkStyleContext *context;
3911 context = gtk_widget_get_style_context (widget);
3914 GtkAllocation allocation;
3915 gtk_widget_get_allocation (widget, &allocation);
3917 GdkRectangle exposed_rect;
3918 gdk_cairo_get_clip_rectangle (cr, &exposed_rect);
3922 Zarea.height = allocation.height;
3924 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3926 gtk_widget_style_get (widget,
3927 "horizontal-separator", &horizontal_separator,
3928 "vertical-separator", &vertical_separator,
3929 "allow-rules", &allow_rules,
3930 "focus-line-width", &focus_line_width,
3931 "row-ending-details", &row_ending_details,
3934 if (tree_view->priv->row_count == 0)
3936 draw_empty_focus (tree_view);
3941 /* clip event->area to the visible area */
3942 if (Zarea.height < 0.5)
3946 validate_visible_area (tree_view);
3948 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, Zarea.y);
3952 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3954 gdk_window_get_width (tree_view->priv->bin_window);
3957 gdk_window_get_height (tree_view->priv->bin_window);
3960 if (tree_view->priv->height < bin_window_height)
3962 gtk_paint_flat_box (gtk_widget_get_style (widget),
3964 gtk_widget_get_state (widget),
3968 0, tree_view->priv->height,
3970 bin_window_height - tree_view->priv->height);
3976 /* find the path for the node */
3977 path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3978 gtk_tree_model_get_iter (tree_view->priv->model,
3981 gtk_tree_path_free (path);
3984 drag_dest_path = NULL;
3986 if (tree_view->priv->cursor)
3987 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3990 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3992 if (tree_view->priv->drag_dest_row)
3993 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3996 _pspp_sheet_view_find_node (tree_view, drag_dest_path,
4000 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
4001 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
4003 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
4004 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
4006 if (draw_vgrid_lines || draw_hgrid_lines)
4007 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
4009 n_visible_columns = 0;
4010 for (list = tree_view->priv->columns; list; list = list->next)
4012 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4014 n_visible_columns ++;
4017 /* Find the last column */
4018 for (last_column = g_list_last (tree_view->priv->columns);
4019 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
4020 last_column = last_column->prev)
4024 for (first_column = g_list_first (tree_view->priv->columns);
4025 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
4026 first_column = first_column->next)
4029 /* Actually process the expose event. To do this, we want to
4030 * start at the first node of the event, and walk the tree in
4031 * order, drawing each successive node.
4038 gboolean is_first = FALSE;
4039 gboolean is_last = FALSE;
4040 gboolean done = FALSE;
4043 max_height = ROW_HEIGHT (tree_view);
4047 background_area.y = y_offset + Zarea.y;
4048 background_area.height = max_height;
4049 max_y = background_area.y + max_height;
4053 if (node == tree_view->priv->prelight_node)
4054 flags |= GTK_CELL_RENDERER_PRELIT;
4056 selected = pspp_sheet_view_node_is_selected (tree_view, node);
4060 if (tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
4062 /* we *need* to set cell data on all cells before the call
4063 * to _has_special_cell, else _has_special_cell() does not
4064 * return a correct value.
4066 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4068 list = (rtl ? list->prev : list->next))
4070 PsppSheetViewColumn *column = list->data;
4071 pspp_sheet_view_column_cell_set_cell_data (column,
4072 tree_view->priv->model,
4076 has_special_cell = pspp_sheet_view_has_special_cell (tree_view);
4079 has_special_cell = tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
4081 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4083 list = (rtl ? list->prev : list->next))
4085 PsppSheetViewColumn *column = list->data;
4086 const gchar *detail = NULL;
4087 gboolean selected_column;
4090 if (!column->visible)
4093 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
4094 selected_column = column->selected && column->selectable;
4096 selected_column = TRUE;
4099 if (cell_offset > Zarea.x + Zarea.width ||
4100 cell_offset + column->width < Zarea.x)
4102 cell_offset += column->width;
4107 if (selected && selected_column)
4108 flags |= GTK_CELL_RENDERER_SELECTED;
4110 flags &= ~GTK_CELL_RENDERER_SELECTED;
4112 if (column->show_sort_indicator)
4113 flags |= GTK_CELL_RENDERER_SORTED;
4115 flags &= ~GTK_CELL_RENDERER_SORTED;
4118 flags |= GTK_CELL_RENDERER_FOCUSED;
4120 flags &= ~GTK_CELL_RENDERER_FOCUSED;
4122 background_area.x = cell_offset;
4123 background_area.width = column->width;
4125 cell_area = background_area;
4126 cell_area.y += vertical_separator / 2;
4127 cell_area.x += horizontal_separator / 2;
4128 cell_area.height -= vertical_separator;
4129 cell_area.width -= horizontal_separator;
4131 if (draw_vgrid_lines)
4133 if (list == first_column)
4135 cell_area.width -= grid_line_width / 2;
4137 else if (list == last_column)
4139 cell_area.x += grid_line_width / 2;
4140 cell_area.width -= grid_line_width / 2;
4144 cell_area.x += grid_line_width / 2;
4145 cell_area.width -= grid_line_width;
4149 if (draw_hgrid_lines)
4151 cell_area.y += grid_line_width / 2;
4152 cell_area.height -= grid_line_width;
4156 if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4158 if (!gdk_rectangle_intersect (&background_area, &exposed_rect, NULL))
4161 cell_offset += column->width;
4166 pspp_sheet_view_column_cell_set_cell_data (column,
4167 tree_view->priv->model,
4170 /* Select the detail for drawing the cell. relevant
4171 * factors are parity, sortedness, and whether to
4174 if (allow_rules && tree_view->priv->has_rules)
4176 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4177 n_visible_columns >= 3)
4180 detail = "cell_odd_ruled_sorted";
4182 detail = "cell_even_ruled_sorted";
4187 detail = "cell_odd_ruled";
4189 detail = "cell_even_ruled";
4194 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4195 n_visible_columns >= 3)
4198 detail = "cell_odd_sorted";
4200 detail = "cell_even_sorted";
4205 detail = "cell_odd";
4207 detail = "cell_even";
4213 gtk_style_context_save (context);
4214 state = gtk_cell_renderer_get_state (NULL, widget, flags);
4215 gtk_style_context_set_state (context, state);
4216 gtk_style_context_add_class (context, GTK_STYLE_CLASS_CELL);
4218 /* Draw background */
4219 gtk_render_background (context, cr,
4222 background_area.width,
4223 background_area.height);
4226 gtk_render_frame (context, cr,
4229 background_area.width,
4230 background_area.height);
4232 if (draw_hgrid_lines)
4234 cairo_set_line_width (cr, 1.0);
4235 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
4237 if (background_area.y >= 0)
4240 gdk_draw_line (event->window,
4241 tree_view->priv->grid_line_gc[widget->state],
4242 background_area.x, background_area.y,
4243 background_area.x + background_area.width,
4246 cairo_move_to (cr, background_area.x, background_area.y - 0.5);
4247 cairo_line_to (cr, background_area.x + background_area.width,
4248 background_area.y - 0.5);
4252 if (y_offset + max_height <= Zarea.height - 0.5)
4255 gdk_draw_line (event->window,
4256 tree_view->priv->grid_line_gc[widget->state],
4257 background_area.x, background_area.y + max_height,
4258 background_area.x + background_area.width,
4259 background_area.y + max_height);
4262 cairo_move_to (cr, background_area.x, background_area.y + max_height - 0.5);
4263 cairo_line_to (cr, background_area.x + background_area.width,
4264 background_area.y + max_height - 0.5);
4270 _pspp_sheet_view_column_cell_render (column,
4276 if (node == cursor && has_special_cell &&
4277 ((column == tree_view->priv->focus_column &&
4278 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4279 gtk_widget_has_focus (widget)) ||
4280 (column == tree_view->priv->edited_column)))
4282 _pspp_sheet_view_column_cell_draw_focus (column,
4289 cell_offset += column->width;
4290 gtk_style_context_restore (context);
4293 if (cell_offset < Zarea.x)
4295 gtk_paint_flat_box (gtk_widget_get_style (widget),
4303 Zarea.x - cell_offset,
4304 background_area.height);
4307 if (node == drag_highlight)
4309 /* Draw indicator for the drop
4311 gint highlight_y = -1;
4315 switch (tree_view->priv->drag_dest_pos)
4317 case PSPP_SHEET_VIEW_DROP_BEFORE:
4318 highlight_y = background_area.y - 1;
4319 if (highlight_y < 0)
4323 case PSPP_SHEET_VIEW_DROP_AFTER:
4324 highlight_y = background_area.y + background_area.height - 1;
4327 case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE:
4328 case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER:
4329 _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node);
4333 width = gdk_window_get_width (tree_view->priv->bin_window);
4335 if (row_ending_details)
4336 gtk_paint_focus (gtk_widget_get_style (widget),
4338 gtk_widget_get_state (widget),
4341 ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4342 : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4343 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4344 - focus_line_width / 2,
4345 width, ROW_HEIGHT (tree_view)
4346 - focus_line_width + 1);
4348 gtk_paint_focus (gtk_widget_get_style (widget),
4350 gtk_widget_get_state (widget),
4352 "treeview-drop-indicator",
4353 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4354 - focus_line_width / 2,
4355 width, ROW_HEIGHT (tree_view)
4356 - focus_line_width + 1);
4361 if (highlight_y >= 0)
4363 gdk_draw_line (event->window,
4364 widget->style->fg_gc[gtk_widget_get_state (widget)],
4367 rtl ? 0 : bin_window_width,
4373 /* draw the big row-spanning focus rectangle, if needed */
4374 if (!has_special_cell && node == cursor &&
4375 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4376 gtk_widget_has_focus (widget))
4378 gint tmp_y, tmp_height;
4380 GtkStateType focus_rect_state;
4383 flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
4384 (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
4385 (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE :
4388 width = gdk_window_get_width (tree_view->priv->bin_window);
4390 if (draw_hgrid_lines)
4392 tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node) + grid_line_width / 2;
4393 tmp_height = ROW_HEIGHT (tree_view) - grid_line_width;
4397 tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node);
4398 tmp_height = ROW_HEIGHT (tree_view);
4401 if (row_ending_details)
4402 gtk_paint_focus (gtk_widget_get_style (widget),
4407 ? (is_last ? "treeview" : "treeview-left" )
4408 : (is_last ? "treeview-right" : "treeview-middle" )),
4412 gtk_paint_focus (gtk_widget_get_style (widget),
4421 y_offset += max_height;
4425 node = pspp_sheet_view_node_next (tree_view, node);
4428 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
4432 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
4439 while (y_offset < Zarea.height);
4442 pspp_sheet_view_draw_vertical_grid_lines (tree_view, cr, n_visible_columns,
4446 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4448 GdkRectangle *rectangles;
4451 gdk_region_get_rectangles (event->region,
4455 while (n_rectangles--)
4456 pspp_sheet_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
4458 g_free (rectangles);
4463 gtk_tree_path_free (cursor_path);
4466 gtk_tree_path_free (drag_dest_path);
4473 pspp_sheet_view_draw (GtkWidget *widget,
4476 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4477 GtkStyleContext *context;
4479 context = gtk_widget_get_style_context (widget);
4481 if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window))
4486 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->bin_window);
4487 pspp_sheet_view_draw_bin (widget, cr);
4490 /* We can't just chain up to Container::expose as it will try to send the
4491 * event to the headers, so we handle propagating it to our children
4492 * (eg. widgets being edited) ourselves.
4494 tmp_list = tree_view->priv->children;
4497 PsppSheetViewChild *child = tmp_list->data;
4498 tmp_list = tmp_list->next;
4500 gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
4505 gtk_render_background (context, cr,
4507 gtk_widget_get_allocated_width (widget),
4508 gtk_widget_get_allocated_height (widget));
4511 gtk_style_context_save (context);
4512 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_VIEW);
4514 if (gtk_cairo_should_draw_window (cr, tree_view->priv->header_window))
4516 gint n_visible_columns;
4519 for (list = tree_view->priv->columns; list != NULL; list = list->next)
4521 PsppSheetViewColumn *column = list->data;
4523 if (column == tree_view->priv->drag_column || !column->visible)
4526 if (span_intersects (column->allocation.x, column->allocation.width,
4527 (int) gtk_adjustment_get_value (tree_view->priv->hadjustment),
4528 (int) gtk_widget_get_allocated_width (widget))
4529 && column->button != NULL)
4530 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4531 column->button, cr);
4534 n_visible_columns = 0;
4535 for (list = tree_view->priv->columns; list; list = list->next)
4537 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4539 n_visible_columns ++;
4542 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->header_window);
4543 pspp_sheet_view_draw_vertical_grid_lines (tree_view,
4547 TREE_VIEW_HEADER_HEIGHT (tree_view));
4550 if (tree_view->priv->drag_window &&
4551 gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window))
4553 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4554 tree_view->priv->drag_column->button,
4558 gtk_style_context_restore (context);
4570 /* returns 0x1 when no column has been found -- yes it's hackish */
4571 static PsppSheetViewColumn *
4572 pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
4573 PsppSheetViewColumn *column,
4576 PsppSheetViewColumn *left_column = NULL;
4577 PsppSheetViewColumn *cur_column = NULL;
4580 if (!column->reorderable)
4581 return (PsppSheetViewColumn *)0x1;
4583 switch (drop_position)
4586 /* find first column where we can drop */
4587 tmp_list = tree_view->priv->columns;
4588 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4589 return (PsppSheetViewColumn *)0x1;
4593 g_assert (tmp_list);
4595 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4596 tmp_list = tmp_list->next;
4598 if (left_column && left_column->visible == FALSE)
4601 if (!tree_view->priv->column_drop_func)
4604 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4606 left_column = cur_column;
4613 if (!tree_view->priv->column_drop_func)
4616 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4619 return (PsppSheetViewColumn *)0x1;
4623 /* find first column after column where we can drop */
4624 tmp_list = tree_view->priv->columns;
4626 for (; tmp_list; tmp_list = tmp_list->next)
4627 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4630 if (!tmp_list || !tmp_list->next)
4631 return (PsppSheetViewColumn *)0x1;
4633 tmp_list = tmp_list->next;
4634 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4635 tmp_list = tmp_list->next;
4639 g_assert (tmp_list);
4641 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4642 tmp_list = tmp_list->next;
4644 if (left_column && left_column->visible == FALSE)
4646 left_column = cur_column;
4648 tmp_list = tmp_list->next;
4652 if (!tree_view->priv->column_drop_func)
4655 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4657 left_column = cur_column;
4664 if (!tree_view->priv->column_drop_func)
4667 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4670 return (PsppSheetViewColumn *)0x1;
4674 /* find first column before column where we can drop */
4675 tmp_list = tree_view->priv->columns;
4677 for (; tmp_list; tmp_list = tmp_list->next)
4678 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4681 if (!tmp_list || !tmp_list->prev)
4682 return (PsppSheetViewColumn *)0x1;
4684 tmp_list = tmp_list->prev;
4685 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4686 tmp_list = tmp_list->prev;
4690 g_assert (tmp_list);
4692 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4694 if (left_column && !left_column->visible)
4696 /*if (!tmp_list->prev)
4697 return (PsppSheetViewColumn *)0x1;
4700 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4701 tmp_list = tmp_list->prev->prev;
4704 cur_column = left_column;
4706 tmp_list = tmp_list->prev;
4710 if (!tree_view->priv->column_drop_func)
4713 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4716 cur_column = left_column;
4717 tmp_list = tmp_list->prev;
4720 if (!tree_view->priv->column_drop_func)
4723 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4726 return (PsppSheetViewColumn *)0x1;
4730 /* same as DROP_HOME case, but doing it backwards */
4731 tmp_list = g_list_last (tree_view->priv->columns);
4734 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4735 return (PsppSheetViewColumn *)0x1;
4739 g_assert (tmp_list);
4741 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4743 if (left_column && !left_column->visible)
4745 cur_column = left_column;
4746 tmp_list = tmp_list->prev;
4749 if (!tree_view->priv->column_drop_func)
4752 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4755 cur_column = left_column;
4756 tmp_list = tmp_list->prev;
4759 if (!tree_view->priv->column_drop_func)
4762 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4765 return (PsppSheetViewColumn *)0x1;
4769 return (PsppSheetViewColumn *)0x1;
4773 pspp_sheet_view_key_press (GtkWidget *widget,
4776 PsppSheetView *tree_view = (PsppSheetView *) widget;
4778 if (tree_view->priv->rubber_band_status)
4780 if (event->keyval == GDK_Escape)
4781 pspp_sheet_view_stop_rubber_band (tree_view);
4786 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4788 if (event->keyval == GDK_Escape)
4790 tree_view->priv->cur_reorder = NULL;
4791 pspp_sheet_view_button_release_drag_column (widget, NULL);
4796 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4798 GList *focus_column;
4801 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4803 for (focus_column = tree_view->priv->columns;
4805 focus_column = focus_column->next)
4807 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4809 if (column->button && gtk_widget_has_focus (column->button))
4814 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4815 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4816 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4818 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4820 if (!column->resizable)
4822 gtk_widget_error_bell (widget);
4826 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4827 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4829 gint old_width = column->resized_width;
4831 column->resized_width = MAX (column->resized_width,
4833 column->resized_width -= 2;
4834 if (column->resized_width < 0)
4835 column->resized_width = 0;
4837 if (column->min_width == -1)
4838 column->resized_width = MAX (column->button_request,
4839 column->resized_width);
4841 column->resized_width = MAX (column->min_width,
4842 column->resized_width);
4844 if (column->max_width != -1)
4845 column->resized_width = MIN (column->resized_width,
4848 column->use_resized_width = TRUE;
4850 if (column->resized_width != old_width)
4851 gtk_widget_queue_resize (widget);
4853 gtk_widget_error_bell (widget);
4855 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4856 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4858 gint old_width = column->resized_width;
4860 column->resized_width = MAX (column->resized_width,
4862 column->resized_width += 2;
4864 if (column->max_width != -1)
4865 column->resized_width = MIN (column->resized_width,
4868 column->use_resized_width = TRUE;
4870 if (column->resized_width != old_width)
4871 gtk_widget_queue_resize (widget);
4873 gtk_widget_error_bell (widget);
4880 (event->state & GDK_MOD1_MASK) &&
4881 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4882 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4883 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4884 || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4886 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4888 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4889 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4891 PsppSheetViewColumn *col;
4892 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
4893 if (col != (PsppSheetViewColumn *)0x1)
4894 pspp_sheet_view_move_column_after (tree_view, column, col);
4896 gtk_widget_error_bell (widget);
4898 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4899 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4901 PsppSheetViewColumn *col;
4902 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4903 if (col != (PsppSheetViewColumn *)0x1)
4904 pspp_sheet_view_move_column_after (tree_view, column, col);
4906 gtk_widget_error_bell (widget);
4908 else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4910 PsppSheetViewColumn *col;
4911 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4912 if (col != (PsppSheetViewColumn *)0x1)
4913 pspp_sheet_view_move_column_after (tree_view, column, col);
4915 gtk_widget_error_bell (widget);
4917 else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4919 PsppSheetViewColumn *col;
4920 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4921 if (col != (PsppSheetViewColumn *)0x1)
4922 pspp_sheet_view_move_column_after (tree_view, column, col);
4924 gtk_widget_error_bell (widget);
4931 /* Chain up to the parent class. It handles the keybindings. */
4932 if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4935 if (tree_view->priv->search_entry_avoid_unhandled_binding)
4937 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4941 /* We pass the event to the search_entry. If its text changes, then we start
4942 * the typeahead find capabilities. */
4943 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4944 && tree_view->priv->enable_search
4945 && !tree_view->priv->search_custom_entry_set)
4947 GdkEvent *new_event;
4949 const char *new_text;
4952 gboolean text_modified;
4953 gulong popup_menu_id;
4955 pspp_sheet_view_ensure_interactive_directory (tree_view);
4957 /* Make a copy of the current text */
4958 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4959 new_event = gdk_event_copy ((GdkEvent *) event);
4960 g_object_unref (((GdkEventKey *) new_event)->window);
4961 ((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window));
4962 gtk_widget_realize (tree_view->priv->search_window);
4964 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
4965 "popup-menu", G_CALLBACK (gtk_true),
4968 /* Move the entry off screen */
4969 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4970 gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4971 gdk_screen_get_width (screen) + 1,
4972 gdk_screen_get_height (screen) + 1);
4973 gtk_widget_show (tree_view->priv->search_window);
4975 /* Send the event to the window. If the preedit_changed signal is emitted
4976 * during this event, we will set priv->imcontext_changed */
4977 tree_view->priv->imcontext_changed = FALSE;
4978 retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4979 gdk_event_free (new_event);
4980 gtk_widget_hide (tree_view->priv->search_window);
4982 g_signal_handler_disconnect (tree_view->priv->search_entry,
4985 /* We check to make sure that the entry tried to handle the text, and that
4986 * the text has changed.
4988 new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
4989 text_modified = strcmp (old_text, new_text) != 0;
4991 if (tree_view->priv->imcontext_changed || /* we're in a preedit */
4992 (retval && text_modified)) /* ...or the text was modified */
4994 if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
4996 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
5001 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
5011 pspp_sheet_view_key_release (GtkWidget *widget,
5014 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
5016 if (tree_view->priv->rubber_band_status)
5019 return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
5022 /* FIXME Is this function necessary? Can I get an enter_notify event
5023 * w/o either an expose event or a mouse motion event?
5026 pspp_sheet_view_enter_notify (GtkWidget *widget,
5027 GdkEventCrossing *event)
5029 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
5033 /* Sanity check it */
5034 if (event->window != tree_view->priv->bin_window)
5037 if (tree_view->priv->row_count == 0)
5040 if (event->mode == GDK_CROSSING_GRAB ||
5041 event->mode == GDK_CROSSING_GTK_GRAB ||
5042 event->mode == GDK_CROSSING_GTK_UNGRAB ||
5043 event->mode == GDK_CROSSING_STATE_CHANGED)
5046 /* find the node internally */
5047 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
5050 pspp_sheet_view_find_offset (tree_view, new_y, &node);
5052 tree_view->priv->event_last_x = event->x;
5053 tree_view->priv->event_last_y = event->y;
5055 prelight_or_select (tree_view, node, event->x, event->y);
5061 pspp_sheet_view_leave_notify (GtkWidget *widget,
5062 GdkEventCrossing *event)
5064 PsppSheetView *tree_view;
5066 if (event->mode == GDK_CROSSING_GRAB)
5069 tree_view = PSPP_SHEET_VIEW (widget);
5071 if (tree_view->priv->prelight_node >= 0)
5072 _pspp_sheet_view_queue_draw_node (tree_view,
5073 tree_view->priv->prelight_node,
5076 tree_view->priv->event_last_x = -10000;
5077 tree_view->priv->event_last_y = -10000;
5079 prelight_or_select (tree_view,
5081 -1000, -1000); /* coords not possibly over an arrow */
5088 pspp_sheet_view_focus_out (GtkWidget *widget,
5089 GdkEventFocus *event)
5091 PsppSheetView *tree_view;
5093 tree_view = PSPP_SHEET_VIEW (widget);
5095 gtk_widget_queue_draw (widget);
5097 /* destroy interactive search dialog */
5098 if (tree_view->priv->search_window)
5099 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
5105 /* Incremental Reflow
5109 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
5112 GtkAllocation allocation;
5113 gint y = pspp_sheet_view_node_find_offset (tree_view, node)
5114 - gtk_adjustment_get_value (tree_view->priv->vadjustment)
5115 + TREE_VIEW_HEADER_HEIGHT (tree_view);
5117 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5119 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
5122 tree_view->priv->fixed_height);
5126 node_is_visible (PsppSheetView *tree_view,
5132 y = pspp_sheet_view_node_find_offset (tree_view, node);
5133 height = ROW_HEIGHT (tree_view);
5135 if (y >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5136 y + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5137 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5143 /* Returns the row height. */
5145 validate_row (PsppSheetView *tree_view,
5150 PsppSheetViewColumn *column;
5151 GList *list, *first_column, *last_column;
5153 gint horizontal_separator;
5154 gint vertical_separator;
5155 gint focus_line_width;
5156 gboolean draw_vgrid_lines, draw_hgrid_lines;
5158 gint grid_line_width;
5159 gboolean wide_separators;
5160 gint separator_height;
5162 gtk_widget_style_get (GTK_WIDGET (tree_view),
5163 "focus-padding", &focus_pad,
5164 "focus-line-width", &focus_line_width,
5165 "horizontal-separator", &horizontal_separator,
5166 "vertical-separator", &vertical_separator,
5167 "grid-line-width", &grid_line_width,
5168 "wide-separators", &wide_separators,
5169 "separator-height", &separator_height,
5173 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5174 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5176 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5177 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5179 for (last_column = g_list_last (tree_view->priv->columns);
5180 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5181 last_column = last_column->prev)
5184 for (first_column = g_list_first (tree_view->priv->columns);
5185 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5186 first_column = first_column->next)
5189 for (list = tree_view->priv->columns; list; list = list->next)
5194 column = list->data;
5196 if (! column->visible)
5199 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5200 pspp_sheet_view_column_cell_get_size (column,
5202 &tmp_width, &tmp_height);
5204 tmp_height += vertical_separator;
5205 height = MAX (height, tmp_height);
5207 tmp_width = tmp_width + horizontal_separator;
5209 if (draw_vgrid_lines)
5211 if (list->data == first_column || list->data == last_column)
5212 tmp_width += grid_line_width / 2.0;
5214 tmp_width += grid_line_width;
5217 if (tmp_width > column->requested_width)
5218 column->requested_width = tmp_width;
5221 if (draw_hgrid_lines)
5222 height += grid_line_width;
5224 tree_view->priv->post_validation_flag = TRUE;
5230 validate_visible_area (PsppSheetView *tree_view)
5232 GtkTreePath *path = NULL;
5233 GtkTreePath *above_path = NULL;
5236 gboolean size_changed = FALSE;
5238 gint area_above = 0;
5239 gint area_below = 0;
5240 GtkAllocation allocation;
5242 if (tree_view->priv->row_count == 0)
5245 if (tree_view->priv->scroll_to_path == NULL)
5248 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5250 total_height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5252 if (total_height == 0)
5255 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5258 /* we are going to scroll, and will update dy */
5259 _pspp_sheet_view_find_node (tree_view, path, &node);
5260 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5262 if (tree_view->priv->scroll_to_use_align)
5264 gint height = ROW_HEIGHT (tree_view);
5265 area_above = (total_height - height) *
5266 tree_view->priv->scroll_to_row_align;
5267 area_below = total_height - area_above - height;
5268 area_above = MAX (area_above, 0);
5269 area_below = MAX (area_below, 0);
5274 * 1) row not visible
5278 gint height = ROW_HEIGHT (tree_view);
5280 dy = pspp_sheet_view_node_find_offset (tree_view, node);
5282 if (dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5283 dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5284 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5286 /* row visible: keep the row at the same position */
5287 area_above = dy - gtk_adjustment_get_value (tree_view->priv->vadjustment);
5288 area_below = (gtk_adjustment_get_value (tree_view->priv->vadjustment) +
5289 gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5294 /* row not visible */
5296 && dy + height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5298 /* row at the beginning -- fixed */
5300 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment)
5301 - area_above - height;
5303 else if (dy >= (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5304 gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5306 /* row at the end -- fixed */
5307 area_above = dy - (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5308 gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
5309 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) -
5310 area_above - height;
5314 area_above = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - height;
5320 /* row somewhere in the middle, bring it to the top
5324 area_below = total_height - height;
5330 /* the scroll to isn't valid; ignore it.
5333 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5334 tree_view->priv->scroll_to_path = NULL;
5338 above_path = gtk_tree_path_copy (path);
5340 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5341 * backwards is much slower then forward, as there is no iter_prev function.
5342 * We go forwards first in case we run out of tree. Then we go backwards to
5345 while (node >= 0 && area_below > 0)
5347 gboolean done = FALSE;
5350 node = pspp_sheet_view_node_next (tree_view, node);
5353 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5355 gtk_tree_path_next (path);
5358 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5368 area_below -= ROW_HEIGHT (tree_view);
5370 gtk_tree_path_free (path);
5372 /* If we ran out of tree, and have extra area_below left, we need to add it
5375 area_above += area_below;
5377 _pspp_sheet_view_find_node (tree_view, above_path, &node);
5379 /* We walk backwards */
5380 while (area_above > 0)
5382 node = pspp_sheet_view_node_prev (tree_view, node);
5384 /* Always find the new path in the tree. We cannot just assume
5385 * a gtk_tree_path_prev() is enough here, as there might be children
5386 * in between this node and the previous sibling node. If this
5387 * appears to be a performance hotspot in profiles, we can look into
5388 * intrigate logic for keeping path, node and iter in sync like
5389 * we do for forward walks. (Which will be hard because of the lacking
5396 gtk_tree_path_free (above_path);
5397 above_path = _pspp_sheet_view_find_path (tree_view, node);
5399 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5401 area_above -= ROW_HEIGHT (tree_view);
5404 /* set the dy here to scroll to the path,
5405 * and sync the top row accordingly
5407 pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5408 pspp_sheet_view_top_row_to_dy (tree_view);
5410 /* update width/height and queue a resize */
5413 GtkRequisition requisition;
5415 /* We temporarily guess a size, under the assumption that it will be the
5416 * same when we get our next size_allocate. If we don't do this, we'll be
5417 * in an inconsistent state if we call top_row_to_dy. */
5419 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5420 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5421 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5422 gtk_adjustment_changed (tree_view->priv->hadjustment);
5423 gtk_adjustment_changed (tree_view->priv->vadjustment);
5424 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5427 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5428 tree_view->priv->scroll_to_path = NULL;
5431 gtk_tree_path_free (above_path);
5433 if (tree_view->priv->scroll_to_column)
5435 tree_view->priv->scroll_to_column = NULL;
5437 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5441 initialize_fixed_height_mode (PsppSheetView *tree_view)
5443 if (!tree_view->priv->row_count)
5446 if (tree_view->priv->fixed_height_set)
5449 if (tree_view->priv->fixed_height < 0)
5456 path = _pspp_sheet_view_find_path (tree_view, node);
5457 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5459 tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5461 gtk_tree_path_free (path);
5463 g_object_notify (G_OBJECT (tree_view), "fixed-height");
5467 /* Our strategy for finding nodes to validate is a little convoluted. We find
5468 * the left-most uninvalidated node. We then try walking right, validating
5469 * nodes. Once we find a valid node, we repeat the previous process of finding
5470 * the first invalid node.
5474 validate_rows_handler (PsppSheetView *tree_view)
5476 initialize_fixed_height_mode (tree_view);
5477 if (tree_view->priv->validate_rows_timer)
5479 g_source_remove (tree_view->priv->validate_rows_timer);
5480 tree_view->priv->validate_rows_timer = 0;
5487 do_presize_handler (PsppSheetView *tree_view)
5489 GtkRequisition requisition;
5491 validate_visible_area (tree_view);
5492 tree_view->priv->presize_handler_timer = 0;
5494 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5497 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5499 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5500 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5501 gtk_adjustment_changed (tree_view->priv->hadjustment);
5502 gtk_adjustment_changed (tree_view->priv->vadjustment);
5503 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5509 presize_handler_callback (gpointer data)
5511 do_presize_handler (PSPP_SHEET_VIEW (data));
5517 install_presize_handler (PsppSheetView *tree_view)
5519 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5522 if (! tree_view->priv->presize_handler_timer)
5524 tree_view->priv->presize_handler_timer =
5525 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5527 if (! tree_view->priv->validate_rows_timer)
5529 tree_view->priv->validate_rows_timer =
5530 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5535 scroll_sync_handler (PsppSheetView *tree_view)
5537 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5538 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5539 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5540 pspp_sheet_view_top_row_to_dy (tree_view);
5542 pspp_sheet_view_dy_to_top_row (tree_view);
5544 tree_view->priv->scroll_sync_timer = 0;
5550 install_scroll_sync_handler (PsppSheetView *tree_view)
5552 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5555 if (!tree_view->priv->scroll_sync_timer)
5557 tree_view->priv->scroll_sync_timer =
5558 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5563 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5567 gtk_tree_row_reference_free (tree_view->priv->top_row);
5571 tree_view->priv->top_row = NULL;
5572 tree_view->priv->top_row_dy = 0;
5576 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5577 tree_view->priv->top_row_dy = offset;
5581 /* Always call this iff dy is in the visible range. If the tree is empty, then
5582 * it's set to be NULL, and top_row_dy is 0;
5585 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5591 if (tree_view->priv->row_count == 0)
5593 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5597 offset = pspp_sheet_view_find_offset (tree_view,
5598 tree_view->priv->dy,
5603 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5607 path = _pspp_sheet_view_find_path (tree_view, node);
5608 pspp_sheet_view_set_top_row (tree_view, path, offset);
5609 gtk_tree_path_free (path);
5615 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5621 /* Avoid recursive calls */
5622 if (tree_view->priv->in_top_row_to_dy)
5625 if (tree_view->priv->top_row)
5626 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5633 _pspp_sheet_view_find_node (tree_view, path, &node);
5636 gtk_tree_path_free (path);
5640 /* keep dy and set new toprow */
5641 gtk_tree_row_reference_free (tree_view->priv->top_row);
5642 tree_view->priv->top_row = NULL;
5643 tree_view->priv->top_row_dy = 0;
5644 /* DO NOT install the idle handler */
5645 pspp_sheet_view_dy_to_top_row (tree_view);
5649 if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5651 /* new top row -- do NOT install the idle handler */
5652 pspp_sheet_view_dy_to_top_row (tree_view);
5656 new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5657 new_dy += tree_view->priv->top_row_dy;
5659 if (new_dy + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
5660 new_dy = tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
5662 new_dy = MAX (0, new_dy);
5664 tree_view->priv->in_top_row_to_dy = TRUE;
5665 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5666 tree_view->priv->in_top_row_to_dy = FALSE;
5671 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5673 install_presize_handler (tree_view);
5679 set_source_row (GdkDragContext *context,
5680 GtkTreeModel *model,
5681 GtkTreePath *source_row)
5683 g_object_set_data_full (G_OBJECT (context),
5684 "gtk-tree-view-source-row",
5685 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5686 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5690 get_source_row (GdkDragContext *context)
5692 GtkTreeRowReference *ref =
5693 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5696 return gtk_tree_row_reference_get_path (ref);
5703 GtkTreeRowReference *dest_row;
5704 guint path_down_mode : 1;
5705 guint empty_view_drop : 1;
5706 guint drop_append_mode : 1;
5711 dest_row_free (gpointer data)
5713 DestRow *dr = (DestRow *)data;
5715 gtk_tree_row_reference_free (dr->dest_row);
5716 g_slice_free (DestRow, dr);
5720 set_dest_row (GdkDragContext *context,
5721 GtkTreeModel *model,
5722 GtkTreePath *dest_row,
5723 gboolean path_down_mode,
5724 gboolean empty_view_drop,
5725 gboolean drop_append_mode)
5731 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5736 dr = g_slice_new (DestRow);
5738 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5739 dr->path_down_mode = path_down_mode != FALSE;
5740 dr->empty_view_drop = empty_view_drop != FALSE;
5741 dr->drop_append_mode = drop_append_mode != FALSE;
5743 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5744 dr, (GDestroyNotify) dest_row_free);
5748 get_dest_row (GdkDragContext *context,
5749 gboolean *path_down_mode)
5752 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5756 GtkTreePath *path = NULL;
5759 *path_down_mode = dr->path_down_mode;
5762 path = gtk_tree_row_reference_get_path (dr->dest_row);
5763 else if (dr->empty_view_drop)
5764 path = gtk_tree_path_new_from_indices (0, -1);
5768 if (path && dr->drop_append_mode)
5769 gtk_tree_path_next (path);
5777 /* Get/set whether drag_motion requested the drag data and
5778 * drag_data_received should thus not actually insert the data,
5779 * since the data doesn't result from a drop.
5782 set_status_pending (GdkDragContext *context,
5783 GdkDragAction suggested_action)
5785 g_object_set_data (G_OBJECT (context),
5786 "gtk-tree-view-status-pending",
5787 GINT_TO_POINTER (suggested_action));
5790 static GdkDragAction
5791 get_status_pending (GdkDragContext *context)
5793 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5794 "gtk-tree-view-status-pending"));
5797 static TreeViewDragInfo*
5798 get_info (PsppSheetView *tree_view)
5800 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5804 destroy_info (TreeViewDragInfo *di)
5806 g_slice_free (TreeViewDragInfo, di);
5809 static TreeViewDragInfo*
5810 ensure_info (PsppSheetView *tree_view)
5812 TreeViewDragInfo *di;
5814 di = get_info (tree_view);
5818 di = g_slice_new0 (TreeViewDragInfo);
5820 g_object_set_data_full (G_OBJECT (tree_view),
5821 "gtk-tree-view-drag-info",
5823 (GDestroyNotify) destroy_info);
5830 remove_info (PsppSheetView *tree_view)
5832 g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5837 drag_scan_timeout (gpointer data)
5839 PsppSheetView *tree_view;
5841 GdkModifierType state;
5842 GtkTreePath *path = NULL;
5843 PsppSheetViewColumn *column = NULL;
5844 GdkRectangle visible_rect;
5846 GDK_THREADS_ENTER ();
5848 tree_view = PSPP_SHEET_VIEW (data);
5850 gdk_window_get_pointer (tree_view->priv->bin_window,
5853 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5855 /* See if we are near the edge. */
5856 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5857 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5858 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5859 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5861 pspp_sheet_view_get_path_at_pos (tree_view,
5862 tree_view->priv->bin_window,
5871 pspp_sheet_view_scroll_to_cell (tree_view,
5877 gtk_tree_path_free (path);
5881 GDK_THREADS_LEAVE ();
5888 add_scroll_timeout (PsppSheetView *tree_view)
5890 if (tree_view->priv->scroll_timeout == 0)
5892 tree_view->priv->scroll_timeout =
5893 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5898 remove_scroll_timeout (PsppSheetView *tree_view)
5900 if (tree_view->priv->scroll_timeout != 0)
5902 g_source_remove (tree_view->priv->scroll_timeout);
5903 tree_view->priv->scroll_timeout = 0;
5908 check_model_dnd (GtkTreeModel *model,
5909 GType required_iface,
5910 const gchar *signal)
5912 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5914 g_warning ("You must override the default '%s' handler "
5915 "on PsppSheetView when using models that don't support "
5916 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5917 "is to connect to '%s' and call "
5918 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5919 "the default handler from running. Look at the source code "
5920 "for the default handler in gtktreeview.c to get an idea what "
5921 "your handler should do. (gtktreeview.c is in the GTK source "
5922 "code.) If you're using GTK from a language other than C, "
5923 "there may be a more natural way to override default handlers, e.g. via derivation.",
5924 signal, g_type_name (required_iface), signal);
5932 scroll_row_timeout (gpointer data)
5934 PsppSheetView *tree_view = data;
5936 pspp_sheet_view_horizontal_autoscroll (tree_view);
5937 pspp_sheet_view_vertical_autoscroll (tree_view);
5939 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5940 pspp_sheet_view_update_rubber_band (tree_view);
5945 /* Returns TRUE if event should not be propagated to parent widgets */
5947 set_destination_row (PsppSheetView *tree_view,
5948 GdkDragContext *context,
5949 /* coordinates relative to the widget */
5952 GdkDragAction *suggested_action,
5955 GtkTreePath *path = NULL;
5956 PsppSheetViewDropPosition pos;
5957 PsppSheetViewDropPosition old_pos;
5958 TreeViewDragInfo *di;
5960 GtkTreePath *old_dest_path = NULL;
5961 gboolean can_drop = FALSE;
5963 *suggested_action = 0;
5966 widget = GTK_WIDGET (tree_view);
5968 di = get_info (tree_view);
5970 if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5972 /* someone unset us as a drag dest, note that if
5973 * we return FALSE drag_leave isn't called
5976 pspp_sheet_view_set_drag_dest_row (tree_view,
5978 PSPP_SHEET_VIEW_DROP_BEFORE);
5980 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5982 return FALSE; /* no longer a drop site */
5985 *target = gtk_drag_dest_find_target (widget, context,
5986 gtk_drag_dest_get_target_list (widget));
5987 if (*target == GDK_NONE)
5992 if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
5998 GtkTreeModel *model;
6000 /* the row got dropped on empty space, let's setup a special case
6004 gtk_tree_path_free (path);
6006 model = pspp_sheet_view_get_model (tree_view);
6008 n_children = gtk_tree_model_iter_n_children (model, NULL);
6011 pos = PSPP_SHEET_VIEW_DROP_AFTER;
6012 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
6016 pos = PSPP_SHEET_VIEW_DROP_BEFORE;
6017 path = gtk_tree_path_new_from_indices (0, -1);
6027 /* If we left the current row's "open" zone, unset the timeout for
6030 pspp_sheet_view_get_drag_dest_row (tree_view,
6035 gtk_tree_path_free (old_dest_path);
6037 if (TRUE /* FIXME if the location droppable predicate */)
6045 GtkWidget *source_widget;
6047 *suggested_action = gdk_drag_context_get_suggested_action (context);
6048 source_widget = gtk_drag_get_source_widget (context);
6050 if (source_widget == widget)
6052 /* Default to MOVE, unless the user has
6053 * pressed ctrl or shift to affect available actions
6055 if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
6056 *suggested_action = GDK_ACTION_MOVE;
6059 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6064 /* can't drop here */
6065 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6067 PSPP_SHEET_VIEW_DROP_BEFORE);
6071 gtk_tree_path_free (path);
6077 get_logical_dest_row (PsppSheetView *tree_view,
6078 gboolean *path_down_mode,
6079 gboolean *drop_append_mode)
6081 /* adjust path to point to the row the drop goes in front of */
6082 GtkTreePath *path = NULL;
6083 PsppSheetViewDropPosition pos;
6085 g_return_val_if_fail (path_down_mode != NULL, NULL);
6086 g_return_val_if_fail (drop_append_mode != NULL, NULL);
6088 *path_down_mode = FALSE;
6089 *drop_append_mode = 0;
6091 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6096 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
6098 else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
6099 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
6100 *path_down_mode = TRUE;
6104 GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
6106 g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
6108 if (!gtk_tree_model_get_iter (model, &iter, path) ||
6109 !gtk_tree_model_iter_next (model, &iter))
6110 *drop_append_mode = 1;
6113 *drop_append_mode = 0;
6114 gtk_tree_path_next (path);
6122 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
6123 GdkEventMotion *event)
6125 GtkWidget *widget = GTK_WIDGET (tree_view);
6126 GdkDragContext *context;
6127 TreeViewDragInfo *di;
6128 GtkTreePath *path = NULL;
6130 gint cell_x, cell_y;
6131 GtkTreeModel *model;
6132 gboolean retval = FALSE;
6134 di = get_info (tree_view);
6136 if (di == NULL || !di->source_set)
6139 if (tree_view->priv->pressed_button < 0)
6142 if (!gtk_drag_check_threshold (widget,
6143 tree_view->priv->press_start_x,
6144 tree_view->priv->press_start_y,
6145 event->x, event->y))
6148 model = pspp_sheet_view_get_model (tree_view);
6153 button = tree_view->priv->pressed_button;
6154 tree_view->priv->pressed_button = -1;
6156 pspp_sheet_view_get_path_at_pos (tree_view,
6157 tree_view->priv->press_start_x,
6158 tree_view->priv->press_start_y,
6167 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6168 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6172 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6175 /* Now we can begin the drag */
6179 context = gtk_drag_begin (widget,
6180 gtk_drag_source_get_target_list (widget),
6185 set_source_row (context, model, path);
6189 gtk_tree_path_free (path);
6197 pspp_sheet_view_drag_begin (GtkWidget *widget,
6198 GdkDragContext *context)
6201 PsppSheetView *tree_view;
6202 GtkTreePath *path = NULL;
6203 gint cell_x, cell_y;
6205 TreeViewDragInfo *di;
6207 tree_view = PSPP_SHEET_VIEW (widget);
6209 /* if the user uses a custom DND source impl, we don't set the icon here */
6210 di = get_info (tree_view);
6212 if (di == NULL || !di->source_set)
6215 pspp_sheet_view_get_path_at_pos (tree_view,
6216 tree_view->priv->press_start_x,
6217 tree_view->priv->press_start_y,
6223 g_return_if_fail (path != NULL);
6225 row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6228 gtk_drag_set_icon_pixmap (context,
6229 gdk_drawable_get_colormap (row_pix),
6232 /* the + 1 is for the black border in the icon */
6233 tree_view->priv->press_start_x + 1,
6236 g_object_unref (row_pix);
6237 gtk_tree_path_free (path);
6243 pspp_sheet_view_drag_end (GtkWidget *widget,
6244 GdkDragContext *context)
6249 /* Default signal implementations for the drag signals */
6251 pspp_sheet_view_drag_data_get (GtkWidget *widget,
6252 GdkDragContext *context,
6253 GtkSelectionData *selection_data,
6257 PsppSheetView *tree_view;
6258 GtkTreeModel *model;
6259 TreeViewDragInfo *di;
6260 GtkTreePath *source_row;
6262 tree_view = PSPP_SHEET_VIEW (widget);
6264 model = pspp_sheet_view_get_model (tree_view);
6269 di = get_info (PSPP_SHEET_VIEW (widget));
6274 source_row = get_source_row (context);
6276 if (source_row == NULL)
6279 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6280 * any model; for DragSource models there are some other targets
6284 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6285 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6290 /* If drag_data_get does nothing, try providing row data. */
6291 if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6293 gtk_tree_set_row_drag_data (selection_data,
6299 gtk_tree_path_free (source_row);
6304 pspp_sheet_view_drag_data_delete (GtkWidget *widget,
6305 GdkDragContext *context)
6307 TreeViewDragInfo *di;
6308 GtkTreeModel *model;
6309 PsppSheetView *tree_view;
6310 GtkTreePath *source_row;
6312 tree_view = PSPP_SHEET_VIEW (widget);
6313 model = pspp_sheet_view_get_model (tree_view);
6315 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6318 di = get_info (tree_view);
6323 source_row = get_source_row (context);
6325 if (source_row == NULL)
6328 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6331 gtk_tree_path_free (source_row);
6333 set_source_row (context, NULL, NULL);
6337 pspp_sheet_view_drag_leave (GtkWidget *widget,
6338 GdkDragContext *context,
6341 /* unset any highlight row */
6342 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6344 PSPP_SHEET_VIEW_DROP_BEFORE);
6346 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6351 pspp_sheet_view_drag_motion (GtkWidget *widget,
6352 GdkDragContext *context,
6353 /* coordinates relative to the widget */
6359 GtkTreePath *path = NULL;
6360 PsppSheetViewDropPosition pos;
6361 PsppSheetView *tree_view;
6362 GdkDragAction suggested_action = 0;
6365 tree_view = PSPP_SHEET_VIEW (widget);
6367 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6370 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6372 /* we only know this *after* set_desination_row */
6373 empty = tree_view->priv->empty_view_drop;
6375 if (path == NULL && !empty)
6377 /* Can't drop here. */
6378 gdk_drag_status (context, 0, time);
6382 if (tree_view->priv->open_dest_timeout == 0 &&
6383 (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6384 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6390 add_scroll_timeout (tree_view);
6393 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6395 /* Request data so we can use the source row when
6396 * determining whether to accept the drop
6398 set_status_pending (context, suggested_action);
6399 gtk_drag_get_data (widget, context, target, time);
6403 set_status_pending (context, 0);
6404 gdk_drag_status (context, suggested_action, time);
6409 gtk_tree_path_free (path);
6416 pspp_sheet_view_drag_drop (GtkWidget *widget,
6417 GdkDragContext *context,
6418 /* coordinates relative to the widget */
6423 PsppSheetView *tree_view;
6425 GdkDragAction suggested_action = 0;
6426 GdkAtom target = GDK_NONE;
6427 TreeViewDragInfo *di;
6428 GtkTreeModel *model;
6429 gboolean path_down_mode;
6430 gboolean drop_append_mode;
6432 tree_view = PSPP_SHEET_VIEW (widget);
6434 model = pspp_sheet_view_get_model (tree_view);
6436 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6438 di = get_info (tree_view);
6443 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6446 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6449 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6451 if (target != GDK_NONE && path != NULL)
6453 /* in case a motion had requested drag data, change things so we
6454 * treat drag data receives as a drop.
6456 set_status_pending (context, 0);
6457 set_dest_row (context, model, path,
6458 path_down_mode, tree_view->priv->empty_view_drop,
6463 gtk_tree_path_free (path);
6465 /* Unset this thing */
6466 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6468 PSPP_SHEET_VIEW_DROP_BEFORE);
6470 if (target != GDK_NONE)
6472 gtk_drag_get_data (widget, context, target, time);
6480 pspp_sheet_view_drag_data_received (GtkWidget *widget,
6481 GdkDragContext *context,
6482 /* coordinates relative to the widget */
6485 GtkSelectionData *selection_data,
6490 TreeViewDragInfo *di;
6491 gboolean accepted = FALSE;
6492 GtkTreeModel *model;
6493 PsppSheetView *tree_view;
6494 GtkTreePath *dest_row;
6495 GdkDragAction suggested_action;
6496 gboolean path_down_mode;
6497 gboolean drop_append_mode;
6499 tree_view = PSPP_SHEET_VIEW (widget);
6501 model = pspp_sheet_view_get_model (tree_view);
6503 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6506 di = get_info (tree_view);
6511 suggested_action = get_status_pending (context);
6513 if (suggested_action)
6515 /* We are getting this data due to a request in drag_motion,
6516 * rather than due to a request in drag_drop, so we are just
6517 * supposed to call drag_status, not actually paste in the
6520 path = get_logical_dest_row (tree_view, &path_down_mode,
6524 suggested_action = 0;
6525 else if (path_down_mode)
6526 gtk_tree_path_down (path);
6528 if (suggested_action)
6530 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6536 path_down_mode = FALSE;
6537 gtk_tree_path_up (path);
6539 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6542 suggested_action = 0;
6545 suggested_action = 0;
6549 gdk_drag_status (context, suggested_action, time);
6552 gtk_tree_path_free (path);
6554 /* If you can't drop, remove user drop indicator until the next motion */
6555 if (suggested_action == 0)
6556 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6558 PSPP_SHEET_VIEW_DROP_BEFORE);
6563 dest_row = get_dest_row (context, &path_down_mode);
6565 if (dest_row == NULL)
6568 if (gtk_selection_data_get_length (selection_data) >= 0)
6572 gtk_tree_path_down (dest_row);
6573 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6574 dest_row, selection_data))
6575 gtk_tree_path_up (dest_row);
6579 if (gtk_selection_data_get_length (selection_data) >= 0)
6581 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6587 gtk_drag_finish (context,
6589 (gdk_drag_context_get_actions (context) == GDK_ACTION_MOVE),
6592 if (gtk_tree_path_get_depth (dest_row) == 1
6593 && gtk_tree_path_get_indices (dest_row)[0] == 0)
6595 /* special special case drag to "0", scroll to first item */
6596 if (!tree_view->priv->scroll_to_path)
6597 pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6600 gtk_tree_path_free (dest_row);
6603 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6608 /* GtkContainer Methods
6613 pspp_sheet_view_remove (GtkContainer *container,
6616 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6617 PsppSheetViewChild *child = NULL;
6620 tmp_list = tree_view->priv->children;
6623 child = tmp_list->data;
6624 if (child->widget == widget)
6626 gtk_widget_unparent (widget);
6628 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6629 g_list_free_1 (tmp_list);
6630 g_slice_free (PsppSheetViewChild, child);
6634 tmp_list = tmp_list->next;
6637 tmp_list = tree_view->priv->columns;
6641 PsppSheetViewColumn *column;
6642 column = tmp_list->data;
6643 if (column->button == widget)
6645 gtk_widget_unparent (widget);
6648 tmp_list = tmp_list->next;
6653 pspp_sheet_view_forall (GtkContainer *container,
6654 gboolean include_internals,
6655 GtkCallback callback,
6656 gpointer callback_data)
6658 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6659 PsppSheetViewChild *child = NULL;
6660 PsppSheetViewColumn *column;
6663 tmp_list = tree_view->priv->children;
6666 child = tmp_list->data;
6667 tmp_list = tmp_list->next;
6669 (* callback) (child->widget, callback_data);
6671 if (include_internals == FALSE)
6674 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6676 column = tmp_list->data;
6679 (* callback) (column->button, callback_data);
6683 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6684 * cells. If so we draw one big row-spanning focus rectangle.
6687 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6691 if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6692 return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6694 for (list = tree_view->priv->columns; list; list = list->next)
6696 if (!((PsppSheetViewColumn *)list->data)->visible)
6698 if (_pspp_sheet_view_column_count_special_cells (list->data))
6706 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6707 PsppSheetViewColumn *focus_column,
6708 gboolean clamp_column_visible)
6710 g_return_if_fail (focus_column != NULL);
6712 tree_view->priv->focus_column = focus_column;
6714 if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view)) != focus_column->button)
6715 gtk_widget_grab_focus (focus_column->button);
6717 if (clamp_column_visible)
6718 pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6721 /* Returns TRUE if the focus is within the headers, after the focus operation is
6725 pspp_sheet_view_header_focus (PsppSheetView *tree_view,
6726 GtkDirectionType dir,
6727 gboolean clamp_column_visible)
6729 GtkWidget *focus_child;
6730 PsppSheetViewColumn *focus_column;
6731 GList *last_column, *first_column;
6735 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6738 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
6740 first_column = tree_view->priv->columns;
6741 while (first_column)
6743 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6745 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6747 first_column = first_column->next;
6750 /* No headers are visible, or are focusable. We can't focus in or out.
6752 if (first_column == NULL)
6755 last_column = g_list_last (tree_view->priv->columns);
6758 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6760 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6762 last_column = last_column->prev;
6766 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6770 case GTK_DIR_TAB_BACKWARD:
6771 case GTK_DIR_TAB_FORWARD:
6774 if (focus_child == NULL)
6776 if (tree_view->priv->focus_column != NULL &&
6777 pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6778 focus_column = tree_view->priv->focus_column;
6780 focus_column = first_column->data;
6781 pspp_sheet_view_focus_column (tree_view, focus_column,
6782 clamp_column_visible);
6789 if (focus_child == NULL)
6791 if (tree_view->priv->focus_column != NULL)
6792 focus_column = tree_view->priv->focus_column;
6793 else if (dir == GTK_DIR_LEFT)
6794 focus_column = last_column->data;
6796 focus_column = first_column->data;
6797 pspp_sheet_view_focus_column (tree_view, focus_column,
6798 clamp_column_visible);
6802 if (gtk_widget_child_focus (focus_child, dir))
6804 /* The focus moves inside the button. */
6805 /* This is probably a great example of bad UI */
6806 if (clamp_column_visible)
6807 pspp_sheet_view_clamp_column_visible (tree_view,
6808 tree_view->priv->focus_column,
6813 /* We need to move the focus among the row of buttons. */
6814 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6815 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6818 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6819 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6821 gtk_widget_error_bell (GTK_WIDGET (tree_view));
6827 PsppSheetViewColumn *column;
6829 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6830 tmp_list = tmp_list->next;
6832 tmp_list = tmp_list->prev;
6834 if (tmp_list == NULL)
6836 g_warning ("Internal button not found");
6839 column = tmp_list->data;
6840 if (column->visible &&
6841 pspp_sheet_view_column_can_focus (column))
6845 pspp_sheet_view_focus_column (tree_view, column,
6846 clamp_column_visible);
6854 g_assert_not_reached ();
6861 /* This function returns in 'path' the first focusable path, if the given path
6862 * is already focusable, it's the returned one.
6866 search_first_focusable_path (PsppSheetView *tree_view,
6868 gboolean search_forward,
6871 /* XXX this function is trivial given that the sheetview doesn't support
6875 if (!path || !*path)
6878 _pspp_sheet_view_find_node (tree_view, *path, &node);
6886 return (*path != NULL);
6890 pspp_sheet_view_focus (GtkWidget *widget,
6891 GtkDirectionType direction)
6893 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6894 GtkContainer *container = GTK_CONTAINER (widget);
6895 GtkWidget *focus_child;
6897 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6900 focus_child = gtk_container_get_focus_child (container);
6902 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6903 /* Case 1. Headers currently have focus. */
6910 pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6912 case GTK_DIR_TAB_BACKWARD:
6915 case GTK_DIR_TAB_FORWARD:
6917 gtk_widget_grab_focus (widget);
6920 g_assert_not_reached ();
6925 /* Case 2. We don't have focus at all. */
6926 if (!gtk_widget_has_focus (widget))
6928 if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6929 gtk_widget_grab_focus (widget);
6933 /* Case 3. We have focus already. */
6934 if (direction == GTK_DIR_TAB_BACKWARD)
6935 return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6936 else if (direction == GTK_DIR_TAB_FORWARD)
6939 /* Other directions caught by the keybindings */
6940 gtk_widget_grab_focus (widget);
6945 pspp_sheet_view_grab_focus (GtkWidget *widget)
6947 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6949 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6953 pspp_sheet_view_style_updated (GtkWidget *widget)
6955 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6957 PsppSheetViewColumn *column;
6958 GtkStyleContext *context;
6960 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->style_updated (widget);
6962 if (gtk_widget_get_realized (widget))
6964 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
6965 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
6966 gtk_style_context_set_background (context, tree_view->priv->header_window);
6967 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6970 gtk_widget_style_get (widget,
6971 "expander-size", &tree_view->priv->expander_size,
6973 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6975 for (list = tree_view->priv->columns; list; list = list->next)
6977 column = list->data;
6978 _pspp_sheet_view_column_cell_set_dirty (column);
6981 tree_view->priv->fixed_height = -1;
6983 /* Invalidate cached button style. */
6984 if (tree_view->priv->button_style)
6986 g_object_unref (tree_view->priv->button_style);
6987 tree_view->priv->button_style = NULL;
6990 gtk_widget_queue_resize (widget);
6995 pspp_sheet_view_set_focus_child (GtkContainer *container,
6998 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
7001 for (list = tree_view->priv->columns; list; list = list->next)
7003 if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
7005 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7010 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
7014 pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
7015 GtkAdjustment *hadj,
7016 GtkAdjustment *vadj)
7018 gboolean need_adjust = FALSE;
7020 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7023 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
7025 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7027 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
7029 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7031 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
7033 g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
7034 pspp_sheet_view_adjustment_changed,
7036 g_object_unref (tree_view->priv->hadjustment);
7039 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
7041 g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
7042 pspp_sheet_view_adjustment_changed,
7044 g_object_unref (tree_view->priv->vadjustment);
7047 if (tree_view->priv->hadjustment != hadj)
7049 tree_view->priv->hadjustment = hadj;
7050 g_object_ref_sink (tree_view->priv->hadjustment);
7052 g_signal_connect (tree_view->priv->hadjustment, "value-changed",
7053 G_CALLBACK (pspp_sheet_view_adjustment_changed),
7058 if (tree_view->priv->vadjustment != vadj)
7060 tree_view->priv->vadjustment = vadj;
7061 g_object_ref_sink (tree_view->priv->vadjustment);
7063 g_signal_connect (tree_view->priv->vadjustment, "value-changed",
7064 G_CALLBACK (pspp_sheet_view_adjustment_changed),
7070 pspp_sheet_view_adjustment_changed (NULL, tree_view);
7075 pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
7076 GtkMovementStep step,
7079 PsppSheetSelectMode mode;
7080 GdkModifierType state;
7082 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
7083 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
7084 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
7085 step == GTK_MOVEMENT_DISPLAY_LINES ||
7086 step == GTK_MOVEMENT_PAGES ||
7087 step == GTK_MOVEMENT_BUFFER_ENDS ||
7088 step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
7090 if (tree_view->priv->row_count == 0)
7092 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7095 pspp_sheet_view_stop_editing (tree_view, FALSE);
7096 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7097 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7100 if (gtk_get_current_event_state (&state))
7102 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7103 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
7104 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
7105 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
7107 /* else we assume not pressed */
7111 case GTK_MOVEMENT_LOGICAL_POSITIONS:
7112 pspp_sheet_view_move_cursor_tab (tree_view, count);
7114 case GTK_MOVEMENT_VISUAL_POSITIONS:
7115 pspp_sheet_view_move_cursor_left_right (tree_view, count, mode);
7117 case GTK_MOVEMENT_DISPLAY_LINES:
7118 pspp_sheet_view_move_cursor_up_down (tree_view, count, mode);
7120 case GTK_MOVEMENT_PAGES:
7121 pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode);
7123 case GTK_MOVEMENT_BUFFER_ENDS:
7124 pspp_sheet_view_move_cursor_start_end (tree_view, count, mode);
7126 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7127 pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode);
7130 g_assert_not_reached ();
7137 pspp_sheet_view_put (PsppSheetView *tree_view,
7138 GtkWidget *child_widget,
7139 /* in bin_window coordinates */
7145 PsppSheetViewChild *child;
7147 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7148 g_return_if_fail (GTK_IS_WIDGET (child_widget));
7150 child = g_slice_new (PsppSheetViewChild);
7152 child->widget = child_widget;
7155 child->width = width;
7156 child->height = height;
7158 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7160 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7161 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7163 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7167 _pspp_sheet_view_child_move_resize (PsppSheetView *tree_view,
7169 /* in tree coordinates */
7175 PsppSheetViewChild *child = NULL;
7177 GdkRectangle allocation;
7179 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7180 g_return_if_fail (GTK_IS_WIDGET (widget));
7182 for (list = tree_view->priv->children; list; list = list->next)
7184 if (((PsppSheetViewChild *)list->data)->widget == widget)
7193 allocation.x = child->x = x;
7194 allocation.y = child->y = y;
7195 allocation.width = child->width = width;
7196 allocation.height = child->height = height;
7198 if (gtk_widget_get_realized (widget))
7199 gtk_widget_size_allocate (widget, &allocation);
7203 /* TreeModel Callbacks
7207 pspp_sheet_view_row_changed (GtkTreeModel *model,
7212 PsppSheetView *tree_view = (PsppSheetView *)data;
7214 gboolean free_path = FALSE;
7215 GtkTreePath *cursor_path;
7217 g_return_if_fail (path != NULL || iter != NULL);
7219 if (tree_view->priv->cursor != NULL)
7220 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7224 if (tree_view->priv->edited_column &&
7225 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7226 pspp_sheet_view_stop_editing (tree_view, TRUE);
7228 if (cursor_path != NULL)
7229 gtk_tree_path_free (cursor_path);
7233 path = gtk_tree_model_get_path (model, iter);
7236 else if (iter == NULL)
7237 gtk_tree_model_get_iter (model, iter, path);
7239 _pspp_sheet_view_find_node (tree_view,
7245 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7246 pspp_sheet_view_node_queue_redraw (tree_view, node);
7250 gtk_tree_path_free (path);
7254 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7259 PsppSheetView *tree_view = (PsppSheetView *) data;
7262 gint height = tree_view->priv->fixed_height;
7263 gboolean free_path = FALSE;
7264 gboolean node_visible = TRUE;
7266 g_return_if_fail (path != NULL || iter != NULL);
7270 path = gtk_tree_model_get_path (model, iter);
7273 else if (iter == NULL)
7274 gtk_tree_model_get_iter (model, iter, path);
7276 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7278 /* Update all row-references */
7279 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7280 indices = gtk_tree_path_get_indices (path);
7281 tmpnode = indices[0];
7283 range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7287 if (node_visible && node_is_visible (tree_view, tmpnode))
7288 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7290 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7293 install_presize_handler (tree_view);
7295 gtk_tree_path_free (path);
7299 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7303 PsppSheetView *tree_view = (PsppSheetView *)data;
7306 g_return_if_fail (path != NULL);
7308 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7310 _pspp_sheet_view_find_node (tree_view, path, &node);
7315 range_tower_delete (tree_view->priv->selected, node, 1);
7317 /* Ensure we don't have a dangling pointer to a dead node */
7318 ensure_unprelighted (tree_view);
7320 /* Cancel editting if we've started */
7321 pspp_sheet_view_stop_editing (tree_view, TRUE);
7323 if (tree_view->priv->destroy_count_func)
7325 gint child_count = 0;
7326 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7329 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7331 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7333 gtk_tree_row_reference_free (tree_view->priv->top_row);
7334 tree_view->priv->top_row = NULL;
7337 install_scroll_sync_handler (tree_view);
7339 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7342 if (helper_data.changed)
7343 g_signal_emit_by_name (tree_view->priv->selection, "changed");
7348 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7349 GtkTreePath *parent,
7354 PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7357 /* XXX need to adjust selection */
7358 len = gtk_tree_model_iter_n_children (model, iter);
7363 gtk_tree_row_reference_reordered (G_OBJECT (data),
7368 if (gtk_tree_path_get_depth (parent) != 0)
7371 if (tree_view->priv->edited_column)
7372 pspp_sheet_view_stop_editing (tree_view, TRUE);
7374 /* we need to be unprelighted */
7375 ensure_unprelighted (tree_view);
7377 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7379 pspp_sheet_view_dy_to_top_row (tree_view);
7383 /* Internal tree functions
7388 pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view,
7389 PsppSheetViewColumn *column,
7393 PsppSheetViewColumn *tmp_column = NULL;
7404 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7407 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7409 list = (rtl ? list->prev : list->next))
7411 tmp_column = list->data;
7413 if (tmp_column == column)
7416 if (tmp_column->visible)
7417 total_width += tmp_column->width;
7420 if (tmp_column != column)
7422 g_warning (G_STRLOC": passed-in column isn't in the tree");
7431 if (column->visible)
7432 *x2 = total_width + column->width;
7434 *x2 = total_width; /* width of 0 */
7438 /* Make sure the node is visible vertically */
7440 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7443 gint node_dy, height;
7444 GtkTreePath *path = NULL;
7446 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7449 /* just return if the node is visible, avoiding a costly expose */
7450 node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7451 height = ROW_HEIGHT (tree_view);
7452 if (node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment)
7453 && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
7454 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
7457 path = _pspp_sheet_view_find_path (tree_view, node);
7460 /* We process updates because we want to clear old selected items when we scroll.
7461 * if this is removed, we get a "selection streak" at the bottom. */
7462 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7463 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7464 gtk_tree_path_free (path);
7469 pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
7470 PsppSheetViewColumn *column,
7471 gboolean focus_to_cell)
7478 x = column->allocation.x;
7479 width = column->allocation.width;
7481 if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7483 /* The column is larger than the horizontal page size. If the
7484 * column has cells which can be focussed individually, then we make
7485 * sure the cell which gets focus is fully visible (if even the
7486 * focus cell is bigger than the page size, we make sure the
7487 * left-hand side of the cell is visible).
7489 * If the column does not have those so-called special cells, we
7490 * make sure the left-hand side of the column is visible.
7493 if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7495 GtkTreePath *cursor_path;
7496 GdkRectangle background_area, cell_area, focus_area;
7498 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7500 pspp_sheet_view_get_cell_area (tree_view,
7501 cursor_path, column, &cell_area);
7502 pspp_sheet_view_get_background_area (tree_view,
7503 cursor_path, column,
7506 gtk_tree_path_free (cursor_path);
7508 _pspp_sheet_view_column_get_focus_area (column,
7514 width = focus_area.width;
7516 if (width < gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7518 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7519 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7520 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7521 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7522 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7526 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7528 gtk_adjustment_get_lower (tree_view->priv->hadjustment),
7529 gtk_adjustment_get_upper (tree_view->priv->hadjustment)
7530 - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)));
7534 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7535 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7536 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7537 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7538 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7543 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7548 path = gtk_tree_path_new ();
7550 gtk_tree_path_append_index (path, node);
7555 _pspp_sheet_view_find_node (PsppSheetView *tree_view,
7559 gint *indices = gtk_tree_path_get_indices (path);
7560 gint depth = gtk_tree_path_get_depth (path);
7563 if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7569 pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
7572 gboolean add_shifted_binding,
7573 GtkMovementStep step,
7577 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7582 if (add_shifted_binding)
7583 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7588 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7591 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7596 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7603 pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view,
7604 PsppSheetViewColumn *column)
7606 PsppSheetViewColumn *left_column;
7607 PsppSheetViewColumn *cur_column = NULL;
7608 PsppSheetViewColumnReorder *reorder;
7613 /* We want to precalculate the motion list such that we know what column slots
7617 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7619 /* First, identify all possible drop spots */
7621 tmp_list = g_list_last (tree_view->priv->columns);
7623 tmp_list = g_list_first (tree_view->priv->columns);
7627 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7628 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7630 if (cur_column->visible == FALSE)
7633 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7634 if (left_column != column && cur_column != column &&
7635 tree_view->priv->column_drop_func &&
7636 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7638 left_column = cur_column;
7641 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7642 reorder->left_column = left_column;
7643 left_column = reorder->right_column = cur_column;
7645 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7648 /* Add the last one */
7649 if (tree_view->priv->column_drop_func == NULL ||
7650 ((left_column != column) &&
7651 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7653 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7654 reorder->left_column = left_column;
7655 reorder->right_column = NULL;
7656 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7659 /* We quickly check to see if it even makes sense to reorder columns. */
7660 /* If there is nothing that can be moved, then we return */
7662 if (tree_view->priv->column_drag_info == NULL)
7665 /* We know there are always 2 slots possbile, as you can always return column. */
7666 /* If that's all there is, return */
7667 if (tree_view->priv->column_drag_info->next == NULL ||
7668 (tree_view->priv->column_drag_info->next->next == NULL &&
7669 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7670 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7672 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7673 g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7674 g_list_free (tree_view->priv->column_drag_info);
7675 tree_view->priv->column_drag_info = NULL;
7678 /* We fill in the ranges for the columns, now that we've isolated them */
7679 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7681 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7683 reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7685 reorder->left_align = left;
7686 if (tmp_list->next != NULL)
7688 g_assert (tmp_list->next->data);
7689 left = reorder->right_align = (reorder->right_column->allocation.x +
7690 reorder->right_column->allocation.width +
7691 ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7695 gint width = gdk_window_get_width (tree_view->priv->header_window);
7696 reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7702 _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view,
7703 PsppSheetViewColumn *column)
7705 GdkEvent *send_event;
7706 GtkAllocation allocation;
7708 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7709 GdkDisplay *display = gdk_screen_get_display (screen);
7711 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7712 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7713 g_return_if_fail (column->button);
7715 pspp_sheet_view_set_column_drag_info (tree_view, column);
7717 if (tree_view->priv->column_drag_info == NULL)
7720 if (tree_view->priv->drag_window == NULL)
7722 GdkWindowAttr attributes;
7723 guint attributes_mask;
7725 attributes.window_type = GDK_WINDOW_CHILD;
7726 attributes.wclass = GDK_INPUT_OUTPUT;
7727 attributes.x = column->allocation.x;
7729 attributes.width = column->allocation.width;
7730 attributes.height = column->allocation.height;
7731 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7732 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7733 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL ;
7735 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7738 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7741 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7742 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7744 gtk_grab_remove (column->button);
7746 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7747 send_event->crossing.send_event = TRUE;
7748 send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button)));
7749 send_event->crossing.subwindow = NULL;
7750 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7751 send_event->crossing.time = GDK_CURRENT_TIME;
7753 gtk_propagate_event (column->button, send_event);
7754 gdk_event_free (send_event);
7756 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7757 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7758 send_event->button.send_event = TRUE;
7759 send_event->button.time = GDK_CURRENT_TIME;
7760 send_event->button.x = -1;
7761 send_event->button.y = -1;
7762 send_event->button.axes = NULL;
7763 send_event->button.state = 0;
7764 send_event->button.button = 1;
7765 send_event->button.device =
7766 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
7768 send_event->button.x_root = 0;
7769 send_event->button.y_root = 0;
7771 gtk_propagate_event (column->button, send_event);
7772 gdk_event_free (send_event);
7774 /* Kids, don't try this at home */
7775 g_object_ref (column->button);
7776 gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7777 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7778 gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7779 g_object_unref (column->button);
7781 tree_view->priv->drag_column_x = column->allocation.x;
7782 allocation = column->allocation;
7784 gtk_widget_size_allocate (column->button, &allocation);
7785 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7787 tree_view->priv->drag_column = column;
7788 gdk_window_show (tree_view->priv->drag_window);
7790 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7792 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7793 while (gtk_events_pending ())
7794 gtk_main_iteration ();
7796 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7797 gdk_pointer_grab (tree_view->priv->drag_window,
7799 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7800 NULL, NULL, GDK_CURRENT_TIME);
7801 gdk_keyboard_grab (tree_view->priv->drag_window,
7807 _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view,
7809 const GdkRectangle *clip_rect)
7812 GtkAllocation allocation;
7814 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7817 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
7819 rect.width = MAX (tree_view->priv->width, allocation.width);
7821 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7822 rect.height = ROW_HEIGHT (tree_view);
7826 GdkRectangle new_rect;
7828 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7830 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7834 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7839 pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
7841 const GdkRectangle *clip_rect)
7845 _pspp_sheet_view_find_node (tree_view, path, &node);
7848 _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7852 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7855 GtkTreePath *cursor_path;
7857 if ((tree_view->priv->row_count == 0) ||
7858 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7862 if (tree_view->priv->cursor)
7863 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7865 if (cursor_path == NULL)
7867 /* There's no cursor. Move the cursor to the first selected row, if any
7868 * are selected, otherwise to the first row in the sheetview.
7870 GList *selected_rows;
7871 GtkTreeModel *model;
7872 PsppSheetSelection *selection;
7874 selection = pspp_sheet_view_get_selection (tree_view);
7875 selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7879 /* XXX we could avoid doing O(n) work to get this result */
7880 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7881 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7882 g_list_free (selected_rows);
7886 cursor_path = gtk_tree_path_new_first ();
7887 search_first_focusable_path (tree_view, &cursor_path,
7891 gtk_tree_row_reference_free (tree_view->priv->cursor);
7892 tree_view->priv->cursor = NULL;
7896 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7897 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7898 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0);
7900 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0);
7906 /* Now find a column for the cursor. */
7907 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7909 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7910 gtk_tree_path_free (cursor_path);
7912 if (tree_view->priv->focus_column == NULL)
7915 for (list = tree_view->priv->columns; list; list = list->next)
7917 if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7919 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7920 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7921 pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7931 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7933 PsppSheetSelectMode mode)
7935 gint selection_count;
7936 int cursor_node = -1;
7937 int new_cursor_node = -1;
7938 GtkTreePath *cursor_path = NULL;
7939 gboolean grab_focus = TRUE;
7941 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7945 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7946 /* FIXME: we lost the cursor; should we get the first? */
7949 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7950 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7952 if (cursor_node < 0)
7953 /* FIXME: we lost the cursor; should we get the first? */
7956 selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7958 if (selection_count == 0
7959 && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7960 && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
7962 /* Don't move the cursor, but just select the current node */
7963 new_cursor_node = cursor_node;
7968 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7970 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7973 gtk_tree_path_free (cursor_path);
7975 if (new_cursor_node)
7977 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7979 search_first_focusable_path (tree_view, &cursor_path,
7984 gtk_tree_path_free (cursor_path);
7988 * If the list has only one item and multi-selection is set then select
7989 * the row (if not yet selected).
7991 if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7992 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7993 new_cursor_node < 0)
7996 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7998 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
8000 if (new_cursor_node < 0
8001 && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
8003 new_cursor_node = cursor_node;
8007 new_cursor_node = -1;
8011 if (new_cursor_node >= 0)
8013 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
8014 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode);
8015 gtk_tree_path_free (cursor_path);
8019 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8021 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8023 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
8025 GTK_DIR_UP : GTK_DIR_DOWN))
8027 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8030 gtk_widget_child_focus (toplevel,
8032 GTK_DIR_TAB_BACKWARD :
8033 GTK_DIR_TAB_FORWARD);
8040 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8045 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8047 return new_cursor_node >= 0;
8051 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
8053 PsppSheetSelectMode mode)
8055 int cursor_node = -1;
8056 GtkTreePath *old_cursor_path = NULL;
8057 GtkTreePath *cursor_path = NULL;
8058 int start_cursor_node = -1;
8061 gint vertical_separator;
8063 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8066 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8067 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8069 /* This is sorta weird. Focus in should give us a cursor */
8072 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
8073 _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
8075 if (cursor_node < 0)
8077 /* FIXME: we lost the cursor. Should we try to get one? */
8078 gtk_tree_path_free (old_cursor_path);
8082 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8083 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
8084 y += tree_view->priv->cursor_offset;
8085 y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment);
8086 y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment), (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator);
8088 if (y >= tree_view->priv->height)
8089 y = tree_view->priv->height - 1;
8091 tree_view->priv->cursor_offset =
8092 pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
8094 if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
8096 cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
8097 tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
8100 y -= tree_view->priv->cursor_offset;
8101 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8103 start_cursor_node = cursor_node;
8105 if (! search_first_focusable_path (tree_view, &cursor_path,
8109 /* It looks like we reached the end of the view without finding
8110 * a focusable row. We will step backwards to find the last
8113 cursor_node = start_cursor_node;
8114 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8116 search_first_focusable_path (tree_view, &cursor_path,
8125 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8127 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode);
8130 pspp_sheet_view_scroll_to_point (tree_view, -1, y);
8131 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8132 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8134 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
8135 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8137 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8140 gtk_tree_path_free (old_cursor_path);
8141 gtk_tree_path_free (cursor_path);
8145 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8147 PsppSheetSelectMode mode)
8149 int cursor_node = -1;
8150 GtkTreePath *cursor_path = NULL;
8151 PsppSheetViewColumn *column;
8154 gboolean found_column = FALSE;
8157 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8159 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8162 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8163 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8167 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8168 if (cursor_node < 0)
8170 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8172 gtk_tree_path_free (cursor_path);
8175 gtk_tree_path_free (cursor_path);
8177 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8178 if (tree_view->priv->focus_column)
8180 for (; list; list = (rtl ? list->prev : list->next))
8182 if (list->data == tree_view->priv->focus_column)
8189 gboolean left, right;
8191 column = list->data;
8192 if (column->visible == FALSE || column->row_head)
8195 pspp_sheet_view_column_cell_set_cell_data (column,
8196 tree_view->priv->model,
8201 right = list->prev ? TRUE : FALSE;
8202 left = list->next ? TRUE : FALSE;
8206 left = list->prev ? TRUE : FALSE;
8207 right = list->next ? TRUE : FALSE;
8210 if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8212 tree_view->priv->focus_column = column;
8213 found_column = TRUE;
8218 list = rtl ? list->prev : list->next;
8220 list = rtl ? list->next : list->prev;
8225 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8226 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8227 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8231 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8234 pspp_sheet_view_clamp_column_visible (tree_view,
8235 tree_view->priv->focus_column, TRUE);
8239 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8241 PsppSheetSelectMode mode)
8243 int cursor_node = -1;
8244 GtkTreePath *cursor_path = NULL;
8245 PsppSheetViewColumn *column;
8246 PsppSheetViewColumn *found_column;
8251 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8253 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8256 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8257 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8261 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8262 if (cursor_node < 0)
8264 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8266 gtk_tree_path_free (cursor_path);
8269 gtk_tree_path_free (cursor_path);
8271 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8272 if (tree_view->priv->focus_column)
8274 for (; list; list = (rtl ? list->prev : list->next))
8276 if (list->data == tree_view->priv->focus_column)
8281 found_column = NULL;
8284 gboolean left, right;
8286 column = list->data;
8287 if (column->visible == FALSE || column->row_head)
8290 pspp_sheet_view_column_cell_set_cell_data (column,
8291 tree_view->priv->model,
8296 right = list->prev ? TRUE : FALSE;
8297 left = list->next ? TRUE : FALSE;
8301 left = list->prev ? TRUE : FALSE;
8302 right = list->next ? TRUE : FALSE;
8305 if (column->tabbable
8306 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8307 found_column = column;
8311 list = rtl ? list->prev : list->next;
8313 list = rtl ? list->next : list->prev;
8318 tree_view->priv->focus_column = found_column;
8319 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8320 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8321 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8324 pspp_sheet_view_clamp_column_visible (tree_view,
8325 tree_view->priv->focus_column, TRUE);
8329 try_move_cursor_tab (PsppSheetView *tree_view,
8330 gboolean start_at_focus_column,
8333 PsppSheetViewColumn *column;
8335 int cursor_node = -1;
8336 GtkTreePath *cursor_path = NULL;
8340 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8341 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8345 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8346 if (cursor_node < 0)
8348 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8350 gtk_tree_path_free (cursor_path);
8353 gtk_tree_path_free (cursor_path);
8355 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8356 if (start_at_focus_column)
8359 ? g_list_last (tree_view->priv->columns)
8360 : g_list_first (tree_view->priv->columns));
8361 if (tree_view->priv->focus_column)
8363 for (; list; list = (rtl ? list->prev : list->next))
8365 if (list->data == tree_view->priv->focus_column)
8372 list = (rtl ^ (count == 1)
8373 ? g_list_first (tree_view->priv->columns)
8374 : g_list_last (tree_view->priv->columns));
8379 gboolean left, right;
8381 column = list->data;
8382 if (column->visible == FALSE || !column->tabbable)
8385 pspp_sheet_view_column_cell_set_cell_data (column,
8386 tree_view->priv->model,
8391 right = list->prev ? TRUE : FALSE;
8392 left = list->next ? TRUE : FALSE;
8396 left = list->prev ? TRUE : FALSE;
8397 right = list->next ? TRUE : FALSE;
8400 if (column->tabbable
8401 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8403 tree_view->priv->focus_column = column;
8404 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8405 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8406 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8411 list = rtl ? list->prev : list->next;
8413 list = rtl ? list->next : list->prev;
8420 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8423 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8426 if (!try_move_cursor_tab (tree_view, TRUE, count))
8428 if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0)
8429 && !try_move_cursor_tab (tree_view, FALSE, count))
8430 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8433 pspp_sheet_view_clamp_column_visible (tree_view,
8434 tree_view->priv->focus_column, TRUE);
8438 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8440 PsppSheetSelectMode mode)
8444 GtkTreePath *old_path;
8446 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8449 g_return_if_fail (tree_view->priv->row_count > 0);
8451 pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8455 /* Now go forward to find the first focusable row. */
8456 path = _pspp_sheet_view_find_path (tree_view, 0);
8457 search_first_focusable_path (tree_view, &path,
8458 TRUE, &cursor_node);
8462 /* Now go backwards to find last focusable row. */
8463 path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8464 search_first_focusable_path (tree_view, &path,
8465 FALSE, &cursor_node);
8471 if (gtk_tree_path_compare (old_path, path))
8473 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
8474 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8478 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8482 gtk_tree_path_free (old_path);
8483 gtk_tree_path_free (path);
8487 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8489 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8492 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8493 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8496 pspp_sheet_selection_select_all (tree_view->priv->selection);
8502 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8504 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8507 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8508 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8511 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8517 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8518 gboolean start_editing,
8519 PsppSheetSelectMode mode)
8522 int cursor_node = -1;
8523 GtkTreePath *cursor_path = NULL;
8525 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8528 if (tree_view->priv->cursor)
8529 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8531 if (cursor_path == NULL)
8534 _pspp_sheet_view_find_node (tree_view, cursor_path,
8537 if (cursor_node < 0)
8539 gtk_tree_path_free (cursor_path);
8543 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing &&
8544 tree_view->priv->focus_column)
8546 if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8548 gtk_tree_path_free (cursor_path);
8553 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8559 /* We bail out if the original (tree, node) don't exist anymore after
8560 * handling the selection-changed callback. We do return TRUE because
8561 * the key press has been handled at this point.
8563 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8565 if (cursor_node != new_node)
8568 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8570 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8571 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8573 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8574 pspp_sheet_view_row_activated (tree_view, cursor_path,
8575 tree_view->priv->focus_column);
8577 gtk_tree_path_free (cursor_path);
8583 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8586 int cursor_node = -1;
8587 GtkTreePath *cursor_path = NULL;
8589 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8593 if (tree_view->priv->cursor)
8594 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8596 if (cursor_path == NULL)
8599 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8600 if (cursor_node < 0)
8602 gtk_tree_path_free (cursor_path);
8606 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8609 PSPP_SHEET_SELECT_MODE_TOGGLE,
8612 /* We bail out if the original (tree, node) don't exist anymore after
8613 * handling the selection-changed callback. We do return TRUE because
8614 * the key press has been handled at this point.
8616 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8618 if (cursor_node != new_node)
8621 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8623 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8624 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8625 gtk_tree_path_free (cursor_path);
8631 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8633 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8634 tree_view->priv->typeselect_flush_timeout = 0;
8639 /* Cut and paste from gtkwindow.c */
8641 send_focus_change (GtkWidget *widget,
8644 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8646 fevent->focus_change.type = GDK_FOCUS_CHANGE;
8647 fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
8648 fevent->focus_change.in = in;
8650 gtk_widget_send_focus_change (widget, fevent);
8651 gdk_event_free (fevent);
8655 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8657 GtkWidget *frame, *vbox, *toplevel;
8660 if (tree_view->priv->search_custom_entry_set)
8663 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8664 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8666 if (tree_view->priv->search_window != NULL)
8668 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8669 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8670 GTK_WINDOW (tree_view->priv->search_window));
8671 else if (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)))
8672 gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
8673 GTK_WINDOW (tree_view->priv->search_window));
8674 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8678 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8679 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8681 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8682 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8683 GTK_WINDOW (tree_view->priv->search_window));
8685 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8686 GDK_WINDOW_TYPE_HINT_UTILITY);
8687 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8688 g_signal_connect (tree_view->priv->search_window, "delete-event",
8689 G_CALLBACK (pspp_sheet_view_search_delete_event),
8691 g_signal_connect (tree_view->priv->search_window, "key-press-event",
8692 G_CALLBACK (pspp_sheet_view_search_key_press_event),
8694 g_signal_connect (tree_view->priv->search_window, "button-press-event",
8695 G_CALLBACK (pspp_sheet_view_search_button_press_event),
8697 g_signal_connect (tree_view->priv->search_window, "scroll-event",
8698 G_CALLBACK (pspp_sheet_view_search_scroll_event),
8701 frame = gtk_frame_new (NULL);
8702 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8703 gtk_widget_show (frame);
8704 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8706 vbox = gtk_vbox_new (FALSE, 0);
8707 gtk_widget_show (vbox);
8708 gtk_container_add (GTK_CONTAINER (frame), vbox);
8709 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8712 tree_view->priv->search_entry = gtk_entry_new ();
8713 gtk_widget_show (tree_view->priv->search_entry);
8714 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8715 G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8717 g_signal_connect (tree_view->priv->search_entry,
8718 "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8722 g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8724 G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8728 gtk_container_add (GTK_CONTAINER (vbox),
8729 tree_view->priv->search_entry);
8731 gtk_widget_realize (tree_view->priv->search_entry);
8734 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8735 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8738 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8739 gboolean keybinding)
8741 /* We only start interactive search if we have focus or the columns
8742 * have focus. If one of our children have focus, we don't want to
8746 gboolean found_focus = FALSE;
8747 GtkWidgetClass *entry_parent_class;
8749 if (!tree_view->priv->enable_search && !keybinding)
8752 if (tree_view->priv->search_custom_entry_set)
8755 if (tree_view->priv->search_window != NULL &&
8756 gtk_widget_get_visible (tree_view->priv->search_window))
8759 for (list = tree_view->priv->columns; list; list = list->next)
8761 PsppSheetViewColumn *column;
8763 column = list->data;
8764 if (! column->visible)
8767 if (column->button && gtk_widget_has_focus (column->button))
8774 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8780 if (tree_view->priv->search_column < 0)
8783 pspp_sheet_view_ensure_interactive_directory (tree_view);
8786 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8789 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8790 gtk_widget_show (tree_view->priv->search_window);
8791 if (tree_view->priv->search_entry_changed_id == 0)
8793 tree_view->priv->search_entry_changed_id =
8794 g_signal_connect (tree_view->priv->search_entry, "changed",
8795 G_CALLBACK (pspp_sheet_view_search_init),
8799 tree_view->priv->typeselect_flush_timeout =
8800 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8801 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8804 /* Grab focus will select all the text. We don't want that to happen, so we
8805 * call the parent instance and bypass the selection change. This is probably
8806 * really non-kosher. */
8807 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8808 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8810 /* send focus-in event */
8811 send_focus_change (tree_view->priv->search_entry, TRUE);
8813 /* search first matching iter */
8814 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8820 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8822 return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8825 /* this function returns the new width of the column being resized given
8826 * the column and x position of the cursor; the x cursor position is passed
8827 * in as a pointer and automagicly corrected if it's beyond min/max limits
8830 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8834 PsppSheetViewColumn *column;
8838 /* first translate the x position from gtk_widget_get_window (widget)
8839 * to clist->clist_window
8841 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8842 column = g_list_nth (tree_view->priv->columns, i)->data;
8843 width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8845 /* Clamp down the value */
8846 if (column->min_width == -1)
8847 width = MAX (column->button_request, width);
8849 width = MAX (column->min_width, width);
8850 if (column->max_width != -1)
8851 width = MIN (width, column->max_width);
8853 *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8859 /* FIXME this adjust_allocation is a big cut-and-paste from
8860 * GtkCList, needs to be some "official" way to do this
8870 /* The window to which gtk_widget_get_window (widget) is relative */
8871 #define ALLOCATION_WINDOW(widget) \
8872 (!gtk_widget_get_has_window (widget) ? \
8873 gtk_widget_get_window (widget) : \
8874 gdk_window_get_parent (gtk_widget_get_window (widget)))
8877 adjust_allocation_recurse (GtkWidget *widget,
8880 ScrollData *scroll_data = data;
8881 GtkAllocation allocation;
8882 gtk_widget_get_allocation (widget, &allocation);
8883 /* Need to really size allocate instead of just poking
8884 * into widget->allocation if the widget is not realized.
8885 * FIXME someone figure out why this was.
8887 if (!gtk_widget_get_realized (widget))
8889 if (gtk_widget_get_visible (widget))
8891 GdkRectangle tmp_rectangle = allocation;
8892 tmp_rectangle.x += scroll_data->dx;
8893 tmp_rectangle.y += scroll_data->dy;
8895 gtk_widget_size_allocate (widget, &tmp_rectangle);
8900 if (ALLOCATION_WINDOW (widget) == scroll_data->window)
8902 allocation.x += scroll_data->dx;
8903 allocation.y += scroll_data->dy;
8905 if (GTK_IS_CONTAINER (widget))
8906 gtk_container_forall (GTK_CONTAINER (widget),
8907 adjust_allocation_recurse,
8914 adjust_allocation (GtkWidget *widget,
8918 ScrollData scroll_data;
8920 if (gtk_widget_get_realized (widget))
8921 scroll_data.window = ALLOCATION_WINDOW (widget);
8923 scroll_data.window = NULL;
8925 scroll_data.dx = dx;
8926 scroll_data.dy = dy;
8928 adjust_allocation_recurse (widget, &scroll_data);
8932 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8936 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8937 PsppSheetView *tree_view)
8939 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8944 gdk_window_move (tree_view->priv->bin_window,
8945 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8946 TREE_VIEW_HEADER_HEIGHT (tree_view));
8947 gdk_window_move (tree_view->priv->header_window,
8948 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8950 dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8953 update_prelight (tree_view,
8954 tree_view->priv->event_last_x,
8955 tree_view->priv->event_last_y - dy);
8957 if (tree_view->priv->edited_column &&
8958 GTK_IS_WIDGET (tree_view->priv->edited_column->editable_widget))
8962 PsppSheetViewChild *child = NULL;
8964 widget = GTK_WIDGET (tree_view->priv->edited_column->editable_widget);
8965 adjust_allocation (widget, 0, dy);
8967 for (list = tree_view->priv->children; list; list = list->next)
8969 child = (PsppSheetViewChild *)list->data;
8970 if (child->widget == widget)
8978 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8980 if (tree_view->priv->dy != (int) gtk_adjustment_get_value (tree_view->priv->vadjustment))
8982 /* update our dy and top_row */
8983 tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8985 if (!tree_view->priv->in_top_row_to_dy)
8986 pspp_sheet_view_dy_to_top_row (tree_view);
8997 * pspp_sheet_view_new:
8999 * Creates a new #PsppSheetView widget.
9001 * Return value: A newly created #PsppSheetView widget.
9004 pspp_sheet_view_new (void)
9006 return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
9010 * pspp_sheet_view_new_with_model:
9011 * @model: the model.
9013 * Creates a new #PsppSheetView widget with the model initialized to @model.
9015 * Return value: A newly created #PsppSheetView widget.
9018 pspp_sheet_view_new_with_model (GtkTreeModel *model)
9020 return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
9027 * pspp_sheet_view_get_model:
9028 * @tree_view: a #PsppSheetView
9030 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
9033 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
9036 pspp_sheet_view_get_model (PsppSheetView *tree_view)
9038 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9040 return tree_view->priv->model;
9044 * pspp_sheet_view_set_model:
9045 * @tree_view: A #GtkTreeNode.
9046 * @model: (allow-none): The model.
9048 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
9049 * set, it will remove it before setting the new model. If @model is %NULL,
9050 * then it will unset the old model.
9053 pspp_sheet_view_set_model (PsppSheetView *tree_view,
9054 GtkTreeModel *model)
9056 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9057 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
9059 if (model == tree_view->priv->model)
9062 if (tree_view->priv->scroll_to_path)
9064 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9065 tree_view->priv->scroll_to_path = NULL;
9068 if (tree_view->priv->model)
9070 GList *tmplist = tree_view->priv->columns;
9072 if (tree_view->priv->selected)
9073 range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
9074 pspp_sheet_view_stop_editing (tree_view, TRUE);
9076 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9077 pspp_sheet_view_row_changed,
9079 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9080 pspp_sheet_view_row_inserted,
9082 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9083 pspp_sheet_view_row_deleted,
9085 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9086 pspp_sheet_view_rows_reordered,
9089 for (; tmplist; tmplist = tmplist->next)
9090 _pspp_sheet_view_column_unset_model (tmplist->data,
9091 tree_view->priv->model);
9093 tree_view->priv->prelight_node = -1;
9095 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
9096 tree_view->priv->drag_dest_row = NULL;
9097 gtk_tree_row_reference_free (tree_view->priv->cursor);
9098 tree_view->priv->cursor = NULL;
9099 gtk_tree_row_reference_free (tree_view->priv->anchor);
9100 tree_view->priv->anchor = NULL;
9101 gtk_tree_row_reference_free (tree_view->priv->top_row);
9102 tree_view->priv->top_row = NULL;
9103 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9104 tree_view->priv->scroll_to_path = NULL;
9106 tree_view->priv->scroll_to_column = NULL;
9108 g_object_unref (tree_view->priv->model);
9110 tree_view->priv->search_column = -1;
9111 tree_view->priv->fixed_height = -1;
9112 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
9113 tree_view->priv->last_button_x = -1;
9114 tree_view->priv->last_button_y = -1;
9117 tree_view->priv->model = model;
9119 if (tree_view->priv->model)
9123 if (tree_view->priv->search_column == -1)
9125 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
9127 GType type = gtk_tree_model_get_column_type (model, i);
9129 if (g_value_type_transformable (type, G_TYPE_STRING))
9131 tree_view->priv->search_column = i;
9137 g_object_ref (tree_view->priv->model);
9138 g_signal_connect (tree_view->priv->model,
9140 G_CALLBACK (pspp_sheet_view_row_changed),
9142 g_signal_connect (tree_view->priv->model,
9144 G_CALLBACK (pspp_sheet_view_row_inserted),
9146 g_signal_connect (tree_view->priv->model,
9148 G_CALLBACK (pspp_sheet_view_row_deleted),
9150 g_signal_connect (tree_view->priv->model,
9152 G_CALLBACK (pspp_sheet_view_rows_reordered),
9155 tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
9157 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
9158 install_presize_handler (tree_view);
9161 g_object_notify (G_OBJECT (tree_view), "model");
9163 if (tree_view->priv->selection)
9164 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
9166 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9167 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9171 * pspp_sheet_view_get_selection:
9172 * @tree_view: A #PsppSheetView.
9174 * Gets the #PsppSheetSelection associated with @tree_view.
9176 * Return value: A #PsppSheetSelection object.
9178 PsppSheetSelection *
9179 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
9181 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9183 return tree_view->priv->selection;
9187 * pspp_sheet_view_get_hadjustment:
9188 * @tree_view: A #PsppSheetView
9190 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
9192 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9196 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
9198 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9200 return pspp_sheet_view_do_get_hadjustment (tree_view);
9203 static GtkAdjustment *
9204 pspp_sheet_view_do_get_hadjustment (PsppSheetView *tree_view)
9206 return tree_view->priv->hadjustment;
9210 * pspp_sheet_view_set_hadjustment:
9211 * @tree_view: A #PsppSheetView
9212 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9214 * Sets the #GtkAdjustment for the current horizontal aspect.
9217 pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view,
9218 GtkAdjustment *adjustment)
9220 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9222 pspp_sheet_view_set_adjustments (tree_view,
9224 tree_view->priv->vadjustment);
9226 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9230 pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
9231 GtkAdjustment *adjustment)
9233 PsppSheetViewPrivate *priv = tree_view->priv;
9235 if (adjustment && priv->hadjustment == adjustment)
9238 if (priv->hadjustment != NULL)
9240 g_signal_handlers_disconnect_by_func (priv->hadjustment,
9241 pspp_sheet_view_adjustment_changed,
9243 g_object_unref (priv->hadjustment);
9246 if (adjustment == NULL)
9247 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9250 g_signal_connect (adjustment, "value-changed",
9251 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9252 priv->hadjustment = g_object_ref_sink (adjustment);
9253 /* FIXME: Adjustment should probably be populated here with fresh values, but
9254 * internal details are too complicated for me to decipher right now.
9256 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9258 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9262 * pspp_sheet_view_get_vadjustment:
9263 * @tree_view: A #PsppSheetView
9265 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9267 * Return value: (transfer none): A #GtkAdjustment object, or %NULL
9268 * if none is currently being used.
9270 * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
9273 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9275 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9277 return pspp_sheet_view_do_get_vadjustment (tree_view);
9280 static GtkAdjustment *
9281 pspp_sheet_view_do_get_vadjustment (PsppSheetView *tree_view)
9283 return tree_view->priv->vadjustment;
9287 * pspp_sheet_view_set_vadjustment:
9288 * @tree_view: A #PsppSheetView
9289 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9291 * Sets the #GtkAdjustment for the current vertical aspect.
9293 * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
9296 pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view,
9297 GtkAdjustment *adjustment)
9299 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9300 g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
9302 pspp_sheet_view_do_set_vadjustment (tree_view, adjustment);
9306 pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
9307 GtkAdjustment *adjustment)
9309 PsppSheetViewPrivate *priv = tree_view->priv;
9311 if (adjustment && priv->vadjustment == adjustment)
9314 if (priv->vadjustment != NULL)
9316 g_signal_handlers_disconnect_by_func (priv->vadjustment,
9317 pspp_sheet_view_adjustment_changed,
9319 g_object_unref (priv->vadjustment);
9322 if (adjustment == NULL)
9323 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9326 g_signal_connect (adjustment, "value-changed",
9327 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9328 priv->vadjustment = g_object_ref_sink (adjustment);
9329 /* FIXME: Adjustment should probably be populated here with fresh values, but
9330 * internal details are too complicated for me to decipher right now.
9332 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9333 g_object_notify (G_OBJECT (tree_view), "vadjustment");
9336 /* Column and header operations */
9339 * pspp_sheet_view_get_headers_visible:
9340 * @tree_view: A #PsppSheetView.
9342 * Returns %TRUE if the headers on the @tree_view are visible.
9344 * Return value: Whether the headers are visible or not.
9347 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9349 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9351 return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9355 * pspp_sheet_view_set_headers_visible:
9356 * @tree_view: A #PsppSheetView.
9357 * @headers_visible: %TRUE if the headers are visible
9359 * Sets the visibility state of the headers.
9362 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9363 gboolean headers_visible)
9367 PsppSheetViewColumn *column;
9368 GtkAllocation allocation;
9370 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9372 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9374 headers_visible = !! headers_visible;
9376 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9379 if (headers_visible)
9380 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9382 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9384 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9386 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9387 if (headers_visible)
9389 gdk_window_move_resize (tree_view->priv->bin_window, x, y + TREE_VIEW_HEADER_HEIGHT (tree_view),
9390 tree_view->priv->width, allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view));
9392 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9393 pspp_sheet_view_map_buttons (tree_view);
9397 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9399 for (list = tree_view->priv->columns; list; list = list->next)
9401 column = list->data;
9403 gtk_widget_unmap (column->button);
9405 gdk_window_hide (tree_view->priv->header_window);
9409 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view));
9410 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2);
9411 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
9412 gtk_adjustment_set_upper (tree_view->priv->vadjustment, tree_view->priv->height);
9413 gtk_adjustment_changed (tree_view->priv->vadjustment);
9415 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9417 g_object_notify (G_OBJECT (tree_view), "headers-visible");
9421 * pspp_sheet_view_columns_autosize:
9422 * @tree_view: A #PsppSheetView.
9424 * Resizes all columns to their optimal width. Only works after the
9425 * treeview has been realized.
9428 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9430 gboolean dirty = FALSE;
9432 PsppSheetViewColumn *column;
9434 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9436 for (list = tree_view->priv->columns; list; list = list->next)
9438 column = list->data;
9439 _pspp_sheet_view_column_cell_set_dirty (column);
9444 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9448 * pspp_sheet_view_set_headers_clickable:
9449 * @tree_view: A #PsppSheetView.
9450 * @setting: %TRUE if the columns are clickable.
9452 * Allow the column title buttons to be clicked.
9455 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9460 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9462 for (list = tree_view->priv->columns; list; list = list->next)
9463 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9465 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9470 * pspp_sheet_view_get_headers_clickable:
9471 * @tree_view: A #PsppSheetView.
9473 * Returns whether all header columns are clickable.
9475 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9480 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9484 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9486 for (list = tree_view->priv->columns; list; list = list->next)
9487 if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9494 * pspp_sheet_view_set_rules_hint
9495 * @tree_view: a #PsppSheetView
9496 * @setting: %TRUE if the tree requires reading across rows
9498 * This function tells GTK+ that the user interface for your
9499 * application requires users to read across tree rows and associate
9500 * cells with one another. By default, GTK+ will then render the tree
9501 * with alternating row colors. Do <emphasis>not</emphasis> use it
9502 * just because you prefer the appearance of the ruled tree; that's a
9503 * question for the theme. Some themes will draw tree rows in
9504 * alternating colors even when rules are turned off, and users who
9505 * prefer that appearance all the time can choose those themes. You
9506 * should call this function only as a <emphasis>semantic</emphasis>
9507 * hint to the theme engine that your tree makes alternating colors
9508 * useful from a functional standpoint (since it has lots of columns,
9513 pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view,
9516 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9518 setting = setting != FALSE;
9520 if (tree_view->priv->has_rules != setting)
9522 tree_view->priv->has_rules = setting;
9523 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9526 g_object_notify (G_OBJECT (tree_view), "rules-hint");
9530 * pspp_sheet_view_get_rules_hint
9531 * @tree_view: a #PsppSheetView
9533 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9535 * Return value: %TRUE if rules are useful for the user of this tree
9538 pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view)
9540 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9542 return tree_view->priv->has_rules;
9545 /* Public Column functions
9549 * pspp_sheet_view_append_column:
9550 * @tree_view: A #PsppSheetView.
9551 * @column: The #PsppSheetViewColumn to add.
9553 * Appends @column to the list of columns.
9555 * Return value: The number of columns in @tree_view after appending.
9558 pspp_sheet_view_append_column (PsppSheetView *tree_view,
9559 PsppSheetViewColumn *column)
9561 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9562 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9563 g_return_val_if_fail (column->tree_view == NULL, -1);
9565 return pspp_sheet_view_insert_column (tree_view, column, -1);
9570 * pspp_sheet_view_remove_column:
9571 * @tree_view: A #PsppSheetView.
9572 * @column: The #PsppSheetViewColumn to remove.
9574 * Removes @column from @tree_view.
9576 * Return value: The number of columns in @tree_view after removing.
9579 pspp_sheet_view_remove_column (PsppSheetView *tree_view,
9580 PsppSheetViewColumn *column)
9582 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9583 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9584 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9586 if (tree_view->priv->focus_column == column)
9587 tree_view->priv->focus_column = NULL;
9589 if (tree_view->priv->edited_column == column)
9591 pspp_sheet_view_stop_editing (tree_view, TRUE);
9593 /* no need to, but just to be sure ... */
9594 tree_view->priv->edited_column = NULL;
9597 _pspp_sheet_view_column_unset_tree_view (column);
9599 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9600 tree_view->priv->n_columns--;
9602 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9606 _pspp_sheet_view_column_unrealize_button (column);
9607 for (list = tree_view->priv->columns; list; list = list->next)
9609 PsppSheetViewColumn *tmp_column;
9611 tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9612 if (tmp_column->visible)
9613 _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9616 if (tree_view->priv->n_columns == 0 &&
9617 pspp_sheet_view_get_headers_visible (tree_view) &&
9618 tree_view->priv->header_window)
9619 gdk_window_hide (tree_view->priv->header_window);
9621 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9624 g_object_unref (column);
9625 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9627 return tree_view->priv->n_columns;
9631 * pspp_sheet_view_insert_column:
9632 * @tree_view: A #PsppSheetView.
9633 * @column: The #PsppSheetViewColumn to be inserted.
9634 * @position: The position to insert @column in.
9636 * This inserts the @column into the @tree_view at @position. If @position is
9637 * -1, then the column is inserted at the end.
9639 * Return value: The number of columns in @tree_view after insertion.
9642 pspp_sheet_view_insert_column (PsppSheetView *tree_view,
9643 PsppSheetViewColumn *column,
9646 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9647 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9648 g_return_val_if_fail (column->tree_view == NULL, -1);
9650 g_object_ref_sink (column);
9652 if (tree_view->priv->n_columns == 0 &&
9653 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9654 pspp_sheet_view_get_headers_visible (tree_view))
9656 gdk_window_show (tree_view->priv->header_window);
9659 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9661 tree_view->priv->n_columns++;
9663 _pspp_sheet_view_column_set_tree_view (column, tree_view);
9665 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9669 _pspp_sheet_view_column_realize_button (column);
9671 for (list = tree_view->priv->columns; list; list = list->next)
9673 column = PSPP_SHEET_VIEW_COLUMN (list->data);
9674 if (column->visible)
9675 _pspp_sheet_view_column_cell_set_dirty (column);
9677 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9680 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9682 return tree_view->priv->n_columns;
9686 * pspp_sheet_view_insert_column_with_attributes:
9687 * @tree_view: A #PsppSheetView
9688 * @position: The position to insert the new column in.
9689 * @title: The title to set the header to.
9690 * @cell: The #GtkCellRenderer.
9691 * @Varargs: A %NULL-terminated list of attributes.
9693 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9694 * @position. If @position is -1, then the newly created column is inserted at
9695 * the end. The column is initialized with the attributes given.
9697 * Return value: The number of columns in @tree_view after insertion.
9700 pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view,
9703 GtkCellRenderer *cell,
9706 PsppSheetViewColumn *column;
9711 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9713 column = pspp_sheet_view_column_new ();
9714 pspp_sheet_view_column_set_title (column, title);
9715 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9717 va_start (args, cell);
9719 attribute = va_arg (args, gchar *);
9721 while (attribute != NULL)
9723 column_id = va_arg (args, gint);
9724 pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9725 attribute = va_arg (args, gchar *);
9730 pspp_sheet_view_insert_column (tree_view, column, position);
9732 return tree_view->priv->n_columns;
9736 * pspp_sheet_view_insert_column_with_data_func:
9737 * @tree_view: a #PsppSheetView
9738 * @position: Position to insert, -1 for append
9739 * @title: column title
9740 * @cell: cell renderer for column
9741 * @func: function to set attributes of cell renderer
9742 * @data: data for @func
9743 * @dnotify: destroy notifier for @data
9745 * Convenience function that inserts a new column into the #PsppSheetView
9746 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9747 * attributes (normally using data from the model). See also
9748 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9750 * Return value: number of columns in the tree view post-insert
9753 pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view,
9756 GtkCellRenderer *cell,
9757 PsppSheetCellDataFunc func,
9759 GDestroyNotify dnotify)
9761 PsppSheetViewColumn *column;
9763 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9765 column = pspp_sheet_view_column_new ();
9766 pspp_sheet_view_column_set_title (column, title);
9767 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9768 pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9770 pspp_sheet_view_insert_column (tree_view, column, position);
9772 return tree_view->priv->n_columns;
9776 * pspp_sheet_view_get_column:
9777 * @tree_view: A #PsppSheetView.
9778 * @n: The position of the column, counting from 0.
9780 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9782 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9785 PsppSheetViewColumn *
9786 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9789 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9791 if (n < 0 || n >= tree_view->priv->n_columns)
9794 if (tree_view->priv->columns == NULL)
9797 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9801 * pspp_sheet_view_get_columns:
9802 * @tree_view: A #PsppSheetView
9804 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9805 * The returned list must be freed with g_list_free ().
9807 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9810 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9812 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9814 return g_list_copy (tree_view->priv->columns);
9818 * pspp_sheet_view_move_column_after:
9819 * @tree_view: A #PsppSheetView
9820 * @column: The #PsppSheetViewColumn to be moved.
9821 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9823 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9824 * @column is placed in the first position.
9827 pspp_sheet_view_move_column_after (PsppSheetView *tree_view,
9828 PsppSheetViewColumn *column,
9829 PsppSheetViewColumn *base_column)
9831 GList *column_list_el, *base_el = NULL;
9833 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9835 column_list_el = g_list_find (tree_view->priv->columns, column);
9836 g_return_if_fail (column_list_el != NULL);
9840 base_el = g_list_find (tree_view->priv->columns, base_column);
9841 g_return_if_fail (base_el != NULL);
9844 if (column_list_el->prev == base_el)
9847 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9848 if (base_el == NULL)
9850 column_list_el->prev = NULL;
9851 column_list_el->next = tree_view->priv->columns;
9852 if (column_list_el->next)
9853 column_list_el->next->prev = column_list_el;
9854 tree_view->priv->columns = column_list_el;
9858 column_list_el->prev = base_el;
9859 column_list_el->next = base_el->next;
9860 if (column_list_el->next)
9861 column_list_el->next->prev = column_list_el;
9862 base_el->next = column_list_el;
9865 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9867 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9868 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9871 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9875 * pspp_sheet_view_set_column_drag_function:
9876 * @tree_view: A #PsppSheetView.
9877 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9878 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9879 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9881 * Sets a user function for determining where a column may be dropped when
9882 * dragged. This function is called on every column pair in turn at the
9883 * beginning of a column drag to determine where a drop can take place. The
9884 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9885 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9886 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9887 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9888 * @tree_view reverts to the default behavior of allowing all columns to be
9889 * dropped everywhere.
9892 pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view,
9893 PsppSheetViewColumnDropFunc func,
9895 GDestroyNotify destroy)
9897 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9899 if (tree_view->priv->column_drop_func_data_destroy)
9900 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9902 tree_view->priv->column_drop_func = func;
9903 tree_view->priv->column_drop_func_data = user_data;
9904 tree_view->priv->column_drop_func_data_destroy = destroy;
9908 * pspp_sheet_view_scroll_to_point:
9909 * @tree_view: a #PsppSheetView
9910 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9911 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9913 * Scrolls the tree view such that the top-left corner of the visible
9914 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9915 * in tree coordinates. The @tree_view must be realized before
9916 * this function is called. If it isn't, you probably want to be
9917 * using pspp_sheet_view_scroll_to_cell().
9919 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9922 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9926 GtkAdjustment *hadj;
9927 GtkAdjustment *vadj;
9929 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9930 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9932 hadj = tree_view->priv->hadjustment;
9933 vadj = tree_view->priv->vadjustment;
9936 gtk_adjustment_set_value (hadj, CLAMP (tree_x, gtk_adjustment_get_lower (hadj), gtk_adjustment_get_upper (hadj) - gtk_adjustment_get_page_size (hadj)));
9938 gtk_adjustment_set_value (vadj, CLAMP (tree_y, gtk_adjustment_get_lower (vadj), gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj)));
9942 * pspp_sheet_view_scroll_to_cell:
9943 * @tree_view: A #PsppSheetView.
9944 * @path: (allow-none): The path of the row to move to, or %NULL.
9945 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9946 * @use_align: whether to use alignment arguments, or %FALSE.
9947 * @row_align: The vertical alignment of the row specified by @path.
9948 * @col_align: The horizontal alignment of the column specified by @column.
9950 * Moves the alignments of @tree_view to the position specified by @column and
9951 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9952 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9953 * or @path need to be non-%NULL. @row_align determines where the row is
9954 * placed, and @col_align determines where @column is placed. Both are expected
9955 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9956 * right/bottom alignment, 0.5 means center.
9958 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9959 * tree does the minimum amount of work to scroll the cell onto the screen.
9960 * This means that the cell will be scrolled to the edge closest to its current
9961 * position. If the cell is currently visible on the screen, nothing is done.
9963 * This function only works if the model is set, and @path is a valid row on the
9964 * model. If the model changes before the @tree_view is realized, the centered
9965 * path will be modified to reflect this change.
9968 pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view,
9970 PsppSheetViewColumn *column,
9975 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9976 g_return_if_fail (tree_view->priv->model != NULL);
9977 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9978 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9979 g_return_if_fail (path != NULL || column != NULL);
9982 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9983 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9985 row_align = CLAMP (row_align, 0.0, 1.0);
9986 col_align = CLAMP (col_align, 0.0, 1.0);
9989 /* Note: Despite the benefits that come from having one code path for the
9990 * scrolling code, we short-circuit validate_visible_area's immplementation as
9991 * it is much slower than just going to the point.
9993 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9994 !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9995 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9997 if (tree_view->priv->scroll_to_path)
9998 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
10000 tree_view->priv->scroll_to_path = NULL;
10001 tree_view->priv->scroll_to_column = NULL;
10004 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
10006 tree_view->priv->scroll_to_column = column;
10007 tree_view->priv->scroll_to_use_align = use_align;
10008 tree_view->priv->scroll_to_row_align = row_align;
10009 tree_view->priv->scroll_to_col_align = col_align;
10011 install_presize_handler (tree_view);
10015 GdkRectangle cell_rect;
10016 GdkRectangle vis_rect;
10017 gint dest_x, dest_y;
10019 pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
10020 pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
10022 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
10024 dest_x = vis_rect.x;
10025 dest_y = vis_rect.y;
10031 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
10035 if (cell_rect.x < vis_rect.x)
10036 dest_x = cell_rect.x;
10037 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
10038 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
10046 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
10047 dest_y = MAX (dest_y, 0);
10051 if (cell_rect.y < vis_rect.y)
10052 dest_y = cell_rect.y;
10053 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
10054 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
10058 pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
10063 * pspp_sheet_view_row_activated:
10064 * @tree_view: A #PsppSheetView
10065 * @path: The #GtkTreePath to be activated.
10066 * @column: The #PsppSheetViewColumn to be activated.
10068 * Activates the cell determined by @path and @column.
10071 pspp_sheet_view_row_activated (PsppSheetView *tree_view,
10073 PsppSheetViewColumn *column)
10075 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10077 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
10082 * pspp_sheet_view_get_reorderable:
10083 * @tree_view: a #PsppSheetView
10085 * Retrieves whether the user can reorder the tree via drag-and-drop. See
10086 * pspp_sheet_view_set_reorderable().
10088 * Return value: %TRUE if the tree can be reordered.
10091 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
10093 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10095 return tree_view->priv->reorderable;
10099 * pspp_sheet_view_set_reorderable:
10100 * @tree_view: A #PsppSheetView.
10101 * @reorderable: %TRUE, if the tree can be reordered.
10103 * This function is a convenience function to allow you to reorder
10104 * models that support the #GtkDragSourceIface and the
10105 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
10106 * these. If @reorderable is %TRUE, then the user can reorder the
10107 * model by dragging and dropping rows. The developer can listen to
10108 * these changes by connecting to the model's row_inserted and
10109 * row_deleted signals. The reordering is implemented by setting up
10110 * the tree view as a drag source and destination. Therefore, drag and
10111 * drop can not be used in a reorderable view for any other purpose.
10113 * This function does not give you any degree of control over the order -- any
10114 * reordering is allowed. If more control is needed, you should probably
10115 * handle drag and drop manually.
10118 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
10119 gboolean reorderable)
10121 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10123 reorderable = reorderable != FALSE;
10125 if (tree_view->priv->reorderable == reorderable)
10130 const GtkTargetEntry row_targets[] = {
10131 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
10134 pspp_sheet_view_enable_model_drag_source (tree_view,
10137 G_N_ELEMENTS (row_targets),
10139 pspp_sheet_view_enable_model_drag_dest (tree_view,
10141 G_N_ELEMENTS (row_targets),
10146 pspp_sheet_view_unset_rows_drag_source (tree_view);
10147 pspp_sheet_view_unset_rows_drag_dest (tree_view);
10150 tree_view->priv->reorderable = reorderable;
10152 g_object_notify (G_OBJECT (tree_view), "reorderable");
10155 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
10156 is pressed, other rows will be unselected.
10158 If CLAMP_NODE is true, then the sheetview will scroll to make the row
10161 pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
10163 gboolean clear_and_select,
10164 gboolean clamp_node,
10165 PsppSheetSelectMode mode)
10169 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10171 GtkTreePath *cursor_path;
10172 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10173 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
10174 gtk_tree_path_free (cursor_path);
10177 gtk_tree_row_reference_free (tree_view->priv->cursor);
10178 tree_view->priv->cursor = NULL;
10180 _pspp_sheet_view_find_node (tree_view, path, &node);
10181 tree_view->priv->cursor =
10182 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
10183 tree_view->priv->model,
10186 if (tree_view->priv->row_count > 0)
10190 if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
10191 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
10195 /* We have to re-find tree and node here again, somebody might have
10196 * cleared the node or the whole tree in the PsppSheetSelection::changed
10197 * callback. If the nodes differ we bail out here.
10199 _pspp_sheet_view_find_node (tree_view, path, &new_node);
10201 if (node != new_node)
10206 pspp_sheet_view_clamp_node_visible (tree_view, node);
10207 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
10211 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
10215 * pspp_sheet_view_get_cursor:
10216 * @tree_view: A #PsppSheetView
10217 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
10218 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
10220 * Fills in @path and @focus_column with the current path and focus column. If
10221 * the cursor isn't currently set, then *@path will be %NULL. If no column
10222 * currently has focus, then *@focus_column will be %NULL.
10224 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
10225 * you are done with it.
10228 pspp_sheet_view_get_cursor (PsppSheetView *tree_view,
10229 GtkTreePath **path,
10230 PsppSheetViewColumn **focus_column)
10232 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10236 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10237 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10244 *focus_column = tree_view->priv->focus_column;
10249 * pspp_sheet_view_set_cursor:
10250 * @tree_view: A #PsppSheetView
10251 * @path: A #GtkTreePath
10252 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10253 * @start_editing: %TRUE if the specified cell should start being edited.
10255 * Sets the current keyboard focus to be at @path, and selects it. This is
10256 * useful when you want to focus the user's attention on a particular row. If
10257 * @focus_column is not %NULL, then focus is given to the column specified by
10258 * it. Additionally, if @focus_column is specified, and @start_editing is
10259 * %TRUE, then editing should be started in the specified cell.
10260 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10261 * in order to give keyboard focus to the widget. Please note that editing
10262 * can only happen when the widget is realized.
10264 * If @path is invalid for @model, the current cursor (if any) will be unset
10265 * and the function will return without failing.
10268 pspp_sheet_view_set_cursor (PsppSheetView *tree_view,
10270 PsppSheetViewColumn *focus_column,
10271 gboolean start_editing)
10273 pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10274 NULL, start_editing);
10278 * pspp_sheet_view_set_cursor_on_cell:
10279 * @tree_view: A #PsppSheetView
10280 * @path: A #GtkTreePath
10281 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10282 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10283 * @start_editing: %TRUE if the specified cell should start being edited.
10285 * Sets the current keyboard focus to be at @path, and selects it. This is
10286 * useful when you want to focus the user's attention on a particular row. If
10287 * @focus_column is not %NULL, then focus is given to the column specified by
10288 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10289 * contains 2 or more editable or activatable cells, then focus is given to
10290 * the cell specified by @focus_cell. Additionally, if @focus_column is
10291 * specified, and @start_editing is %TRUE, then editing should be started in
10292 * the specified cell. This function is often followed by
10293 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10294 * widget. Please note that editing can only happen when the widget is
10297 * If @path is invalid for @model, the current cursor (if any) will be unset
10298 * and the function will return without failing.
10303 pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view,
10305 PsppSheetViewColumn *focus_column,
10306 GtkCellRenderer *focus_cell,
10307 gboolean start_editing)
10309 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10310 g_return_if_fail (path != NULL);
10311 g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10313 if (!tree_view->priv->model)
10318 g_return_if_fail (focus_column);
10319 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10322 /* cancel the current editing, if it exists */
10323 if (tree_view->priv->edited_column &&
10324 tree_view->priv->edited_column->editable_widget)
10325 pspp_sheet_view_stop_editing (tree_view, TRUE);
10327 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
10329 if (focus_column && focus_column->visible)
10332 gboolean column_in_tree = FALSE;
10334 for (list = tree_view->priv->columns; list; list = list->next)
10335 if (list->data == focus_column)
10337 column_in_tree = TRUE;
10340 g_return_if_fail (column_in_tree);
10341 tree_view->priv->focus_column = focus_column;
10343 pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10345 pspp_sheet_view_start_editing (tree_view, path);
10347 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10348 pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10354 * pspp_sheet_view_get_bin_window:
10355 * @tree_view: A #PsppSheetView
10357 * Returns the window that @tree_view renders to. This is used primarily to
10358 * compare to <literal>event->window</literal> to confirm that the event on
10359 * @tree_view is on the right window.
10361 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10364 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10366 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10368 return tree_view->priv->bin_window;
10372 * pspp_sheet_view_get_path_at_pos:
10373 * @tree_view: A #PsppSheetView.
10374 * @x: The x position to be identified (relative to bin_window).
10375 * @y: The y position to be identified (relative to bin_window).
10376 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10377 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10378 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10379 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10381 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10382 * (please see pspp_sheet_view_get_bin_window()).
10383 * That is, @x and @y are relative to an events coordinates. @x and @y must
10384 * come from an event on the @tree_view only where <literal>event->window ==
10385 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10386 * things like popup menus. If @path is non-%NULL, then it will be filled
10387 * with the #GtkTreePath at that point. This path should be freed with
10388 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10389 * with the column at that point. @cell_x and @cell_y return the coordinates
10390 * relative to the cell background (i.e. the @background_area passed to
10391 * gtk_cell_renderer_render()). This function is only meaningful if
10392 * @tree_view is realized. Therefore this function will always return %FALSE
10393 * if @tree_view is not realized or does not have a model.
10395 * For converting widget coordinates (eg. the ones you get from
10396 * GtkWidget::query-tooltip), please see
10397 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10399 * Return value: %TRUE if a row exists at that coordinate.
10402 pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view,
10405 GtkTreePath **path,
10406 PsppSheetViewColumn **column,
10413 g_return_val_if_fail (tree_view != NULL, FALSE);
10420 if (tree_view->priv->bin_window == NULL)
10423 if (tree_view->priv->row_count == 0)
10426 if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment))
10429 if (x < 0 || y < 0)
10432 if (column || cell_x)
10434 PsppSheetViewColumn *tmp_column;
10435 PsppSheetViewColumn *last_column = NULL;
10437 gint remaining_x = x;
10438 gboolean found = FALSE;
10441 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10442 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10444 list = (rtl ? list->prev : list->next))
10446 tmp_column = list->data;
10448 if (tmp_column->visible == FALSE)
10451 last_column = tmp_column;
10452 if (remaining_x <= tmp_column->width)
10457 *column = tmp_column;
10460 *cell_x = remaining_x;
10464 remaining_x -= tmp_column->width;
10467 /* If found is FALSE and there is a last_column, then it the remainder
10468 * space is in that area
10475 *column = last_column;
10478 *cell_x = last_column->width + remaining_x;
10487 y_offset = pspp_sheet_view_find_offset (tree_view,
10488 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10495 *cell_y = y_offset;
10498 *path = _pspp_sheet_view_find_path (tree_view, node);
10503 /* Computes 'cell_area' from 'background_area', which must be the background
10504 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10505 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10506 the cell area as passed to _pspp_sheet_view_column_cell_render().
10508 'column' is required to properly adjust 'cell_area->x' and
10509 'cell_area->width'. It may be set to NULL if these values are not of
10510 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10513 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
10514 PsppSheetViewColumn *column,
10515 const GdkRectangle *background_area,
10516 gboolean subtract_focus_rect,
10517 GdkRectangle *cell_area)
10519 gint vertical_separator;
10520 gint horizontal_separator;
10522 *cell_area = *background_area;
10524 gtk_widget_style_get (GTK_WIDGET (tree_view),
10525 "vertical-separator", &vertical_separator,
10526 "horizontal-separator", &horizontal_separator,
10528 cell_area->x += horizontal_separator / 2;
10529 cell_area->y += vertical_separator / 2;
10530 cell_area->width -= horizontal_separator;
10531 cell_area->height -= vertical_separator;
10533 if (subtract_focus_rect)
10535 int focus_line_width;
10537 gtk_widget_style_get (GTK_WIDGET (tree_view),
10538 "focus-line-width", &focus_line_width,
10540 cell_area->x += focus_line_width;
10541 cell_area->y += focus_line_width;
10542 cell_area->width -= 2 * focus_line_width;
10543 cell_area->height -= 2 * focus_line_width;
10546 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10548 gint grid_line_width;
10549 gtk_widget_style_get (GTK_WIDGET (tree_view),
10550 "grid-line-width", &grid_line_width,
10553 if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10554 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10557 PsppSheetViewColumn *first_column, *last_column;
10560 /* Find the last visible column. */
10561 last_column = NULL;
10562 for (list = g_list_last (tree_view->priv->columns);
10566 PsppSheetViewColumn *c = list->data;
10574 /* Find the first visible column. */
10575 first_column = NULL;
10576 for (list = g_list_first (tree_view->priv->columns);
10580 PsppSheetViewColumn *c = list->data;
10588 if (column == first_column)
10590 cell_area->width -= grid_line_width / 2;
10592 else if (column == last_column)
10594 cell_area->x += grid_line_width / 2;
10595 cell_area->width -= grid_line_width / 2;
10599 cell_area->x += grid_line_width / 2;
10600 cell_area->width -= grid_line_width;
10604 if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10605 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10607 cell_area->y += grid_line_width / 2;
10608 cell_area->height -= grid_line_width;
10612 if (column == NULL)
10615 cell_area->width = 0;
10620 * pspp_sheet_view_get_cell_area:
10621 * @tree_view: a #PsppSheetView
10622 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10623 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10624 * @rect: rectangle to fill with cell rect
10626 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10627 * row specified by @path and the column specified by @column. If @path is
10628 * %NULL, or points to a path not currently displayed, the @y and @height fields
10629 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10630 * fields will be filled with 0. The sum of all cell rects does not cover the
10631 * entire tree; there are extra pixels in between rows, for example. The
10632 * returned rectangle is equivalent to the @cell_area passed to
10633 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10637 pspp_sheet_view_get_cell_area (PsppSheetView *tree_view,
10639 PsppSheetViewColumn *column,
10640 GdkRectangle *rect)
10642 GdkRectangle background_area;
10644 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10645 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10646 g_return_if_fail (rect != NULL);
10647 g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10648 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10650 pspp_sheet_view_get_background_area (tree_view, path, column,
10652 pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10657 * pspp_sheet_view_get_background_area:
10658 * @tree_view: a #PsppSheetView
10659 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10660 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10661 * @rect: rectangle to fill with cell background rect
10663 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10664 * row specified by @path and the column specified by @column. If @path is
10665 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10666 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10667 * fields will be filled with 0. The returned rectangle is equivalent to the
10668 * @background_area passed to gtk_cell_renderer_render(). These background
10669 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10670 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10671 * itself, excluding surrounding borders.
10675 pspp_sheet_view_get_background_area (PsppSheetView *tree_view,
10677 PsppSheetViewColumn *column,
10678 GdkRectangle *rect)
10682 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10683 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10684 g_return_if_fail (rect != NULL);
10693 /* Get vertical coords */
10695 _pspp_sheet_view_find_node (tree_view, path, &node);
10699 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10701 rect->height = ROW_HEIGHT (tree_view);
10708 pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10709 rect->width = x2 - rect->x;
10714 * pspp_sheet_view_get_visible_rect:
10715 * @tree_view: a #PsppSheetView
10716 * @visible_rect: rectangle to fill
10718 * Fills @visible_rect with the currently-visible region of the
10719 * buffer, in tree coordinates. Convert to bin_window coordinates with
10720 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10721 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10722 * scrollable area of the tree.
10725 pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view,
10726 GdkRectangle *visible_rect)
10730 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10732 widget = GTK_WIDGET (tree_view);
10736 GtkAllocation allocation;
10737 gtk_widget_get_allocation (widget, &allocation);
10738 visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
10739 visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
10740 visible_rect->width = allocation.width;
10741 visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10746 * pspp_sheet_view_widget_to_tree_coords:
10747 * @tree_view: a #PsppSheetView
10748 * @wx: X coordinate relative to bin_window
10749 * @wy: Y coordinate relative to bin_window
10750 * @tx: return location for tree X coordinate
10751 * @ty: return location for tree Y coordinate
10753 * Converts bin_window coordinates to coordinates for the
10754 * tree (the full scrollable area of the tree).
10756 * Deprecated: 2.12: Due to historial reasons the name of this function is
10757 * incorrect. For converting coordinates relative to the widget to
10758 * bin_window coordinates, please see
10759 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10763 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10769 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10772 *tx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10774 *ty = wy + tree_view->priv->dy;
10778 * pspp_sheet_view_tree_to_widget_coords:
10779 * @tree_view: a #PsppSheetView
10780 * @tx: tree X coordinate
10781 * @ty: tree Y coordinate
10782 * @wx: return location for X coordinate relative to bin_window
10783 * @wy: return location for Y coordinate relative to bin_window
10785 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10786 * to bin_window coordinates.
10788 * Deprecated: 2.12: Due to historial reasons the name of this function is
10789 * incorrect. For converting bin_window coordinates to coordinates relative
10790 * to bin_window, please see
10791 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10795 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10801 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10804 *wx = tx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10806 *wy = ty - tree_view->priv->dy;
10811 * pspp_sheet_view_convert_widget_to_tree_coords:
10812 * @tree_view: a #PsppSheetView
10813 * @wx: X coordinate relative to the widget
10814 * @wy: Y coordinate relative to the widget
10815 * @tx: return location for tree X coordinate
10816 * @ty: return location for tree Y coordinate
10818 * Converts widget coordinates to coordinates for the
10819 * tree (the full scrollable area of the tree).
10824 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10832 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10834 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10837 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10843 * pspp_sheet_view_convert_tree_to_widget_coords:
10844 * @tree_view: a #PsppSheetView
10845 * @tx: X coordinate relative to the tree
10846 * @ty: Y coordinate relative to the tree
10847 * @wx: return location for widget X coordinate
10848 * @wy: return location for widget Y coordinate
10850 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10851 * to widget coordinates.
10856 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10864 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10866 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10869 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10875 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10876 * @tree_view: a #PsppSheetView
10877 * @wx: X coordinate relative to the widget
10878 * @wy: Y coordinate relative to the widget
10879 * @bx: return location for bin_window X coordinate
10880 * @by: return location for bin_window Y coordinate
10882 * Converts widget coordinates to coordinates for the bin_window
10883 * (see pspp_sheet_view_get_bin_window()).
10888 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10894 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10897 *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10899 *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10903 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10904 * @tree_view: a #PsppSheetView
10905 * @bx: bin_window X coordinate
10906 * @by: bin_window Y coordinate
10907 * @wx: return location for widget X coordinate
10908 * @wy: return location for widget Y coordinate
10910 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10911 * to widget relative coordinates.
10916 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10922 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10925 *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10927 *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10931 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10932 * @tree_view: a #PsppSheetView
10933 * @tx: tree X coordinate
10934 * @ty: tree Y coordinate
10935 * @bx: return location for X coordinate relative to bin_window
10936 * @by: return location for Y coordinate relative to bin_window
10938 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10939 * to bin_window coordinates.
10944 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10950 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10955 *by = ty - tree_view->priv->dy;
10959 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10960 * @tree_view: a #PsppSheetView
10961 * @bx: X coordinate relative to bin_window
10962 * @by: Y coordinate relative to bin_window
10963 * @tx: return location for tree X coordinate
10964 * @ty: return location for tree Y coordinate
10966 * Converts bin_window coordinates to coordinates for the
10967 * tree (the full scrollable area of the tree).
10972 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10978 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10983 *ty = by + tree_view->priv->dy;
10989 * pspp_sheet_view_get_visible_range:
10990 * @tree_view: A #PsppSheetView
10991 * @start_path: (allow-none): Return location for start of region, or %NULL.
10992 * @end_path: (allow-none): Return location for end of region, or %NULL.
10994 * Sets @start_path and @end_path to be the first and last visible path.
10995 * Note that there may be invisible paths in between.
10997 * The paths should be freed with gtk_tree_path_free() after use.
10999 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
11004 pspp_sheet_view_get_visible_range (PsppSheetView *tree_view,
11005 GtkTreePath **start_path,
11006 GtkTreePath **end_path)
11011 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11013 if (!tree_view->priv->row_count)
11020 pspp_sheet_view_find_offset (tree_view,
11021 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
11024 *start_path = _pspp_sheet_view_find_path (tree_view, node);
11033 if (tree_view->priv->height < gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
11034 y = tree_view->priv->height - 1;
11036 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1;
11038 pspp_sheet_view_find_offset (tree_view, y, &node);
11040 *end_path = _pspp_sheet_view_find_path (tree_view, node);
11049 unset_reorderable (PsppSheetView *tree_view)
11051 if (tree_view->priv->reorderable)
11053 tree_view->priv->reorderable = FALSE;
11054 g_object_notify (G_OBJECT (tree_view), "reorderable");
11059 * pspp_sheet_view_enable_model_drag_source:
11060 * @tree_view: a #PsppSheetView
11061 * @start_button_mask: Mask of allowed buttons to start drag
11062 * @targets: the table of targets that the drag will support
11063 * @n_targets: the number of items in @targets
11064 * @actions: the bitmask of possible actions for a drag from this
11067 * Turns @tree_view into a drag source for automatic DND. Calling this
11068 * method sets #PsppSheetView:reorderable to %FALSE.
11071 pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view,
11072 GdkModifierType start_button_mask,
11073 const GtkTargetEntry *targets,
11075 GdkDragAction actions)
11077 TreeViewDragInfo *di;
11079 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11081 gtk_drag_source_set (GTK_WIDGET (tree_view),
11087 di = ensure_info (tree_view);
11089 di->start_button_mask = start_button_mask;
11090 di->source_actions = actions;
11091 di->source_set = TRUE;
11093 unset_reorderable (tree_view);
11097 * pspp_sheet_view_enable_model_drag_dest:
11098 * @tree_view: a #PsppSheetView
11099 * @targets: the table of targets that the drag will support
11100 * @n_targets: the number of items in @targets
11101 * @actions: the bitmask of possible actions for a drag from this
11104 * Turns @tree_view into a drop destination for automatic DND. Calling
11105 * this method sets #PsppSheetView:reorderable to %FALSE.
11108 pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view,
11109 const GtkTargetEntry *targets,
11111 GdkDragAction actions)
11113 TreeViewDragInfo *di;
11115 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11117 gtk_drag_dest_set (GTK_WIDGET (tree_view),
11123 di = ensure_info (tree_view);
11124 di->dest_set = TRUE;
11126 unset_reorderable (tree_view);
11130 * pspp_sheet_view_unset_rows_drag_source:
11131 * @tree_view: a #PsppSheetView
11133 * Undoes the effect of
11134 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
11135 * #PsppSheetView:reorderable to %FALSE.
11138 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
11140 TreeViewDragInfo *di;
11142 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11144 di = get_info (tree_view);
11148 if (di->source_set)
11150 gtk_drag_source_unset (GTK_WIDGET (tree_view));
11151 di->source_set = FALSE;
11154 if (!di->dest_set && !di->source_set)
11155 remove_info (tree_view);
11158 unset_reorderable (tree_view);
11162 * pspp_sheet_view_unset_rows_drag_dest:
11163 * @tree_view: a #PsppSheetView
11165 * Undoes the effect of
11166 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
11167 * #PsppSheetView:reorderable to %FALSE.
11170 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
11172 TreeViewDragInfo *di;
11174 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11176 di = get_info (tree_view);
11182 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
11183 di->dest_set = FALSE;
11186 if (!di->dest_set && !di->source_set)
11187 remove_info (tree_view);
11190 unset_reorderable (tree_view);
11194 * pspp_sheet_view_set_drag_dest_row:
11195 * @tree_view: a #PsppSheetView
11196 * @path: (allow-none): The path of the row to highlight, or %NULL.
11197 * @pos: Specifies whether to drop before, after or into the row
11199 * Sets the row that is highlighted for feedback.
11202 pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view,
11204 PsppSheetViewDropPosition pos)
11206 GtkTreePath *current_dest;
11208 /* Note; this function is exported to allow a custom DND
11209 * implementation, so it can't touch TreeViewDragInfo
11212 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11214 current_dest = NULL;
11216 if (tree_view->priv->drag_dest_row)
11218 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11219 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
11222 /* special case a drop on an empty model */
11223 tree_view->priv->empty_view_drop = 0;
11225 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
11226 && gtk_tree_path_get_depth (path) == 1
11227 && gtk_tree_path_get_indices (path)[0] == 0)
11231 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
11235 tree_view->priv->empty_view_drop = 1;
11238 tree_view->priv->drag_dest_pos = pos;
11242 tree_view->priv->drag_dest_row =
11243 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
11244 pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
11247 tree_view->priv->drag_dest_row = NULL;
11251 int node, new_node;
11253 _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11254 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11258 new_node = pspp_sheet_view_node_next (tree_view, node);
11260 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11262 new_node = pspp_sheet_view_node_prev (tree_view, node);
11264 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11266 gtk_tree_path_free (current_dest);
11271 * pspp_sheet_view_get_drag_dest_row:
11272 * @tree_view: a #PsppSheetView
11273 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11274 * @pos: (allow-none): Return location for the drop position, or %NULL
11276 * Gets information about the row that is highlighted for feedback.
11279 pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view,
11280 GtkTreePath **path,
11281 PsppSheetViewDropPosition *pos)
11283 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11287 if (tree_view->priv->drag_dest_row)
11288 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11291 if (tree_view->priv->empty_view_drop)
11292 *path = gtk_tree_path_new_from_indices (0, -1);
11299 *pos = tree_view->priv->drag_dest_pos;
11303 * pspp_sheet_view_get_dest_row_at_pos:
11304 * @tree_view: a #PsppSheetView
11305 * @drag_x: the position to determine the destination row for
11306 * @drag_y: the position to determine the destination row for
11307 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11308 * @pos: (allow-none): Return location for the drop position, or %NULL
11310 * Determines the destination row for a given position. @drag_x and
11311 * @drag_y are expected to be in widget coordinates. This function is only
11312 * meaningful if @tree_view is realized. Therefore this function will always
11313 * return %FALSE if @tree_view is not realized or does not have a model.
11315 * Return value: whether there is a row at the given position, %TRUE if this
11316 * is indeed the case.
11319 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view,
11322 GtkTreePath **path,
11323 PsppSheetViewDropPosition *pos)
11327 gdouble offset_into_row;
11330 PsppSheetViewColumn *column = NULL;
11331 GtkTreePath *tmp_path = NULL;
11333 /* Note; this function is exported to allow a custom DND
11334 * implementation, so it can't touch TreeViewDragInfo
11337 g_return_val_if_fail (tree_view != NULL, FALSE);
11338 g_return_val_if_fail (drag_x >= 0, FALSE);
11339 g_return_val_if_fail (drag_y >= 0, FALSE);
11344 if (tree_view->priv->bin_window == NULL)
11347 if (tree_view->priv->row_count == 0)
11350 /* If in the top third of a row, we drop before that row; if
11351 * in the bottom third, drop after that row; if in the middle,
11352 * and the row has children, drop into the row.
11354 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11357 if (!pspp_sheet_view_get_path_at_pos (tree_view,
11366 pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11369 offset_into_row = cell_y;
11374 gtk_tree_path_free (tmp_path);
11378 third = cell.height / 3.0;
11382 if (offset_into_row < third)
11384 *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11386 else if (offset_into_row < (cell.height / 2.0))
11388 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11390 else if (offset_into_row < third * 2.0)
11392 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11396 *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11404 #if GTK3_TRANSITION
11405 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11407 * pspp_sheet_view_create_row_drag_icon:
11408 * @tree_view: a #PsppSheetView
11409 * @path: a #GtkTreePath in @tree_view
11411 * Creates a #GdkPixmap representation of the row at @path.
11412 * This image is used for a drag icon.
11414 * Return value: a newly-allocated pixmap of the drag icon.
11417 pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view,
11424 GdkRectangle background_area;
11425 GdkRectangle expose_area;
11427 /* start drawing inside the black outline */
11429 GdkDrawable *drawable;
11430 gint bin_window_width;
11433 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11434 g_return_val_if_fail (path != NULL, NULL);
11436 widget = GTK_WIDGET (tree_view);
11438 if (!gtk_widget_get_realized (widget))
11441 _pspp_sheet_view_find_node (tree_view,
11448 if (!gtk_tree_model_get_iter (tree_view->priv->model,
11455 background_area.y = y;
11456 background_area.height = ROW_HEIGHT (tree_view);
11458 bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
11460 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11461 bin_window_width + 2,
11462 background_area.height + 2,
11467 expose_area.width = bin_window_width + 2;
11468 expose_area.height = background_area.height + 2;
11470 #if GTK3_TRANSITION
11471 gdk_draw_rectangle (drawable,
11472 widget->style->base_gc [gtk_widget_get_state (widget)],
11475 bin_window_width + 2,
11476 background_area.height + 2);
11479 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11481 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11483 list = (rtl ? list->prev : list->next))
11485 PsppSheetViewColumn *column = list->data;
11486 GdkRectangle cell_area;
11487 gint vertical_separator;
11489 if (!column->visible)
11492 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11494 background_area.x = cell_offset;
11495 background_area.width = column->width;
11497 gtk_widget_style_get (widget,
11498 "vertical-separator", &vertical_separator,
11501 cell_area = background_area;
11503 cell_area.y += vertical_separator / 2;
11504 cell_area.height -= vertical_separator;
11506 if (pspp_sheet_view_column_cell_is_visible (column))
11507 _pspp_sheet_view_column_cell_render (column,
11513 cell_offset += column->width;
11516 #if GTK3_TRANSITION
11517 gdk_draw_rectangle (drawable,
11518 widget->style->black_gc,
11521 bin_window_width + 1,
11522 background_area.height + 1);
11530 * pspp_sheet_view_set_destroy_count_func:
11531 * @tree_view: A #PsppSheetView
11532 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11533 * @data: (allow-none): User data to be passed to @func, or %NULL
11534 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11536 * This function should almost never be used. It is meant for private use by
11537 * ATK for determining the number of visible children that are removed when a row is deleted.
11540 pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view,
11541 PsppSheetDestroyCountFunc func,
11543 GDestroyNotify destroy)
11545 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11547 if (tree_view->priv->destroy_count_destroy)
11548 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11550 tree_view->priv->destroy_count_func = func;
11551 tree_view->priv->destroy_count_data = data;
11552 tree_view->priv->destroy_count_destroy = destroy;
11557 * Interactive search
11561 * pspp_sheet_view_set_enable_search:
11562 * @tree_view: A #PsppSheetView
11563 * @enable_search: %TRUE, if the user can search interactively
11565 * If @enable_search is set, then the user can type in text to search through
11566 * the tree interactively (this is sometimes called "typeahead find").
11568 * Note that even if this is %FALSE, the user can still initiate a search
11569 * using the "start-interactive-search" key binding.
11572 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11573 gboolean enable_search)
11575 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11577 enable_search = !!enable_search;
11579 if (tree_view->priv->enable_search != enable_search)
11581 tree_view->priv->enable_search = enable_search;
11582 g_object_notify (G_OBJECT (tree_view), "enable-search");
11587 * pspp_sheet_view_get_enable_search:
11588 * @tree_view: A #PsppSheetView
11590 * Returns whether or not the tree allows to start interactive searching
11591 * by typing in text.
11593 * Return value: whether or not to let the user search interactively
11596 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11598 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11600 return tree_view->priv->enable_search;
11605 * pspp_sheet_view_get_search_column:
11606 * @tree_view: A #PsppSheetView
11608 * Gets the column searched on by the interactive search code.
11610 * Return value: the column the interactive search code searches in.
11613 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11615 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11617 return (tree_view->priv->search_column);
11621 * pspp_sheet_view_set_search_column:
11622 * @tree_view: A #PsppSheetView
11623 * @column: the column of the model to search in, or -1 to disable searching
11625 * Sets @column as the column where the interactive search code should
11626 * search in for the current model.
11628 * If the search column is set, users can use the "start-interactive-search"
11629 * key binding to bring up search popup. The enable-search property controls
11630 * whether simply typing text will also start an interactive search.
11632 * Note that @column refers to a column of the current model. The search
11633 * column is reset to -1 when the model is changed.
11636 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11639 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11640 g_return_if_fail (column >= -1);
11642 if (tree_view->priv->search_column == column)
11645 tree_view->priv->search_column = column;
11646 g_object_notify (G_OBJECT (tree_view), "search-column");
11650 * pspp_sheet_view_get_search_equal_func:
11651 * @tree_view: A #PsppSheetView
11653 * Returns the compare function currently in use.
11655 * Return value: the currently used compare function for the search code.
11658 PsppSheetViewSearchEqualFunc
11659 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11661 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11663 return tree_view->priv->search_equal_func;
11667 * pspp_sheet_view_set_search_equal_func:
11668 * @tree_view: A #PsppSheetView
11669 * @search_equal_func: the compare function to use during the search
11670 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11671 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11673 * Sets the compare function for the interactive search capabilities; note
11674 * that somewhat like strcmp() returning 0 for equality
11675 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11678 pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view,
11679 PsppSheetViewSearchEqualFunc search_equal_func,
11680 gpointer search_user_data,
11681 GDestroyNotify search_destroy)
11683 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11684 g_return_if_fail (search_equal_func != NULL);
11686 if (tree_view->priv->search_destroy)
11687 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11689 tree_view->priv->search_equal_func = search_equal_func;
11690 tree_view->priv->search_user_data = search_user_data;
11691 tree_view->priv->search_destroy = search_destroy;
11692 if (tree_view->priv->search_equal_func == NULL)
11693 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11697 * pspp_sheet_view_get_search_entry:
11698 * @tree_view: A #PsppSheetView
11700 * Returns the #GtkEntry which is currently in use as interactive search
11701 * entry for @tree_view. In case the built-in entry is being used, %NULL
11702 * will be returned.
11704 * Return value: the entry currently in use as search entry.
11709 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11711 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11713 if (tree_view->priv->search_custom_entry_set)
11714 return GTK_ENTRY (tree_view->priv->search_entry);
11720 * pspp_sheet_view_set_search_entry:
11721 * @tree_view: A #PsppSheetView
11722 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11724 * Sets the entry which the interactive search code will use for this
11725 * @tree_view. This is useful when you want to provide a search entry
11726 * in our interface at all time at a fixed position. Passing %NULL for
11727 * @entry will make the interactive search code use the built-in popup
11733 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11736 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11737 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11739 if (tree_view->priv->search_custom_entry_set)
11741 if (tree_view->priv->search_entry_changed_id)
11743 g_signal_handler_disconnect (tree_view->priv->search_entry,
11744 tree_view->priv->search_entry_changed_id);
11745 tree_view->priv->search_entry_changed_id = 0;
11747 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11748 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11751 g_object_unref (tree_view->priv->search_entry);
11753 else if (tree_view->priv->search_window)
11755 gtk_widget_destroy (tree_view->priv->search_window);
11757 tree_view->priv->search_window = NULL;
11762 tree_view->priv->search_entry = g_object_ref (entry);
11763 tree_view->priv->search_custom_entry_set = TRUE;
11765 if (tree_view->priv->search_entry_changed_id == 0)
11767 tree_view->priv->search_entry_changed_id =
11768 g_signal_connect (tree_view->priv->search_entry, "changed",
11769 G_CALLBACK (pspp_sheet_view_search_init),
11773 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11774 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11777 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11781 tree_view->priv->search_entry = NULL;
11782 tree_view->priv->search_custom_entry_set = FALSE;
11787 * pspp_sheet_view_set_search_position_func:
11788 * @tree_view: A #PsppSheetView
11789 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11790 * to use the default search position function
11791 * @data: (allow-none): user data to pass to @func, or %NULL
11792 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11794 * Sets the function to use when positioning the search dialog.
11799 pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view,
11800 PsppSheetViewSearchPositionFunc func,
11801 gpointer user_data,
11802 GDestroyNotify destroy)
11804 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11806 if (tree_view->priv->search_position_destroy)
11807 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11809 tree_view->priv->search_position_func = func;
11810 tree_view->priv->search_position_user_data = user_data;
11811 tree_view->priv->search_position_destroy = destroy;
11812 if (tree_view->priv->search_position_func == NULL)
11813 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11817 * pspp_sheet_view_get_search_position_func:
11818 * @tree_view: A #PsppSheetView
11820 * Returns the positioning function currently in use.
11822 * Return value: the currently used function for positioning the search dialog.
11826 PsppSheetViewSearchPositionFunc
11827 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11829 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11831 return tree_view->priv->search_position_func;
11836 pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
11837 PsppSheetView *tree_view)
11839 if (tree_view->priv->disable_popdown)
11842 if (tree_view->priv->search_entry_changed_id)
11844 g_signal_handler_disconnect (tree_view->priv->search_entry,
11845 tree_view->priv->search_entry_changed_id);
11846 tree_view->priv->search_entry_changed_id = 0;
11848 if (tree_view->priv->typeselect_flush_timeout)
11850 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11851 tree_view->priv->typeselect_flush_timeout = 0;
11854 if (gtk_widget_get_visible (search_dialog))
11856 /* send focus-in event */
11857 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11858 gtk_widget_hide (search_dialog);
11859 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11860 send_focus_change (GTK_WIDGET (tree_view), TRUE);
11865 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11866 GtkWidget *search_dialog,
11867 gpointer user_data)
11870 gint tree_x, tree_y;
11871 gint tree_width, tree_height;
11872 GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
11873 GdkScreen *screen = gdk_window_get_screen (tree_window);
11874 GtkRequisition requisition;
11876 GdkRectangle monitor;
11878 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11879 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11881 gtk_widget_realize (search_dialog);
11883 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11884 tree_width = gdk_window_get_width (tree_window);
11885 tree_height = gdk_window_get_height (tree_window);
11887 gtk_widget_size_request (search_dialog, &requisition);
11889 if (tree_x + tree_width > gdk_screen_get_width (screen))
11890 x = gdk_screen_get_width (screen) - requisition.width;
11891 else if (tree_x + tree_width - requisition.width < 0)
11894 x = tree_x + tree_width - requisition.width;
11896 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11897 y = gdk_screen_get_height (screen) - requisition.height;
11898 else if (tree_y + tree_height < 0) /* isn't really possible ... */
11901 y = tree_y + tree_height;
11903 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11907 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11911 PsppSheetView *tree_view = (PsppSheetView *)data;
11913 tree_view->priv->disable_popdown = 1;
11914 g_signal_connect (menu, "hide",
11915 G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11918 #if GTK3_TRANSITION
11919 /* Because we're visible but offscreen, we just set a flag in the preedit
11923 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11924 PsppSheetView *tree_view)
11926 tree_view->priv->imcontext_changed = 1;
11927 if (tree_view->priv->typeselect_flush_timeout)
11929 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11930 tree_view->priv->typeselect_flush_timeout =
11931 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11932 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11940 pspp_sheet_view_search_activate (GtkEntry *entry,
11941 PsppSheetView *tree_view)
11946 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11949 /* If we have a row selected and it's the cursor row, we activate
11951 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11953 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11955 _pspp_sheet_view_find_node (tree_view, path, &node);
11957 if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11958 pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11960 gtk_tree_path_free (path);
11965 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11967 PsppSheetView *tree_view = (PsppSheetView *)data;
11969 tree_view->priv->disable_popdown = 0;
11975 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11978 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11982 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11983 GdkEventAny *event,
11984 PsppSheetView *tree_view)
11986 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11988 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11994 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11995 GdkEventButton *event,
11996 PsppSheetView *tree_view)
11998 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
12000 pspp_sheet_view_search_dialog_hide (widget, tree_view);
12002 if (event->window == tree_view->priv->bin_window)
12003 pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
12009 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
12010 GdkEventScroll *event,
12011 PsppSheetView *tree_view)
12013 gboolean retval = FALSE;
12015 if (event->direction == GDK_SCROLL_UP)
12017 pspp_sheet_view_search_move (widget, tree_view, TRUE);
12020 else if (event->direction == GDK_SCROLL_DOWN)
12022 pspp_sheet_view_search_move (widget, tree_view, FALSE);
12026 /* renew the flush timeout */
12027 if (retval && tree_view->priv->typeselect_flush_timeout
12028 && !tree_view->priv->search_custom_entry_set)
12030 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12031 tree_view->priv->typeselect_flush_timeout =
12032 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12033 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12041 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
12042 GdkEventKey *event,
12043 PsppSheetView *tree_view)
12045 gboolean retval = FALSE;
12047 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
12048 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12050 /* close window and cancel the search */
12051 if (!tree_view->priv->search_custom_entry_set
12052 && (event->keyval == GDK_Escape ||
12053 event->keyval == GDK_Tab ||
12054 event->keyval == GDK_KP_Tab ||
12055 event->keyval == GDK_ISO_Left_Tab))
12057 pspp_sheet_view_search_dialog_hide (widget, tree_view);
12061 /* select previous matching iter */
12062 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
12064 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
12065 gtk_widget_error_bell (widget);
12070 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
12071 && (event->keyval == GDK_g || event->keyval == GDK_G))
12073 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
12074 gtk_widget_error_bell (widget);
12079 /* select next matching iter */
12080 if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
12082 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
12083 gtk_widget_error_bell (widget);
12088 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
12089 && (event->keyval == GDK_g || event->keyval == GDK_G))
12091 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
12092 gtk_widget_error_bell (widget);
12097 /* renew the flush timeout */
12098 if (retval && tree_view->priv->typeselect_flush_timeout
12099 && !tree_view->priv->search_custom_entry_set)
12101 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12102 tree_view->priv->typeselect_flush_timeout =
12103 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12104 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12111 /* this function returns FALSE if there is a search string but
12112 * nothing was found, and TRUE otherwise.
12115 pspp_sheet_view_search_move (GtkWidget *window,
12116 PsppSheetView *tree_view,
12124 GtkTreeModel *model;
12125 PsppSheetSelection *selection;
12127 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
12129 g_return_val_if_fail (text != NULL, FALSE);
12131 len = strlen (text);
12133 if (up && tree_view->priv->selected_iter == 1)
12134 return strlen (text) < 1;
12136 len = strlen (text);
12141 model = pspp_sheet_view_get_model (tree_view);
12142 selection = pspp_sheet_view_get_selection (tree_view);
12145 pspp_sheet_selection_unselect_all (selection);
12146 if (!gtk_tree_model_get_iter_first (model, &iter))
12149 ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
12150 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
12155 tree_view->priv->selected_iter += up?(-1):(1);
12160 /* return to old iter */
12162 gtk_tree_model_get_iter_first (model, &iter);
12163 pspp_sheet_view_search_iter (model, selection,
12165 &count, tree_view->priv->selected_iter);
12171 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
12175 gpointer search_data)
12177 gboolean retval = TRUE;
12179 gchar *normalized_string;
12180 gchar *normalized_key;
12181 gchar *case_normalized_string = NULL;
12182 gchar *case_normalized_key = NULL;
12183 GValue value = {0,};
12184 GValue transformed = {0,};
12186 gtk_tree_model_get_value (model, iter, column, &value);
12188 g_value_init (&transformed, G_TYPE_STRING);
12190 if (!g_value_transform (&value, &transformed))
12192 g_value_unset (&value);
12196 g_value_unset (&value);
12198 str = g_value_get_string (&transformed);
12201 g_value_unset (&transformed);
12205 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
12206 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
12208 if (normalized_string && normalized_key)
12210 case_normalized_string = g_utf8_casefold (normalized_string, -1);
12211 case_normalized_key = g_utf8_casefold (normalized_key, -1);
12213 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
12217 g_value_unset (&transformed);
12218 g_free (normalized_key);
12219 g_free (normalized_string);
12220 g_free (case_normalized_key);
12221 g_free (case_normalized_string);
12227 pspp_sheet_view_search_iter (GtkTreeModel *model,
12228 PsppSheetSelection *selection,
12237 PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
12239 path = gtk_tree_model_get_path (model, iter);
12240 _pspp_sheet_view_find_node (tree_view, path, &node);
12244 gboolean done = FALSE;
12246 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
12251 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
12253 pspp_sheet_selection_select_iter (selection, iter);
12254 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12257 gtk_tree_path_free (path);
12266 node = pspp_sheet_view_node_next (tree_view, node);
12272 has_next = gtk_tree_model_iter_next (model, iter);
12275 gtk_tree_path_next (path);
12278 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12283 gtk_tree_path_free (path);
12285 /* we've run out of tree, done with this func */
12297 pspp_sheet_view_search_init (GtkWidget *entry,
12298 PsppSheetView *tree_view)
12304 GtkTreeModel *model;
12305 PsppSheetSelection *selection;
12307 g_return_if_fail (GTK_IS_ENTRY (entry));
12308 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12310 text = gtk_entry_get_text (GTK_ENTRY (entry));
12312 model = pspp_sheet_view_get_model (tree_view);
12313 selection = pspp_sheet_view_get_selection (tree_view);
12316 pspp_sheet_selection_unselect_all (selection);
12317 if (tree_view->priv->typeselect_flush_timeout
12318 && !tree_view->priv->search_custom_entry_set)
12320 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12321 tree_view->priv->typeselect_flush_timeout =
12322 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12323 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12330 if (!gtk_tree_model_get_iter_first (model, &iter))
12333 ret = pspp_sheet_view_search_iter (model, selection,
12338 tree_view->priv->selected_iter = 1;
12342 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12343 PsppSheetView *tree_view)
12345 if (tree_view->priv->edited_column == NULL)
12348 _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12349 tree_view->priv->edited_column = NULL;
12351 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12352 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12354 g_signal_handlers_disconnect_by_func (cell_editable,
12355 pspp_sheet_view_remove_widget,
12357 g_signal_handlers_disconnect_by_func (cell_editable,
12358 pspp_sheet_view_editable_button_press_event,
12360 g_signal_handlers_disconnect_by_func (cell_editable,
12361 pspp_sheet_view_editable_clicked,
12364 gtk_container_remove (GTK_CONTAINER (tree_view),
12365 GTK_WIDGET (cell_editable));
12367 /* FIXME should only redraw a single node */
12368 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12372 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12373 GtkTreePath *cursor_path)
12376 GdkRectangle background_area;
12377 GdkRectangle cell_area;
12378 GtkCellEditable *editable_widget = NULL;
12379 gchar *path_string;
12380 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12381 gint retval = FALSE;
12384 g_assert (tree_view->priv->focus_column);
12386 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12389 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12390 if (cursor_node < 0)
12393 path_string = gtk_tree_path_to_string (cursor_path);
12394 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12396 pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12397 tree_view->priv->model,
12399 pspp_sheet_view_get_background_area (tree_view,
12401 tree_view->priv->focus_column,
12403 pspp_sheet_view_get_cell_area (tree_view,
12405 tree_view->priv->focus_column,
12408 if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12417 if (editable_widget != NULL)
12421 GtkCellRenderer *cell;
12424 cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12426 _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12429 area.width -= right + left;
12431 pspp_sheet_view_real_start_editing (tree_view,
12432 tree_view->priv->focus_column,
12441 g_free (path_string);
12446 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12447 GdkEventButton *event,
12448 PsppSheetView *sheet_view)
12452 node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12453 "pspp-sheet-view-node"));
12454 return pspp_sheet_view_row_head_clicked (sheet_view,
12456 sheet_view->priv->edited_column,
12461 pspp_sheet_view_editable_clicked (GtkButton *button,
12462 PsppSheetView *sheet_view)
12464 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12469 is_all_selected (GtkWidget *widget)
12471 GtkEntryBuffer *buffer;
12472 gint start_pos, end_pos;
12474 if (!GTK_IS_ENTRY (widget))
12477 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12478 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12479 &start_pos, &end_pos)
12481 && end_pos == gtk_entry_buffer_get_length (buffer));
12485 is_at_left (GtkWidget *widget)
12487 return (GTK_IS_ENTRY (widget)
12488 && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12492 is_at_right (GtkWidget *widget)
12494 GtkEntryBuffer *buffer;
12497 if (!GTK_IS_ENTRY (widget))
12500 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12501 length = gtk_entry_buffer_get_length (buffer);
12502 return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12506 pspp_sheet_view_event (GtkWidget *widget,
12507 GdkEventKey *event,
12508 PsppSheetView *tree_view)
12510 PsppSheetViewColumn *column;
12517 /* Intercept only key press events.
12518 It would make sense to use "key-press-event" instead of "event", but
12519 GtkEntry attaches its own signal handler to "key-press-event" that runs
12520 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12522 if (event->type != GDK_KEY_PRESS)
12525 keyval = event->keyval;
12527 switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12530 switch (event->keyval)
12532 case GDK_Left: case GDK_KP_Left:
12533 case GDK_Home: case GDK_KP_Home:
12534 if (!is_all_selected (widget) && !is_at_left (widget))
12538 case GDK_Right: case GDK_KP_Right:
12539 case GDK_End: case GDK_KP_End:
12540 if (!is_all_selected (widget) && !is_at_right (widget))
12544 case GDK_Up: case GDK_KP_Up:
12545 case GDK_Down: case GDK_KP_Down:
12548 case GDK_Page_Up: case GDK_KP_Page_Up:
12549 case GDK_Page_Down: case GDK_KP_Page_Down:
12560 case GDK_Tab: case GDK_KP_Tab:
12561 case GDK_ISO_Left_Tab:
12570 case GDK_SHIFT_MASK:
12571 switch (event->keyval)
12574 case GDK_ISO_Left_Tab:
12583 case GDK_CONTROL_MASK:
12584 switch (event->keyval)
12586 case GDK_Left: case GDK_KP_Left:
12587 if (!is_all_selected (widget) && !is_at_left (widget))
12591 case GDK_Right: case GDK_KP_Right:
12592 if (!is_all_selected (widget) && !is_at_right (widget))
12596 case GDK_Up: case GDK_KP_Up:
12597 case GDK_Down: case GDK_KP_Down:
12609 row = tree_view->priv->edited_row;
12610 column = tree_view->priv->edited_column;
12611 path = gtk_tree_path_new_from_indices (row, -1);
12613 pspp_sheet_view_stop_editing (tree_view, cancel);
12614 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12616 pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12617 gtk_tree_path_free (path);
12619 handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12620 G_OBJECT (tree_view));
12622 g_signal_stop_emission_by_name (widget, "event");
12624 pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12625 pspp_sheet_view_start_editing (tree_view, path);
12626 gtk_tree_path_free (path);
12632 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12635 PsppSheetView *sheet_view = data;
12637 g_signal_connect (widget, "event",
12638 G_CALLBACK (pspp_sheet_view_event),
12641 if (GTK_IS_CONTAINER (widget))
12642 gtk_container_foreach (GTK_CONTAINER (widget),
12643 pspp_sheet_view_override_cell_keypresses,
12648 pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
12649 PsppSheetViewColumn *column,
12651 GtkCellEditable *cell_editable,
12652 GdkRectangle *cell_area,
12656 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12657 gint pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
12658 GtkRequisition requisition;
12661 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12663 tree_view->priv->edited_column = column;
12664 _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12666 row = gtk_tree_path_get_indices (path)[0];
12667 tree_view->priv->edited_row = row;
12668 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12669 cell_area->y += pre_val - (int)gtk_adjustment_get_value (tree_view->priv->vadjustment);
12671 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12672 pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12673 tree_view->priv->anchor_column = column;
12675 gtk_widget_size_request (GTK_WIDGET (cell_editable), &requisition);
12677 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12679 if (requisition.height < cell_area->height)
12681 gint diff = cell_area->height - requisition.height;
12682 pspp_sheet_view_put (tree_view,
12683 GTK_WIDGET (cell_editable),
12684 cell_area->x, cell_area->y + diff/2,
12685 cell_area->width, requisition.height);
12689 pspp_sheet_view_put (tree_view,
12690 GTK_WIDGET (cell_editable),
12691 cell_area->x, cell_area->y,
12692 cell_area->width, cell_area->height);
12695 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12696 (GdkEvent *)event);
12698 gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12699 g_signal_connect (cell_editable, "remove-widget",
12700 G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12701 if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12702 GTK_IS_BUTTON (cell_editable))
12704 g_signal_connect (cell_editable, "button-press-event",
12705 G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12707 g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12708 GINT_TO_POINTER (row));
12709 g_signal_connect (cell_editable, "clicked",
12710 G_CALLBACK (pspp_sheet_view_editable_clicked),
12714 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12719 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12720 gboolean cancel_editing)
12722 PsppSheetViewColumn *column;
12723 GtkCellRenderer *cell;
12725 if (tree_view->priv->edited_column == NULL)
12729 * This is very evil. We need to do this, because
12730 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12731 * later on. If pspp_sheet_view_row_changed notices
12732 * tree_view->priv->edited_column != NULL, it'll call
12733 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12735 * Please read that again if you intend to modify anything here.
12738 column = tree_view->priv->edited_column;
12739 tree_view->priv->edited_column = NULL;
12741 cell = _pspp_sheet_view_column_get_edited_cell (column);
12742 gtk_cell_renderer_stop_editing (cell, cancel_editing);
12744 if (!cancel_editing)
12745 gtk_cell_editable_editing_done (column->editable_widget);
12747 tree_view->priv->edited_column = column;
12749 gtk_cell_editable_remove_widget (column->editable_widget);
12754 * pspp_sheet_view_set_hover_selection:
12755 * @tree_view: a #PsppSheetView
12756 * @hover: %TRUE to enable hover selection mode
12758 * Enables of disables the hover selection mode of @tree_view.
12759 * Hover selection makes the selected row follow the pointer.
12760 * Currently, this works only for the selection modes
12761 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12766 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12769 hover = hover != FALSE;
12771 if (hover != tree_view->priv->hover_selection)
12773 tree_view->priv->hover_selection = hover;
12775 g_object_notify (G_OBJECT (tree_view), "hover-selection");
12780 * pspp_sheet_view_get_hover_selection:
12781 * @tree_view: a #PsppSheetView
12783 * Returns whether hover selection mode is turned on for @tree_view.
12785 * Return value: %TRUE if @tree_view is in hover selection mode
12790 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12792 return tree_view->priv->hover_selection;
12796 * pspp_sheet_view_set_rubber_banding:
12797 * @tree_view: a #PsppSheetView
12798 * @enable: %TRUE to enable rubber banding
12800 * Enables or disables rubber banding in @tree_view. If the selection mode is
12801 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12802 * banding will allow the user to select multiple rows by dragging the mouse.
12807 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12810 enable = enable != FALSE;
12812 if (enable != tree_view->priv->rubber_banding_enable)
12814 tree_view->priv->rubber_banding_enable = enable;
12816 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12821 * pspp_sheet_view_get_rubber_banding:
12822 * @tree_view: a #PsppSheetView
12824 * Returns whether rubber banding is turned on for @tree_view. If the
12825 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12826 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12827 * select multiple rows by dragging the mouse.
12829 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12834 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12836 return tree_view->priv->rubber_banding_enable;
12840 * pspp_sheet_view_is_rubber_banding_active:
12841 * @tree_view: a #PsppSheetView
12843 * Returns whether a rubber banding operation is currently being done
12846 * Return value: %TRUE if a rubber banding operation is currently being
12847 * done in @tree_view.
12852 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12854 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12856 if (tree_view->priv->rubber_banding_enable
12857 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12864 pspp_sheet_view_grab_notify (GtkWidget *widget,
12865 gboolean was_grabbed)
12867 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12869 tree_view->priv->in_grab = !was_grabbed;
12873 tree_view->priv->pressed_button = -1;
12875 if (tree_view->priv->rubber_band_status)
12876 pspp_sheet_view_stop_rubber_band (tree_view);
12881 pspp_sheet_view_state_changed (GtkWidget *widget,
12882 GtkStateType previous_state)
12884 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12886 if (gtk_widget_get_realized (widget))
12888 GtkStyle *style = gtk_widget_get_style (widget);
12889 gdk_window_set_background (tree_view->priv->bin_window, &style->base[gtk_widget_get_state (widget)]);
12892 gtk_widget_queue_draw (widget);
12896 * pspp_sheet_view_get_grid_lines:
12897 * @tree_view: a #PsppSheetView
12899 * Returns which grid lines are enabled in @tree_view.
12901 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12906 PsppSheetViewGridLines
12907 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12909 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12911 return tree_view->priv->grid_lines;
12915 * pspp_sheet_view_set_grid_lines:
12916 * @tree_view: a #PsppSheetView
12917 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12920 * Sets which grid lines to draw in @tree_view.
12925 pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view,
12926 PsppSheetViewGridLines grid_lines)
12928 PsppSheetViewPrivate *priv;
12929 PsppSheetViewGridLines old_grid_lines;
12931 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12933 priv = tree_view->priv;
12935 old_grid_lines = priv->grid_lines;
12936 priv->grid_lines = grid_lines;
12938 if (old_grid_lines != grid_lines)
12940 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12942 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12947 * pspp_sheet_view_get_special_cells:
12948 * @tree_view: a #PsppSheetView
12950 * Returns which grid lines are enabled in @tree_view.
12952 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12953 * the sheet view contain special cells.
12955 PsppSheetViewSpecialCells
12956 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12958 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12960 return tree_view->priv->special_cells;
12964 * pspp_sheet_view_set_special_cells:
12965 * @tree_view: a #PsppSheetView
12966 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12967 * the sheet view contain special cells.
12969 * Sets whether rows in the sheet view contain special cells, controlling the
12970 * rendering of row selections.
12973 pspp_sheet_view_set_special_cells (PsppSheetView *tree_view,
12974 PsppSheetViewSpecialCells special_cells)
12976 PsppSheetViewPrivate *priv;
12978 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12980 priv = tree_view->priv;
12982 if (priv->special_cells != special_cells)
12984 priv->special_cells = special_cells;
12985 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12986 g_object_notify (G_OBJECT (tree_view), "special-cells");
12991 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12993 /* XXX (re)calculate fixed_height if necessary */
12994 return tree_view->priv->fixed_height;
12998 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
13001 g_return_if_fail (fixed_height > 0);
13003 if (tree_view->priv->fixed_height != fixed_height)
13005 tree_view->priv->fixed_height = fixed_height;
13006 g_object_notify (G_OBJECT (tree_view), "fixed-height");
13008 if (!tree_view->priv->fixed_height_set)
13010 tree_view->priv->fixed_height_set = TRUE;
13011 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
13016 * pspp_sheet_view_set_tooltip_row:
13017 * @tree_view: a #PsppSheetView
13018 * @tooltip: a #GtkTooltip
13019 * @path: a #GtkTreePath
13021 * Sets the tip area of @tooltip to be the area covered by the row at @path.
13022 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13023 * See also gtk_tooltip_set_tip_area().
13028 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
13029 GtkTooltip *tooltip,
13032 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13033 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
13035 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
13039 * pspp_sheet_view_set_tooltip_cell:
13040 * @tree_view: a #PsppSheetView
13041 * @tooltip: a #GtkTooltip
13042 * @path: (allow-none): a #GtkTreePath or %NULL
13043 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
13044 * @cell: (allow-none): a #GtkCellRenderer or %NULL
13046 * Sets the tip area of @tooltip to the area @path, @column and @cell have
13047 * in common. For example if @path is %NULL and @column is set, the tip
13048 * area will be set to the full area covered by @column. See also
13049 * gtk_tooltip_set_tip_area().
13051 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13056 pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view,
13057 GtkTooltip *tooltip,
13059 PsppSheetViewColumn *column,
13060 GtkCellRenderer *cell)
13064 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13065 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
13066 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
13067 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
13069 /* Determine x values. */
13070 if (column && cell)
13075 pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
13076 pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
13078 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13081 rect.width = width;
13087 pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
13088 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13091 rect.width = tmp.width;
13095 GtkAllocation allocation;
13096 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
13098 rect.width = allocation.width;
13101 /* Determine y values. */
13106 pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
13107 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13110 rect.height = tmp.height;
13115 rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
13118 gtk_tooltip_set_tip_area (tooltip, &rect);
13122 * pspp_sheet_view_get_tooltip_context:
13123 * @tree_view: a #PsppSheetView
13124 * @x: the x coordinate (relative to widget coordinates)
13125 * @y: the y coordinate (relative to widget coordinates)
13126 * @keyboard_tip: whether this is a keyboard tooltip or not
13127 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
13128 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
13129 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
13131 * This function is supposed to be used in a #GtkWidget::query-tooltip
13132 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
13133 * which are received in the signal handler, should be passed to this
13134 * function without modification.
13136 * The return value indicates whether there is a tree view row at the given
13137 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
13138 * tooltips the row returned will be the cursor row. When %TRUE, then any of
13139 * @model, @path and @iter which have been provided will be set to point to
13140 * that row and the corresponding model. @x and @y will always be converted
13141 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
13143 * Return value: whether or not the given tooltip context points to a row.
13148 pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view,
13151 gboolean keyboard_tip,
13152 GtkTreeModel **model,
13153 GtkTreePath **path,
13156 GtkTreePath *tmppath = NULL;
13158 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
13159 g_return_val_if_fail (x != NULL, FALSE);
13160 g_return_val_if_fail (y != NULL, FALSE);
13164 pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
13171 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
13174 if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
13175 &tmppath, NULL, NULL, NULL))
13180 *model = pspp_sheet_view_get_model (tree_view);
13183 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
13189 gtk_tree_path_free (tmppath);
13195 pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget,
13198 gboolean keyboard_tip,
13199 GtkTooltip *tooltip,
13202 GValue value = { 0, };
13203 GValue transformed = { 0, };
13206 GtkTreeModel *model;
13207 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
13209 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
13212 &model, &path, &iter))
13215 gtk_tree_model_get_value (model, &iter,
13216 tree_view->priv->tooltip_column, &value);
13218 g_value_init (&transformed, G_TYPE_STRING);
13220 if (!g_value_transform (&value, &transformed))
13222 g_value_unset (&value);
13223 gtk_tree_path_free (path);
13228 g_value_unset (&value);
13230 if (!g_value_get_string (&transformed))
13232 g_value_unset (&transformed);
13233 gtk_tree_path_free (path);
13238 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
13239 pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
13241 gtk_tree_path_free (path);
13242 g_value_unset (&transformed);
13248 * pspp_sheet_view_set_tooltip_column:
13249 * @tree_view: a #PsppSheetView
13250 * @column: an integer, which is a valid column number for @tree_view's model
13252 * If you only plan to have simple (text-only) tooltips on full rows, you
13253 * can use this function to have #PsppSheetView handle these automatically
13254 * for you. @column should be set to the column in @tree_view's model
13255 * containing the tooltip texts, or -1 to disable this feature.
13257 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
13258 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
13260 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
13261 * so &, <, etc have to be escaped in the text.
13266 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
13269 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13271 if (column == tree_view->priv->tooltip_column)
13276 g_signal_handlers_disconnect_by_func (tree_view,
13277 pspp_sheet_view_set_tooltip_query_cb,
13279 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13283 if (tree_view->priv->tooltip_column == -1)
13285 g_signal_connect (tree_view, "query-tooltip",
13286 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13287 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13291 tree_view->priv->tooltip_column = column;
13292 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13296 * pspp_sheet_view_get_tooltip_column:
13297 * @tree_view: a #PsppSheetView
13299 * Returns the column of @tree_view's model which is being used for
13300 * displaying tooltips on @tree_view's rows.
13302 * Return value: the index of the tooltip column that is currently being
13303 * used, or -1 if this is disabled.
13308 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13310 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13312 return tree_view->priv->tooltip_column;
13316 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13317 GValue *return_accu,
13318 const GValue *handler_return,
13321 gboolean continue_emission;
13322 gboolean signal_handled;
13324 signal_handled = g_value_get_boolean (handler_return);
13325 g_value_set_boolean (return_accu, signal_handled);
13326 continue_emission = !signal_handled;
13328 return continue_emission;
13333 pspp_sheet_view_grid_lines_get_type (void)
13335 static GType etype = 0;
13336 if (G_UNLIKELY(etype == 0)) {
13337 static const GEnumValue values[] = {
13338 { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13339 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13340 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13341 { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13344 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13350 pspp_sheet_view_special_cells_get_type (void)
13352 static GType etype = 0;
13353 if (G_UNLIKELY(etype == 0)) {
13354 static const GEnumValue values[] = {
13355 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13356 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13357 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13360 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);