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);
1529 gtk_widget_register_window (widget, window);
1530 gtk_widget_get_allocation (widget, &allocation);
1532 /* Make the window for the tree */
1534 attributes.y = TREE_VIEW_HEADER_HEIGHT (tree_view);
1535 attributes.width = MAX (tree_view->priv->width, allocation.width);
1536 attributes.height = allocation.height;
1537 attributes.event_mask = (GDK_EXPOSURE_MASK |
1539 GDK_POINTER_MOTION_MASK |
1540 GDK_ENTER_NOTIFY_MASK |
1541 GDK_LEAVE_NOTIFY_MASK |
1542 GDK_BUTTON_PRESS_MASK |
1543 GDK_BUTTON_RELEASE_MASK |
1544 gtk_widget_get_events (widget));
1546 tree_view->priv->bin_window = gdk_window_new (window,
1547 &attributes, attributes_mask);
1548 gtk_widget_register_window (widget, tree_view->priv->bin_window);
1549 gtk_widget_get_allocation (widget, &allocation);
1551 /* Make the column header window */
1554 attributes.width = MAX (tree_view->priv->width, allocation.width);
1555 attributes.height = tree_view->priv->header_height;
1556 attributes.event_mask = (GDK_EXPOSURE_MASK |
1558 GDK_BUTTON_PRESS_MASK |
1559 GDK_BUTTON_RELEASE_MASK |
1560 GDK_KEY_PRESS_MASK |
1561 GDK_KEY_RELEASE_MASK |
1562 gtk_widget_get_events (widget));
1564 tree_view->priv->header_window = gdk_window_new (window,
1565 &attributes, attributes_mask);
1566 gtk_widget_register_window (widget, tree_view->priv->header_window);
1568 { /* Ensure Background */
1569 GtkStyleContext *context;
1571 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
1573 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
1574 gtk_style_context_set_background (context, tree_view->priv->header_window);
1577 tmp_list = tree_view->priv->children;
1580 PsppSheetViewChild *child = tmp_list->data;
1581 tmp_list = tmp_list->next;
1583 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
1586 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
1587 _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list->data));
1589 /* Need to call those here, since they create GCs */
1590 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
1592 install_presize_handler (tree_view);
1596 pspp_sheet_view_unrealize (GtkWidget *widget)
1598 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1599 PsppSheetViewPrivate *priv = tree_view->priv;
1602 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->unrealize (widget);
1604 if (priv->scroll_timeout != 0)
1606 g_source_remove (priv->scroll_timeout);
1607 priv->scroll_timeout = 0;
1610 if (priv->open_dest_timeout != 0)
1612 g_source_remove (priv->open_dest_timeout);
1613 priv->open_dest_timeout = 0;
1616 if (priv->presize_handler_timer != 0)
1618 g_source_remove (priv->presize_handler_timer);
1619 priv->presize_handler_timer = 0;
1622 if (priv->validate_rows_timer != 0)
1624 g_source_remove (priv->validate_rows_timer);
1625 priv->validate_rows_timer = 0;
1628 if (priv->scroll_sync_timer != 0)
1630 g_source_remove (priv->scroll_sync_timer);
1631 priv->scroll_sync_timer = 0;
1634 if (priv->typeselect_flush_timeout)
1636 g_source_remove (priv->typeselect_flush_timeout);
1637 priv->typeselect_flush_timeout = 0;
1640 for (list = priv->columns; list; list = list->next)
1641 _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list->data));
1643 gdk_window_set_user_data (priv->bin_window, NULL);
1644 gdk_window_destroy (priv->bin_window);
1645 priv->bin_window = NULL;
1647 gdk_window_set_user_data (priv->header_window, NULL);
1648 gdk_window_destroy (priv->header_window);
1649 priv->header_window = NULL;
1651 if (priv->drag_window)
1653 gdk_window_set_user_data (priv->drag_window, NULL);
1654 gdk_window_destroy (priv->drag_window);
1655 priv->drag_window = NULL;
1658 if (priv->drag_highlight_window)
1660 gdk_window_set_user_data (priv->drag_highlight_window, NULL);
1661 gdk_window_destroy (priv->drag_highlight_window);
1662 priv->drag_highlight_window = NULL;
1665 if (tree_view->priv->columns != NULL)
1667 list = tree_view->priv->columns;
1670 PsppSheetViewColumn *column;
1671 column = PSPP_SHEET_VIEW_COLUMN (list->data);
1673 pspp_sheet_view_remove_column (tree_view, column);
1675 tree_view->priv->columns = NULL;
1679 /* GtkWidget::size_request helper */
1681 pspp_sheet_view_size_request_columns (PsppSheetView *tree_view)
1685 tree_view->priv->header_height = 0;
1687 if (tree_view->priv->model)
1689 for (list = tree_view->priv->columns; list; list = list->next)
1691 GtkRequisition requisition;
1692 PsppSheetViewColumn *column = list->data;
1694 pspp_sheet_view_column_size_request (column, &requisition);
1695 column->button_request = requisition.width;
1696 tree_view->priv->header_height = MAX (tree_view->priv->header_height, requisition.height);
1702 /* Called only by ::size_request */
1704 pspp_sheet_view_update_size (PsppSheetView *tree_view)
1707 PsppSheetViewColumn *column;
1710 if (tree_view->priv->model == NULL)
1712 tree_view->priv->width = 0;
1713 tree_view->priv->prev_width = 0;
1714 tree_view->priv->height = 0;
1718 tree_view->priv->prev_width = tree_view->priv->width;
1719 tree_view->priv->width = 0;
1721 /* keep this in sync with size_allocate below */
1722 for (list = tree_view->priv->columns, i = 0; list; list = list->next, i++)
1724 gint real_requested_width = 0;
1725 column = list->data;
1726 if (!column->visible)
1729 if (column->use_resized_width)
1731 real_requested_width = column->resized_width;
1735 real_requested_width = column->fixed_width;
1738 if (column->min_width != -1)
1739 real_requested_width = MAX (real_requested_width, column->min_width);
1740 if (column->max_width != -1)
1741 real_requested_width = MIN (real_requested_width, column->max_width);
1743 tree_view->priv->width += real_requested_width;
1746 tree_view->priv->height = tree_view->priv->fixed_height * tree_view->priv->row_count;
1750 pspp_sheet_view_size_request (GtkWidget *widget,
1751 GtkRequisition *requisition)
1753 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
1756 /* we validate some rows initially just to make sure we have some size.
1757 * In practice, with a lot of static lists, this should get a good width.
1759 initialize_fixed_height_mode (tree_view);
1760 pspp_sheet_view_size_request_columns (tree_view);
1761 pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget));
1763 requisition->width = tree_view->priv->width;
1764 requisition->height = tree_view->priv->height + TREE_VIEW_HEADER_HEIGHT (tree_view);
1766 tmp_list = tree_view->priv->children;
1770 PsppSheetViewChild *child = tmp_list->data;
1771 GtkRequisition child_requisition;
1773 tmp_list = tmp_list->next;
1775 if (gtk_widget_get_visible (child->widget))
1776 gtk_widget_size_request (child->widget, &child_requisition);
1781 invalidate_column (PsppSheetView *tree_view,
1782 PsppSheetViewColumn *column)
1784 gint column_offset = 0;
1786 GtkWidget *widget = GTK_WIDGET (tree_view);
1789 if (!gtk_widget_get_realized (widget))
1792 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1793 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
1795 list = (rtl ? list->prev : list->next))
1797 PsppSheetViewColumn *tmpcolumn = list->data;
1798 if (tmpcolumn == column)
1800 GdkRectangle invalid_rect;
1801 GtkAllocation allocation;
1803 gtk_widget_get_allocation (widget, &allocation);
1804 invalid_rect.x = column_offset;
1806 invalid_rect.width = column->width;
1807 invalid_rect.height = allocation.height;
1809 gdk_window_invalidate_rect (gtk_widget_get_window (widget), &invalid_rect, TRUE);
1813 column_offset += tmpcolumn->width;
1818 invalidate_last_column (PsppSheetView *tree_view)
1823 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1825 for (last_column = (rtl ? g_list_first (tree_view->priv->columns) : g_list_last (tree_view->priv->columns));
1827 last_column = (rtl ? last_column->next : last_column->prev))
1829 if (PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible)
1831 invalidate_column (tree_view, last_column->data);
1838 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView *tree_view,
1839 PsppSheetViewColumn *column)
1841 gint real_requested_width;
1843 if (column->use_resized_width)
1845 real_requested_width = column->resized_width;
1849 real_requested_width = column->fixed_width;
1852 if (column->min_width != -1)
1853 real_requested_width = MAX (real_requested_width, column->min_width);
1854 if (column->max_width != -1)
1855 real_requested_width = MIN (real_requested_width, column->max_width);
1857 return real_requested_width;
1861 span_intersects (int a0, int a_width,
1862 int b0, int b_width)
1864 int a1 = a0 + a_width;
1865 int b1 = b0 + b_width;
1866 return (a0 >= b0 && a0 < b1) || (b0 >= a0 && b0 < a1);
1869 /* GtkWidget::size_allocate helper */
1871 pspp_sheet_view_size_allocate_columns (GtkWidget *widget,
1872 gboolean *width_changed)
1874 PsppSheetView *tree_view;
1875 GList *list, *first_column, *last_column;
1876 PsppSheetViewColumn *column;
1877 GtkAllocation col_allocation;
1878 GtkAllocation allocation;
1880 gint extra, extra_per_column;
1881 gint full_requested_width = 0;
1882 gint number_of_expand_columns = 0;
1883 gboolean column_changed = FALSE;
1886 tree_view = PSPP_SHEET_VIEW (widget);
1888 for (last_column = g_list_last (tree_view->priv->columns);
1889 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
1890 last_column = last_column->prev)
1893 if (last_column == NULL)
1896 for (first_column = g_list_first (tree_view->priv->columns);
1897 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
1898 first_column = first_column->next)
1901 col_allocation.y = 0;
1902 col_allocation.height = tree_view->priv->header_height;
1904 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1906 /* find out how many extra space and expandable columns we have */
1907 for (list = tree_view->priv->columns; list != last_column->next; list = list->next)
1909 column = (PsppSheetViewColumn *)list->data;
1911 if (!column->visible)
1914 full_requested_width += pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1917 number_of_expand_columns++;
1920 gtk_widget_get_allocation (widget, &allocation);
1921 extra = MAX (allocation.width - full_requested_width, 0);
1922 if (number_of_expand_columns > 0)
1923 extra_per_column = extra/number_of_expand_columns;
1925 extra_per_column = 0;
1927 for (list = (rtl ? last_column : first_column);
1928 list != (rtl ? first_column->prev : last_column->next);
1929 list = (rtl ? list->prev : list->next))
1931 gint real_requested_width = 0;
1934 column = list->data;
1935 old_width = column->width;
1937 if (!column->visible)
1940 /* We need to handle the dragged button specially.
1942 if (column == tree_view->priv->drag_column)
1944 GtkAllocation drag_allocation;
1945 drag_allocation.width = gdk_window_get_width (tree_view->priv->drag_window);
1946 drag_allocation.height = gdk_window_get_height (tree_view->priv->drag_window);
1947 drag_allocation.x = 0;
1948 drag_allocation.y = 0;
1949 pspp_sheet_view_column_size_allocate (tree_view->priv->drag_column,
1951 width += drag_allocation.width;
1955 real_requested_width = pspp_sheet_view_get_real_requested_width_from_column (tree_view, column);
1957 col_allocation.x = width;
1958 column->width = real_requested_width;
1962 if (number_of_expand_columns == 1)
1964 /* We add the remander to the last column as
1966 column->width += extra;
1970 column->width += extra_per_column;
1971 extra -= extra_per_column;
1972 number_of_expand_columns --;
1976 if (column->width != old_width)
1977 g_object_notify (G_OBJECT (column), "width");
1979 col_allocation.width = column->width;
1980 width += column->width;
1982 if (column->width > old_width)
1983 column_changed = TRUE;
1985 pspp_sheet_view_column_size_allocate (column, &col_allocation);
1988 gdk_window_move_resize (column->window,
1989 col_allocation.x + (rtl ? 0 : col_allocation.width) - TREE_VIEW_DRAG_WIDTH/2,
1991 TREE_VIEW_DRAG_WIDTH, col_allocation.height);
1994 /* We change the width here. The user might have been resizing columns,
1995 * so the total width of the tree view changes.
1997 tree_view->priv->width = width;
1999 *width_changed = TRUE;
2002 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2006 pspp_sheet_view_size_allocate (GtkWidget *widget,
2007 GtkAllocation *allocation)
2009 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2011 gboolean width_changed = FALSE;
2012 GtkAllocation old_allocation;
2013 gtk_widget_get_allocation (widget, &old_allocation);
2015 if (allocation->width != old_allocation.width)
2016 width_changed = TRUE;
2019 gtk_widget_set_allocation (widget, allocation);
2021 tmp_list = tree_view->priv->children;
2025 GtkAllocation allocation;
2027 PsppSheetViewChild *child = tmp_list->data;
2028 tmp_list = tmp_list->next;
2030 /* totally ignore our child's requisition */
2031 allocation.x = child->x;
2032 allocation.y = child->y;
2033 allocation.width = child->width;
2034 allocation.height = child->height;
2035 gtk_widget_size_allocate (child->widget, &allocation);
2038 /* We size-allocate the columns first because the width of the
2039 * tree view (used in updating the adjustments below) might change.
2041 pspp_sheet_view_size_allocate_columns (widget, &width_changed);
2043 gtk_adjustment_set_page_size (tree_view->priv->hadjustment, allocation->width);
2044 gtk_adjustment_set_page_increment (tree_view->priv->hadjustment, allocation->width * 0.9);
2045 gtk_adjustment_set_step_increment (tree_view->priv->hadjustment, allocation->width * 0.1);
2046 gtk_adjustment_set_lower (tree_view->priv->hadjustment, 0);
2047 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->hadjustment), tree_view->priv->width));
2049 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
2051 if (allocation->width < tree_view->priv->width)
2053 if (tree_view->priv->init_hadjust_value)
2055 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2056 tree_view->priv->init_hadjust_value = FALSE;
2058 else if (allocation->width != old_allocation.width)
2060 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));
2063 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));
2067 gtk_adjustment_set_value (tree_view->priv->hadjustment, 0);
2068 tree_view->priv->init_hadjust_value = TRUE;
2072 if (gtk_adjustment_get_value (tree_view->priv->hadjustment) + allocation->width > tree_view->priv->width)
2073 gtk_adjustment_set_value (tree_view->priv->hadjustment, MAX (tree_view->priv->width - allocation->width, 0));
2075 gtk_adjustment_changed (tree_view->priv->hadjustment);
2077 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2078 gtk_adjustment_set_step_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.1);
2079 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, gtk_adjustment_get_page_size (tree_view->priv->vadjustment) * 0.9);
2080 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
2081 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_page_size (tree_view->priv->vadjustment), tree_view->priv->height));
2083 gtk_adjustment_changed (tree_view->priv->vadjustment);
2085 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2086 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
2087 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
2088 else if (gtk_adjustment_get_value (tree_view->priv->vadjustment) + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
2089 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment),
2090 tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
2091 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
2092 pspp_sheet_view_top_row_to_dy (tree_view);
2094 pspp_sheet_view_dy_to_top_row (tree_view);
2096 if (gtk_widget_get_realized (widget))
2098 gdk_window_move_resize (gtk_widget_get_window (widget),
2099 allocation->x, allocation->y,
2100 allocation->width, allocation->height);
2101 gdk_window_move_resize (tree_view->priv->header_window,
2102 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2104 MAX (tree_view->priv->width, allocation->width),
2105 tree_view->priv->header_height);
2106 gdk_window_move_resize (tree_view->priv->bin_window,
2107 - (gint) gtk_adjustment_get_value (tree_view->priv->hadjustment),
2108 TREE_VIEW_HEADER_HEIGHT (tree_view),
2109 MAX (tree_view->priv->width, allocation->width),
2110 allocation->height - TREE_VIEW_HEADER_HEIGHT (tree_view));
2113 if (tree_view->priv->row_count == 0)
2114 invalidate_empty_focus (tree_view);
2116 if (gtk_widget_get_realized (widget))
2118 gboolean has_expand_column = FALSE;
2119 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
2121 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)))
2123 has_expand_column = TRUE;
2128 /* This little hack only works if we have an LTR locale, and no column has the */
2131 if (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_LTR &&
2132 ! has_expand_column)
2133 invalidate_last_column (tree_view);
2135 gtk_widget_queue_draw (widget);
2140 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2142 grab_focus_and_unset_draw_keyfocus (PsppSheetView *tree_view)
2144 GtkWidget *widget = GTK_WIDGET (tree_view);
2146 if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
2147 gtk_widget_grab_focus (widget);
2148 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2152 pspp_sheet_view_node_is_selected (PsppSheetView *tree_view,
2155 return node >= 0 && range_tower_contains (tree_view->priv->selected, node);
2159 pspp_sheet_view_node_select (PsppSheetView *tree_view,
2162 range_tower_set1 (tree_view->priv->selected, node, 1);
2166 pspp_sheet_view_node_unselect (PsppSheetView *tree_view,
2169 range_tower_set0 (tree_view->priv->selected, node, 1);
2173 pspp_sheet_view_node_next (PsppSheetView *tree_view,
2176 return node + 1 < tree_view->priv->row_count ? node + 1 : -1;
2180 pspp_sheet_view_node_prev (PsppSheetView *tree_view,
2183 return node > 0 ? node - 1 : -1;
2187 all_columns_selected (PsppSheetView *tree_view)
2191 for (list = tree_view->priv->columns; list; list = list->next)
2193 PsppSheetViewColumn *column = list->data;
2194 if (column->selectable && !column->selected)
2202 pspp_sheet_view_row_head_clicked (PsppSheetView *tree_view,
2204 PsppSheetViewColumn *column,
2205 GdkEventButton *event)
2207 PsppSheetSelection *selection;
2208 PsppSheetSelectionMode mode;
2210 gboolean update_anchor;
2214 g_return_val_if_fail (tree_view != NULL, FALSE);
2215 g_return_val_if_fail (column != NULL, FALSE);
2217 selection = tree_view->priv->selection;
2218 mode = pspp_sheet_selection_get_mode (selection);
2219 if (mode != PSPP_SHEET_SELECTION_RECTANGLE)
2222 if (!column->row_head)
2227 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2228 if (event->type != GDK_BUTTON_PRESS
2229 || (modifiers != GDK_CONTROL_MASK && modifiers != GDK_SHIFT_MASK))
2235 path = gtk_tree_path_new_from_indices (node, -1);
2238 pspp_sheet_selection_unselect_all (selection);
2239 pspp_sheet_selection_select_path (selection, path);
2240 pspp_sheet_selection_select_all_columns (selection);
2241 update_anchor = TRUE;
2244 else if (event->type == GDK_BUTTON_PRESS && event->button == 3)
2246 if (pspp_sheet_selection_count_selected_rows (selection) <= 1
2247 || !all_columns_selected (tree_view))
2249 pspp_sheet_selection_unselect_all (selection);
2250 pspp_sheet_selection_select_path (selection, path);
2251 pspp_sheet_selection_select_all_columns (selection);
2252 update_anchor = TRUE;
2256 update_anchor = handled = FALSE;
2258 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2259 && modifiers == GDK_CONTROL_MASK)
2261 if (!all_columns_selected (tree_view))
2263 pspp_sheet_selection_unselect_all (selection);
2264 pspp_sheet_selection_select_all_columns (selection);
2267 if (pspp_sheet_selection_path_is_selected (selection, path))
2268 pspp_sheet_selection_unselect_path (selection, path);
2270 pspp_sheet_selection_select_path (selection, path);
2271 update_anchor = TRUE;
2274 else if (event->type == GDK_BUTTON_PRESS && event->button == 1
2275 && modifiers == GDK_SHIFT_MASK)
2277 GtkTreeRowReference *anchor = tree_view->priv->anchor;
2278 GtkTreePath *anchor_path;
2280 if (all_columns_selected (tree_view)
2281 && gtk_tree_row_reference_valid (anchor))
2283 update_anchor = FALSE;
2284 anchor_path = gtk_tree_row_reference_get_path (anchor);
2288 update_anchor = TRUE;
2289 anchor_path = gtk_tree_path_copy (path);
2292 pspp_sheet_selection_unselect_all (selection);
2293 pspp_sheet_selection_select_range (selection, anchor_path, path);
2294 pspp_sheet_selection_select_all_columns (selection);
2296 gtk_tree_path_free (anchor_path);
2301 update_anchor = handled = FALSE;
2305 if (tree_view->priv->anchor)
2306 gtk_tree_row_reference_free (tree_view->priv->anchor);
2307 tree_view->priv->anchor =
2308 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
2309 tree_view->priv->model,
2313 gtk_tree_path_free (path);
2318 find_click (PsppSheetView *tree_view,
2321 PsppSheetViewColumn **column,
2322 GdkRectangle *background_area,
2323 GdkRectangle *cell_area)
2330 /* find the node that was clicked */
2331 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, y);
2334 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, node);
2339 background_area->y = y_offset + y;
2340 background_area->height = ROW_HEIGHT (tree_view);
2341 background_area->x = 0;
2343 /* Let the column have a chance at selecting it. */
2344 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
2345 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
2346 list; list = (rtl ? list->prev : list->next))
2348 PsppSheetViewColumn *candidate = list->data;
2350 if (!candidate->visible)
2353 background_area->width = candidate->width;
2354 if ((background_area->x > x) ||
2355 (background_area->x + background_area->width <= x))
2357 background_area->x += background_area->width;
2361 /* we found the focus column */
2363 pspp_sheet_view_adjust_cell_area (tree_view, candidate, background_area,
2365 *column = candidate;
2373 pspp_sheet_view_button_press (GtkWidget *widget,
2374 GdkEventButton *event)
2376 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2378 PsppSheetViewColumn *column = NULL;
2380 GdkRectangle background_area;
2381 GdkRectangle cell_area;
2384 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2385 pspp_sheet_view_stop_editing (tree_view, FALSE);
2388 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2389 * we're done handling the button press.
2392 if (event->window == tree_view->priv->bin_window)
2397 gint pre_val, aft_val;
2398 PsppSheetViewColumn *column = NULL;
2399 GtkCellRenderer *focus_cell = NULL;
2400 gboolean row_double_click = FALSE;
2403 if (tree_view->priv->row_count == 0)
2405 grab_focus_and_unset_draw_keyfocus (tree_view);
2409 if (!find_click (tree_view, event->x, event->y, &node, &column,
2410 &background_area, &cell_area))
2412 grab_focus_and_unset_draw_keyfocus (tree_view);
2416 tree_view->priv->focus_column = column;
2418 if (pspp_sheet_view_row_head_clicked (tree_view, node, column, event))
2422 pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2424 path = _pspp_sheet_view_find_path (tree_view, node);
2426 /* we only handle selection modifications on the first button press
2428 if (event->type == GDK_BUTTON_PRESS)
2430 PsppSheetSelectionMode mode = 0;
2432 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2433 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
2434 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2435 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
2437 focus_cell = _pspp_sheet_view_column_get_cell_at_pos (column, event->x - background_area.x);
2439 pspp_sheet_view_column_focus_cell (column, focus_cell);
2441 if (event->state & GDK_CONTROL_MASK)
2443 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, mode);
2444 pspp_sheet_view_real_toggle_cursor_row (tree_view);
2446 else if (event->state & GDK_SHIFT_MASK)
2448 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
2449 pspp_sheet_view_real_select_cursor_row (tree_view, FALSE, mode);
2453 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
2456 if (tree_view->priv->anchor_column == NULL ||
2457 !(event->state & GDK_SHIFT_MASK))
2458 tree_view->priv->anchor_column = column;
2459 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
2460 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
2461 tree_view->priv->anchor_column,
2465 /* the treeview may have been scrolled because of _set_cursor,
2469 aft_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
2470 dval = pre_val - aft_val;
2472 cell_area.y += dval;
2473 background_area.y += dval;
2475 /* Save press to possibly begin a drag
2477 if (!tree_view->priv->in_grab &&
2478 tree_view->priv->pressed_button < 0)
2480 tree_view->priv->pressed_button = event->button;
2481 tree_view->priv->press_start_x = event->x;
2482 tree_view->priv->press_start_y = event->y;
2483 tree_view->priv->press_start_node = node;
2485 if (tree_view->priv->rubber_banding_enable
2486 && (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
2487 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE))
2489 tree_view->priv->press_start_y += tree_view->priv->dy;
2490 tree_view->priv->rubber_band_x = event->x;
2491 tree_view->priv->rubber_band_y = event->y + tree_view->priv->dy;
2492 tree_view->priv->rubber_band_status = RUBBER_BAND_MAYBE_START;
2494 if ((event->state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
2495 tree_view->priv->rubber_band_ctrl = TRUE;
2496 if ((event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
2497 tree_view->priv->rubber_band_shift = TRUE;
2502 /* Test if a double click happened on the same row. */
2503 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2505 int double_click_time, double_click_distance;
2507 g_object_get (gtk_settings_get_for_screen (
2508 gtk_widget_get_screen (widget)),
2509 "gtk-double-click-time", &double_click_time,
2510 "gtk-double-click-distance", &double_click_distance,
2513 /* Same conditions as _gdk_event_button_generate */
2514 if (tree_view->priv->last_button_x != -1 &&
2515 (event->time < tree_view->priv->last_button_time + double_click_time) &&
2516 (ABS (event->x - tree_view->priv->last_button_x) <= double_click_distance) &&
2517 (ABS (event->y - tree_view->priv->last_button_y) <= double_click_distance))
2519 /* We do no longer compare paths of this row and the
2520 * row clicked previously. We use the double click
2521 * distance to decide whether this is a valid click,
2522 * allowing the mouse to slightly move over another row.
2524 row_double_click = TRUE;
2526 tree_view->priv->last_button_time = 0;
2527 tree_view->priv->last_button_x = -1;
2528 tree_view->priv->last_button_y = -1;
2532 tree_view->priv->last_button_time = event->time;
2533 tree_view->priv->last_button_x = event->x;
2534 tree_view->priv->last_button_y = event->y;
2538 if (row_double_click)
2540 gtk_grab_remove (widget);
2541 pspp_sheet_view_row_activated (tree_view, path, column);
2543 if (tree_view->priv->pressed_button == event->button)
2544 tree_view->priv->pressed_button = -1;
2547 gtk_tree_path_free (path);
2549 /* If we activated the row through a double click we don't want to grab
2550 * focus back, as moving focus to another widget is pretty common.
2552 if (!row_double_click)
2553 grab_focus_and_unset_draw_keyfocus (tree_view);
2558 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2560 for (i = 0, list = tree_view->priv->columns; list; list = list->next, i++)
2562 column = list->data;
2563 if (event->window == column->window &&
2564 column->resizable &&
2569 if (gdk_pointer_grab (column->window, FALSE,
2570 GDK_POINTER_MOTION_HINT_MASK |
2571 GDK_BUTTON1_MOTION_MASK |
2572 GDK_BUTTON_RELEASE_MASK,
2573 NULL, NULL, event->time))
2576 gtk_grab_add (widget);
2577 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2578 column->resized_width = column->width;
2580 /* block attached dnd signal handler */
2581 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2583 g_signal_handlers_block_matched (widget,
2584 G_SIGNAL_MATCH_DATA,
2588 tree_view->priv->drag_pos = i;
2589 tree_view->priv->x_drag = column->allocation.x + (rtl ? 0 : column->allocation.width);
2591 if (!gtk_widget_has_focus (widget))
2592 gtk_widget_grab_focus (widget);
2600 /* GtkWidget::button_release_event helper */
2602 pspp_sheet_view_button_release_drag_column (GtkWidget *widget,
2603 GdkEventButton *event)
2605 PsppSheetView *tree_view;
2609 tree_view = PSPP_SHEET_VIEW (widget);
2611 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
2612 gdk_display_pointer_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2613 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), GDK_CURRENT_TIME);
2615 /* Move the button back */
2616 g_return_val_if_fail (tree_view->priv->drag_column->button, FALSE);
2618 g_object_ref (tree_view->priv->drag_column->button);
2619 gtk_container_remove (GTK_CONTAINER (tree_view), tree_view->priv->drag_column->button);
2620 gtk_widget_set_parent_window (tree_view->priv->drag_column->button, tree_view->priv->header_window);
2621 gtk_widget_set_parent (tree_view->priv->drag_column->button, GTK_WIDGET (tree_view));
2622 g_object_unref (tree_view->priv->drag_column->button);
2623 gtk_widget_queue_resize (widget);
2624 if (tree_view->priv->drag_column->resizable)
2626 gdk_window_raise (tree_view->priv->drag_column->window);
2627 gdk_window_show (tree_view->priv->drag_column->window);
2630 gdk_window_hide (tree_view->priv->drag_column->window);
2632 gtk_widget_grab_focus (tree_view->priv->drag_column->button);
2636 if (tree_view->priv->cur_reorder &&
2637 tree_view->priv->cur_reorder->right_column != tree_view->priv->drag_column)
2638 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2639 tree_view->priv->cur_reorder->right_column);
2643 if (tree_view->priv->cur_reorder &&
2644 tree_view->priv->cur_reorder->left_column != tree_view->priv->drag_column)
2645 pspp_sheet_view_move_column_after (tree_view, tree_view->priv->drag_column,
2646 tree_view->priv->cur_reorder->left_column);
2648 tree_view->priv->drag_column = NULL;
2649 gdk_window_hide (tree_view->priv->drag_window);
2651 for (l = tree_view->priv->column_drag_info; l != NULL; l = l->next)
2652 g_slice_free (PsppSheetViewColumnReorder, l->data);
2653 g_list_free (tree_view->priv->column_drag_info);
2654 tree_view->priv->column_drag_info = NULL;
2655 tree_view->priv->cur_reorder = NULL;
2657 if (tree_view->priv->drag_highlight_window)
2658 gdk_window_hide (tree_view->priv->drag_highlight_window);
2660 /* Reset our flags */
2661 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_UNSET;
2662 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
2667 /* GtkWidget::button_release_event helper */
2669 pspp_sheet_view_button_release_column_resize (GtkWidget *widget,
2670 GdkEventButton *event)
2672 PsppSheetView *tree_view;
2675 tree_view = PSPP_SHEET_VIEW (widget);
2677 tree_view->priv->drag_pos = -1;
2679 /* unblock attached dnd signal handler */
2680 drag_data = g_object_get_data (G_OBJECT (widget), "gtk-site-data");
2682 g_signal_handlers_unblock_matched (widget,
2683 G_SIGNAL_MATCH_DATA,
2687 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE);
2688 gtk_grab_remove (widget);
2689 gdk_display_pointer_ungrab (gdk_window_get_display (event->window),
2695 pspp_sheet_view_button_release_edit (PsppSheetView *tree_view,
2696 GdkEventButton *event)
2698 GtkCellEditable *cell_editable;
2703 PsppSheetViewColumn *column;
2704 GdkRectangle background_area;
2705 GdkRectangle cell_area;
2711 if (event->window != tree_view->priv->bin_window)
2714 /* Ignore a released button, if that button wasn't depressed */
2715 if (tree_view->priv->pressed_button != event->button)
2718 if (!find_click (tree_view, event->x, event->y, &node, &column, &background_area,
2722 /* decide if we edit */
2723 path = _pspp_sheet_view_find_path (tree_view, node);
2724 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
2725 if (event->button != 1 || modifiers)
2728 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
2729 pspp_sheet_view_column_cell_set_cell_data (column,
2730 tree_view->priv->model,
2733 if (!pspp_sheet_view_column_get_quick_edit (column)
2734 && _pspp_sheet_view_column_has_editable_cell (column))
2737 flags = 0; /* FIXME: get the right flags */
2738 path_string = gtk_tree_path_to_string (path);
2740 if (!_pspp_sheet_view_column_cell_event (column,
2748 if (cell_editable == NULL)
2751 pspp_sheet_view_real_set_cursor (tree_view, path,
2752 TRUE, TRUE, 0); /* XXX mode? */
2753 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
2756 _pspp_sheet_view_column_get_neighbor_sizes (
2757 column, _pspp_sheet_view_column_get_edited_cell (column), &left, &right);
2760 area.width -= right + left;
2762 pspp_sheet_view_real_start_editing (tree_view,
2769 g_free (path_string);
2770 gtk_tree_path_free (path);
2775 pspp_sheet_view_button_release (GtkWidget *widget,
2776 GdkEventButton *event)
2778 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2780 pspp_sheet_view_stop_editing (tree_view, FALSE);
2781 if (tree_view->priv->rubber_band_status != RUBBER_BAND_ACTIVE
2782 && pspp_sheet_view_button_release_edit (tree_view, event))
2784 if (tree_view->priv->pressed_button == event->button)
2785 tree_view->priv->pressed_button = -1;
2787 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
2791 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2792 return pspp_sheet_view_button_release_drag_column (widget, event);
2794 if (tree_view->priv->rubber_band_status)
2795 pspp_sheet_view_stop_rubber_band (tree_view);
2797 if (tree_view->priv->pressed_button == event->button)
2798 tree_view->priv->pressed_button = -1;
2800 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2801 return pspp_sheet_view_button_release_column_resize (widget, event);
2807 pspp_sheet_view_grab_broken (GtkWidget *widget,
2808 GdkEventGrabBroken *event)
2810 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
2812 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
2813 pspp_sheet_view_button_release_drag_column (widget, (GdkEventButton *)event);
2815 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
2816 pspp_sheet_view_button_release_column_resize (widget, (GdkEventButton *)event);
2821 /* GtkWidget::motion_event function set.
2825 do_prelight (PsppSheetView *tree_view,
2827 /* these are in bin_window coords */
2831 int prev_node = tree_view->priv->prelight_node;
2833 if (prev_node != node)
2835 tree_view->priv->prelight_node = node;
2838 _pspp_sheet_view_queue_draw_node (tree_view, prev_node, NULL);
2841 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
2847 prelight_or_select (PsppSheetView *tree_view,
2849 /* these are in bin_window coords */
2853 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
2855 if (tree_view->priv->hover_selection &&
2856 (mode == PSPP_SHEET_SELECTION_SINGLE || mode == PSPP_SHEET_SELECTION_BROWSE) &&
2857 !(tree_view->priv->edited_column &&
2858 tree_view->priv->edited_column->editable_widget))
2862 if (!pspp_sheet_view_node_is_selected (tree_view, node))
2866 path = _pspp_sheet_view_find_path (tree_view, node);
2867 pspp_sheet_selection_select_path (tree_view->priv->selection, path);
2868 if (pspp_sheet_view_node_is_selected (tree_view, node))
2870 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
2871 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, FALSE, 0); /* XXX mode? */
2873 gtk_tree_path_free (path);
2877 else if (mode == PSPP_SHEET_SELECTION_SINGLE)
2878 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
2881 do_prelight (tree_view, node, x, y);
2885 ensure_unprelighted (PsppSheetView *tree_view)
2887 do_prelight (tree_view,
2889 -1000, -1000); /* coords not possibly over an arrow */
2891 g_assert (tree_view->priv->prelight_node < 0);
2895 update_prelight (PsppSheetView *tree_view,
2902 if (tree_view->priv->row_count == 0)
2907 ensure_unprelighted (tree_view);
2911 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y);
2915 pspp_sheet_view_find_offset (tree_view, new_y, &node);
2918 prelight_or_select (tree_view, node, x, y);
2924 /* Our motion arrow is either a box (in the case of the original spot)
2925 * or an arrow. It is expander_size wide.
2948 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView *tree_view)
2951 PsppSheetViewColumnReorder *reorder = tree_view->priv->cur_reorder;
2952 GtkWidget *widget = GTK_WIDGET (tree_view);
2953 GdkBitmap *mask = NULL;
2958 gint arrow_type = DRAG_COLUMN_WINDOW_STATE_UNSET;
2959 GdkWindowAttr attributes;
2960 guint attributes_mask;
2963 reorder->left_column == tree_view->priv->drag_column ||
2964 reorder->right_column == tree_view->priv->drag_column)
2965 arrow_type = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
2966 else if (reorder->left_column || reorder->right_column)
2968 GdkRectangle visible_rect;
2969 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
2970 if (reorder->left_column)
2971 x = reorder->left_column->allocation.x + reorder->left_column->allocation.width;
2973 x = reorder->right_column->allocation.x;
2975 if (x < visible_rect.x)
2976 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT;
2977 else if (x > visible_rect.x + visible_rect.width)
2978 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT;
2980 arrow_type = DRAG_COLUMN_WINDOW_STATE_ARROW;
2983 /* We want to draw the rectangle over the initial location. */
2984 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2989 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ORIGINAL)
2991 if (tree_view->priv->drag_highlight_window)
2993 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
2995 gdk_window_destroy (tree_view->priv->drag_highlight_window);
2998 attributes.window_type = GDK_WINDOW_CHILD;
2999 attributes.wclass = GDK_INPUT_OUTPUT;
3000 attributes.x = tree_view->priv->drag_column_x;
3002 width = attributes.width = tree_view->priv->drag_column->allocation.width;
3003 height = attributes.height = tree_view->priv->drag_column->allocation.height;
3004 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3005 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3006 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3007 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3008 tree_view->priv->drag_highlight_window = gdk_window_new (tree_view->priv->header_window, &attributes, attributes_mask);
3009 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3011 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3012 gc = gdk_gc_new (mask);
3014 gdk_gc_set_foreground (gc, &col);
3015 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3017 gdk_gc_set_foreground(gc, &col);
3018 gdk_draw_rectangle (mask, gc, TRUE, 2, 2, width - 4, height - 4);
3019 g_object_unref (gc);
3021 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3023 if (mask) g_object_unref (mask);
3024 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ORIGINAL;
3027 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW)
3033 width = tree_view->priv->expander_size;
3035 /* Get x, y, width, height of arrow */
3036 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
3037 if (reorder->left_column)
3039 x += reorder->left_column->allocation.x + reorder->left_column->allocation.width - width/2;
3040 height = reorder->left_column->allocation.height;
3044 x += reorder->right_column->allocation.x - width/2;
3045 height = reorder->right_column->allocation.height;
3047 y -= tree_view->priv->expander_size/2; /* The arrow takes up only half the space */
3048 height += tree_view->priv->expander_size;
3050 /* Create the new window */
3051 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW)
3053 if (tree_view->priv->drag_highlight_window)
3055 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3057 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3060 attributes.window_type = GDK_WINDOW_TEMP;
3061 attributes.wclass = GDK_INPUT_OUTPUT;
3062 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3063 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3064 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3065 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3068 attributes.width = width;
3069 attributes.height = height;
3070 tree_view->priv->drag_highlight_window = gdk_window_new (gtk_widget_get_root_window (widget),
3071 &attributes, attributes_mask);
3072 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3074 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3075 gc = gdk_gc_new (mask);
3077 gdk_gc_set_foreground (gc, &col);
3078 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3080 /* Draw the 2 arrows as per above */
3082 gdk_gc_set_foreground (gc, &col);
3083 for (i = 0; i < width; i ++)
3085 if (i == (width/2 - 1))
3087 gdk_draw_line (mask, gc, i, j, i, height - j);
3088 if (i < (width/2 - 1))
3093 g_object_unref (gc);
3094 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3096 if (mask) g_object_unref (mask);
3099 tree_view->priv->drag_column_window_state = DRAG_COLUMN_WINDOW_STATE_ARROW;
3100 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3102 else if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT ||
3103 arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3109 width = tree_view->priv->expander_size;
3111 /* Get x, y, width, height of arrow */
3112 width = width/2; /* remember, the arrow only takes half the available width */
3113 gdk_window_get_origin (gtk_widget_get_window (widget), &x, &y);
3114 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3115 x += widget->allocation.width - width;
3117 if (reorder->left_column)
3118 height = reorder->left_column->allocation.height;
3120 height = reorder->right_column->allocation.height;
3122 y -= tree_view->priv->expander_size;
3123 height += 2*tree_view->priv->expander_size;
3125 /* Create the new window */
3126 if (tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT &&
3127 tree_view->priv->drag_column_window_state != DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT)
3129 if (tree_view->priv->drag_highlight_window)
3131 gdk_window_set_user_data (tree_view->priv->drag_highlight_window,
3133 gdk_window_destroy (tree_view->priv->drag_highlight_window);
3136 attributes.window_type = GDK_WINDOW_TEMP;
3137 attributes.wclass = GDK_INPUT_OUTPUT;
3138 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
3139 attributes.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
3140 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3141 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
3144 attributes.width = width;
3145 attributes.height = height;
3146 tree_view->priv->drag_highlight_window = gdk_window_new (NULL, &attributes, attributes_mask);
3147 gdk_window_set_user_data (tree_view->priv->drag_highlight_window, GTK_WIDGET (tree_view));
3149 mask = gdk_pixmap_new (tree_view->priv->drag_highlight_window, width, height, 1);
3150 gc = gdk_gc_new (mask);
3152 gdk_gc_set_foreground (gc, &col);
3153 gdk_draw_rectangle (mask, gc, TRUE, 0, 0, width, height);
3155 /* Draw the 2 arrows as per above */
3157 gdk_gc_set_foreground (gc, &col);
3158 j = tree_view->priv->expander_size;
3159 for (i = 0; i < width; i ++)
3162 if (arrow_type == DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT)
3166 gdk_draw_line (mask, gc, k, j, k, height - j);
3167 gdk_draw_line (mask, gc, k, 0, k, tree_view->priv->expander_size - j);
3168 gdk_draw_line (mask, gc, k, height, k, height - tree_view->priv->expander_size + j);
3171 g_object_unref (gc);
3172 gdk_window_shape_combine_mask (tree_view->priv->drag_highlight_window,
3174 if (mask) g_object_unref (mask);
3177 tree_view->priv->drag_column_window_state = arrow_type;
3178 gdk_window_move (tree_view->priv->drag_highlight_window, x, y);
3182 g_warning (G_STRLOC"Invalid PsppSheetViewColumnReorder struct");
3183 gdk_window_hide (tree_view->priv->drag_highlight_window);
3187 gdk_window_show (tree_view->priv->drag_highlight_window);
3188 gdk_window_raise (tree_view->priv->drag_highlight_window);
3193 pspp_sheet_view_motion_resize_column (GtkWidget *widget,
3194 GdkEventMotion *event)
3198 PsppSheetViewColumn *column;
3199 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3201 column = pspp_sheet_view_get_column (tree_view, tree_view->priv->drag_pos);
3203 if (event->is_hint || event->window != gtk_widget_get_window (widget))
3204 gtk_widget_get_pointer (widget, &x, NULL);
3208 if (tree_view->priv->hadjustment)
3209 x += gtk_adjustment_get_value (tree_view->priv->hadjustment);
3211 new_width = pspp_sheet_view_new_column_width (tree_view,
3212 tree_view->priv->drag_pos, &x);
3213 if (x != tree_view->priv->x_drag &&
3214 (new_width != column->fixed_width))
3216 column->use_resized_width = TRUE;
3217 column->resized_width = new_width;
3220 column->resized_width -= tree_view->priv->last_extra_space_per_column;
3222 gtk_widget_queue_resize (widget);
3230 pspp_sheet_view_update_current_reorder (PsppSheetView *tree_view)
3232 PsppSheetViewColumnReorder *reorder = NULL;
3236 gdk_window_get_pointer (tree_view->priv->header_window, &mouse_x, NULL, NULL);
3237 for (list = tree_view->priv->column_drag_info; list; list = list->next)
3239 reorder = (PsppSheetViewColumnReorder *) list->data;
3240 if (mouse_x >= reorder->left_align && mouse_x < reorder->right_align)
3245 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3248 tree_view->priv->cur_reorder = reorder;
3249 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view);
3253 pspp_sheet_view_vertical_autoscroll (PsppSheetView *tree_view)
3255 GdkRectangle visible_rect;
3260 gdk_window_get_pointer (tree_view->priv->bin_window, NULL, &y, NULL);
3261 y += tree_view->priv->dy;
3263 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3265 /* see if we are near the edge. */
3266 offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
3269 offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
3274 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->vadjustment) + offset, 0.0,
3275 gtk_adjustment_get_upper (tree_view->priv->vadjustment) - gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
3276 gtk_adjustment_set_value (tree_view->priv->vadjustment, value);
3280 pspp_sheet_view_horizontal_autoscroll (PsppSheetView *tree_view)
3282 GdkRectangle visible_rect;
3287 gdk_window_get_pointer (tree_view->priv->bin_window, &x, NULL, NULL);
3289 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
3291 /* See if we are near the edge. */
3292 offset = x - (visible_rect.x + SCROLL_EDGE_SIZE);
3295 offset = x - (visible_rect.x + visible_rect.width - SCROLL_EDGE_SIZE);
3301 value = CLAMP (gtk_adjustment_get_value (tree_view->priv->hadjustment) + offset,
3302 0.0, gtk_adjustment_get_upper (tree_view->priv->hadjustment) - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
3303 gtk_adjustment_set_value (tree_view->priv->hadjustment, value);
3310 pspp_sheet_view_motion_drag_column (GtkWidget *widget,
3311 GdkEventMotion *event)
3313 PsppSheetView *tree_view = (PsppSheetView *) widget;
3314 PsppSheetViewColumn *column = tree_view->priv->drag_column;
3316 GtkAllocation allocation;
3319 if ((column == NULL) ||
3320 (event->window != tree_view->priv->drag_window))
3323 /* Handle moving the header */
3324 gdk_window_get_position (tree_view->priv->drag_window, &x, &y);
3325 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
3326 x = CLAMP (x + (gint)event->x - column->drag_x, 0,
3327 MAX (tree_view->priv->width, allocation.width) - column->allocation.width);
3328 gdk_window_move (tree_view->priv->drag_window, x, y);
3330 /* autoscroll, if needed */
3331 pspp_sheet_view_horizontal_autoscroll (tree_view);
3332 /* Update the current reorder position and arrow; */
3333 pspp_sheet_view_update_current_reorder (tree_view);
3339 pspp_sheet_view_stop_rubber_band (PsppSheetView *tree_view)
3341 remove_scroll_timeout (tree_view);
3342 gtk_grab_remove (GTK_WIDGET (tree_view));
3344 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3346 GtkTreePath *tmp_path;
3348 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3350 /* The anchor path should be set to the start path */
3351 tmp_path = _pspp_sheet_view_find_path (tree_view,
3352 tree_view->priv->rubber_band_start_node);
3354 if (tree_view->priv->anchor)
3355 gtk_tree_row_reference_free (tree_view->priv->anchor);
3357 tree_view->priv->anchor =
3358 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
3359 tree_view->priv->model,
3362 gtk_tree_path_free (tmp_path);
3364 /* ... and the cursor to the end path */
3365 tmp_path = _pspp_sheet_view_find_path (tree_view,
3366 tree_view->priv->rubber_band_end_node);
3367 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view), tmp_path, FALSE, FALSE, 0); /* XXX mode? */
3368 gtk_tree_path_free (tmp_path);
3370 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
3373 /* Clear status variables */
3374 tree_view->priv->rubber_band_status = RUBBER_BAND_OFF;
3375 tree_view->priv->rubber_band_shift = 0;
3376 tree_view->priv->rubber_band_ctrl = 0;
3378 tree_view->priv->rubber_band_start_node = -1;
3379 tree_view->priv->rubber_band_end_node = -1;
3383 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView *tree_view,
3387 gboolean skip_start,
3390 if (start_node == end_node)
3393 /* We skip the first node and jump inside the loop */
3399 /* Small optimization by assuming insensitive nodes are never
3404 if (tree_view->priv->rubber_band_shift)
3405 pspp_sheet_view_node_select (tree_view, start_node);
3406 else if (tree_view->priv->rubber_band_ctrl)
3408 /* Toggle the selection state */
3409 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3410 pspp_sheet_view_node_unselect (tree_view, start_node);
3412 pspp_sheet_view_node_select (tree_view, start_node);
3415 pspp_sheet_view_node_select (tree_view, start_node);
3419 /* Mirror the above */
3420 if (tree_view->priv->rubber_band_shift)
3421 pspp_sheet_view_node_unselect (tree_view, start_node);
3422 else if (tree_view->priv->rubber_band_ctrl)
3424 /* Toggle the selection state */
3425 if (pspp_sheet_view_node_is_selected (tree_view, start_node))
3426 pspp_sheet_view_node_unselect (tree_view, start_node);
3428 pspp_sheet_view_node_select (tree_view, start_node);
3431 pspp_sheet_view_node_unselect (tree_view, start_node);
3434 _pspp_sheet_view_queue_draw_node (tree_view, start_node, NULL);
3436 if (start_node == end_node)
3441 start_node = pspp_sheet_view_node_next (tree_view, start_node);
3444 /* Ran out of tree */
3447 if (skip_end && start_node == end_node)
3454 pspp_sheet_view_node_find_offset (PsppSheetView *tree_view,
3457 return node * tree_view->priv->fixed_height;
3461 pspp_sheet_view_find_offset (PsppSheetView *tree_view,
3465 int fixed_height = tree_view->priv->fixed_height;
3466 if (fixed_height <= 0
3468 || height >= tree_view->priv->row_count * fixed_height)
3475 *new_node = height / fixed_height;
3476 return height % fixed_height;
3481 pspp_sheet_view_update_rubber_band_selection (PsppSheetView *tree_view)
3486 pspp_sheet_view_find_offset (tree_view, MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &start_node);
3487 pspp_sheet_view_find_offset (tree_view, MAX (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y), &end_node);
3489 /* Handle the start area first */
3490 if (tree_view->priv->rubber_band_start_node < 0)
3492 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3499 else if (start_node < tree_view->priv->rubber_band_start_node)
3501 /* New node is above the old one; selection became bigger */
3502 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3504 tree_view->priv->rubber_band_start_node,
3509 else if (start_node > tree_view->priv->rubber_band_start_node)
3511 /* New node is below the old one; selection became smaller */
3512 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3513 tree_view->priv->rubber_band_start_node,
3520 tree_view->priv->rubber_band_start_node = start_node;
3522 /* Next, handle the end area */
3523 if (tree_view->priv->rubber_band_end_node < 0)
3525 /* In the event this happens, start_node was also -1; this case is
3529 else if (end_node < 0)
3531 /* Find the last node in the tree */
3532 pspp_sheet_view_find_offset (tree_view, tree_view->priv->height - 1,
3535 /* Selection reached end of the tree */
3536 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3537 tree_view->priv->rubber_band_end_node,
3543 else if (end_node > tree_view->priv->rubber_band_end_node)
3545 /* New node is below the old one; selection became bigger */
3546 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3547 tree_view->priv->rubber_band_end_node,
3553 else if (end_node < tree_view->priv->rubber_band_end_node)
3555 /* New node is above the old one; selection became smaller */
3556 pspp_sheet_view_update_rubber_band_selection_range (tree_view,
3558 tree_view->priv->rubber_band_end_node,
3564 tree_view->priv->rubber_band_end_node = end_node;
3567 #define GDK_RECTANGLE_PTR(X) ((GdkRectangle *)(X))
3570 pspp_sheet_view_update_rubber_band (PsppSheetView *tree_view)
3573 cairo_rectangle_int_t old_area;
3574 cairo_rectangle_int_t new_area;
3575 cairo_rectangle_int_t common;
3576 cairo_region_t *invalid_region;
3577 PsppSheetViewColumn *column;
3579 old_area.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3580 old_area.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3581 old_area.width = ABS (tree_view->priv->rubber_band_x - tree_view->priv->press_start_x) + 1;
3582 old_area.height = ABS (tree_view->priv->rubber_band_y - tree_view->priv->press_start_y) + 1;
3584 gdk_window_get_pointer (tree_view->priv->bin_window, &x, &y, NULL);
3587 y = MAX (y, 0) + tree_view->priv->dy;
3589 new_area.x = MIN (tree_view->priv->press_start_x, x);
3590 new_area.y = MIN (tree_view->priv->press_start_y, y) - tree_view->priv->dy;
3591 new_area.width = ABS (x - tree_view->priv->press_start_x) + 1;
3592 new_area.height = ABS (y - tree_view->priv->press_start_y) + 1;
3594 invalid_region = cairo_region_create_rectangle (&old_area);
3595 cairo_region_union_rectangle (invalid_region, &new_area);
3597 gdk_rectangle_intersect (GDK_RECTANGLE_PTR (&old_area),
3598 GDK_RECTANGLE_PTR (&new_area), GDK_RECTANGLE_PTR (&common));
3599 if (common.width > 2 && common.height > 2)
3601 cairo_region_t *common_region;
3603 /* make sure the border is invalidated */
3609 common_region = cairo_region_create_rectangle (&common);
3611 cairo_region_subtract (invalid_region, common_region);
3612 cairo_region_destroy (common_region);
3615 #if GTK_MAJOR_VERSION == 3
3616 gdk_window_invalidate_region (tree_view->priv->bin_window, invalid_region, TRUE);
3619 cairo_rectangle_int_t extents;
3621 cairo_region_get_extents (invalid_region, &extents);
3622 ereg = gdk_region_rectangle (GDK_RECTANGLE_PTR (&extents));
3623 gdk_window_invalidate_region (tree_view->priv->bin_window, ereg, TRUE);
3624 gdk_region_destroy (ereg);
3628 cairo_region_destroy (invalid_region);
3630 tree_view->priv->rubber_band_x = x;
3631 tree_view->priv->rubber_band_y = y;
3632 pspp_sheet_view_get_path_at_pos (tree_view, x, y, NULL, &column, NULL, NULL);
3634 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
3635 pspp_sheet_selection_select_column_range (tree_view->priv->selection,
3636 tree_view->priv->anchor_column,
3639 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
3641 pspp_sheet_view_update_rubber_band_selection (tree_view);
3646 pspp_sheet_view_paint_rubber_band (PsppSheetView *tree_view,
3651 GdkRectangle rubber_rect;
3655 rubber_rect.x = MIN (tree_view->priv->press_start_x, tree_view->priv->rubber_band_x);
3656 rubber_rect.y = MIN (tree_view->priv->press_start_y, tree_view->priv->rubber_band_y) - tree_view->priv->dy;
3657 rubber_rect.width = ABS (tree_view->priv->press_start_x - tree_view->priv->rubber_band_x) + 1;
3658 rubber_rect.height = ABS (tree_view->priv->press_start_y - tree_view->priv->rubber_band_y) + 1;
3660 if (!gdk_rectangle_intersect (&rubber_rect, area, &rect))
3663 cr = gdk_cairo_create (tree_view->priv->bin_window);
3664 cairo_set_line_width (cr, 1.0);
3666 style = gtk_widget_get_style (GTK_WIDGET (tree_view));
3667 cairo_set_source_rgba (cr,
3668 style->fg[GTK_STATE_NORMAL].red / 65535.,
3669 style->fg[GTK_STATE_NORMAL].green / 65535.,
3670 style->fg[GTK_STATE_NORMAL].blue / 65535.,
3673 gdk_cairo_rectangle (cr, &rect);
3677 cairo_set_source_rgb (cr,
3678 style->fg[GTK_STATE_NORMAL].red / 65535.,
3679 style->fg[GTK_STATE_NORMAL].green / 65535.,
3680 style->fg[GTK_STATE_NORMAL].blue / 65535.);
3682 cairo_rectangle (cr,
3683 rubber_rect.x + 0.5, rubber_rect.y + 0.5,
3684 rubber_rect.width - 1, rubber_rect.height - 1);
3693 pspp_sheet_view_motion_bin_window (GtkWidget *widget,
3694 GdkEventMotion *event)
3696 PsppSheetView *tree_view;
3700 tree_view = (PsppSheetView *) widget;
3702 if (tree_view->priv->row_count == 0)
3705 if (tree_view->priv->rubber_band_status == RUBBER_BAND_MAYBE_START)
3707 GdkRectangle background_area, cell_area;
3708 PsppSheetViewColumn *column;
3710 if (find_click (tree_view, event->x, event->y, &node, &column,
3711 &background_area, &cell_area)
3712 && tree_view->priv->focus_column == column
3713 && tree_view->priv->press_start_node == node)
3716 gtk_grab_add (GTK_WIDGET (tree_view));
3717 pspp_sheet_view_update_rubber_band (tree_view);
3719 tree_view->priv->rubber_band_status = RUBBER_BAND_ACTIVE;
3721 else if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
3723 pspp_sheet_view_update_rubber_band (tree_view);
3725 add_scroll_timeout (tree_view);
3728 /* only check for an initiated drag when a button is pressed */
3729 if (tree_view->priv->pressed_button >= 0
3730 && !tree_view->priv->rubber_band_status)
3731 pspp_sheet_view_maybe_begin_dragging_row (tree_view, event);
3733 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
3737 pspp_sheet_view_find_offset (tree_view, new_y, &node);
3739 tree_view->priv->event_last_x = event->x;
3740 tree_view->priv->event_last_y = event->y;
3742 prelight_or_select (tree_view, node, event->x, event->y);
3748 pspp_sheet_view_motion (GtkWidget *widget,
3749 GdkEventMotion *event)
3751 PsppSheetView *tree_view;
3753 tree_view = (PsppSheetView *) widget;
3755 /* Resizing a column */
3756 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE))
3757 return pspp_sheet_view_motion_resize_column (widget, event);
3760 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
3761 return pspp_sheet_view_motion_drag_column (widget, event);
3763 /* Sanity check it */
3764 if (event->window == tree_view->priv->bin_window)
3765 return pspp_sheet_view_motion_bin_window (widget, event);
3770 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3771 * the tree is empty.
3774 invalidate_empty_focus (PsppSheetView *tree_view)
3778 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
3783 area.width = gdk_window_get_width (tree_view->priv->bin_window);
3784 area.height = gdk_window_get_height (tree_view->priv->bin_window);
3785 gdk_window_invalidate_rect (tree_view->priv->bin_window, &area, FALSE);
3788 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3792 draw_empty_focus (PsppSheetView *tree_view)
3794 GtkWidget *widget = GTK_WIDGET (tree_view);
3796 cairo_t *cr = gdk_cairo_create (tree_view->priv->bin_window);
3798 if (!gtk_widget_has_focus (widget))
3801 w = gdk_window_get_width (tree_view->priv->bin_window);
3802 h = gdk_window_get_height (tree_view->priv->bin_window);
3808 gtk_paint_focus (gtk_widget_get_style (widget),
3810 gtk_widget_get_state (widget),
3818 pspp_sheet_view_draw_vertical_grid_lines (PsppSheetView *tree_view,
3820 gint n_visible_columns,
3824 GList *list = tree_view->priv->columns;
3828 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3829 && tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_BOTH)
3832 /* Only draw the lines for visible rows and columns */
3833 for (list = tree_view->priv->columns; list; list = list->next, i++)
3835 PsppSheetViewColumn *column = list->data;
3838 if (! column->visible)
3841 current_x += column->width;
3843 /* Generally the grid lines should fit within the column, but for the
3844 last visible column we put it just past the end of the column.
3845 (Otherwise horizontal grid lines sometimes stick out by one pixel.) */
3847 if (i != n_visible_columns - 1)
3850 cairo_set_line_width (cr, 1.0);
3851 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
3852 cairo_move_to (cr, x + 0.5, min_y);
3853 cairo_line_to (cr, x + 0.5, max_y - min_y);
3858 /* Warning: Very scary function.
3859 * Modify at your own risk
3861 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3862 * FIXME: It's not...
3865 pspp_sheet_view_draw_bin (GtkWidget *widget,
3868 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
3873 int drag_highlight = -1;
3876 gint y_offset, cell_offset;
3878 GdkRectangle background_area;
3879 GdkRectangle cell_area;
3881 gint bin_window_width;
3882 gint bin_window_height;
3883 GtkTreePath *cursor_path;
3884 GtkTreePath *drag_dest_path;
3885 GList *first_column, *last_column;
3886 gint vertical_separator;
3887 gint horizontal_separator;
3888 gint focus_line_width;
3889 gboolean allow_rules;
3890 gboolean has_special_cell;
3892 gint n_visible_columns;
3893 gint grid_line_width;
3894 gboolean row_ending_details;
3895 gboolean draw_vgrid_lines, draw_hgrid_lines;
3897 GtkStyleContext *context;
3898 context = gtk_widget_get_style_context (widget);
3901 GtkAllocation allocation;
3902 gtk_widget_get_allocation (widget, &allocation);
3904 GdkRectangle exposed_rect;
3905 gdk_cairo_get_clip_rectangle (cr, &exposed_rect);
3909 Zarea.height = allocation.height;
3911 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3913 gtk_widget_style_get (widget,
3914 "horizontal-separator", &horizontal_separator,
3915 "vertical-separator", &vertical_separator,
3916 "allow-rules", &allow_rules,
3917 "focus-line-width", &focus_line_width,
3918 "row-ending-details", &row_ending_details,
3921 if (tree_view->priv->row_count == 0)
3923 draw_empty_focus (tree_view);
3928 /* clip event->area to the visible area */
3929 if (Zarea.height < 0.5)
3933 validate_visible_area (tree_view);
3935 new_y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, Zarea.y);
3939 y_offset = -pspp_sheet_view_find_offset (tree_view, new_y, &node);
3941 gdk_window_get_width (tree_view->priv->bin_window);
3944 gdk_window_get_height (tree_view->priv->bin_window);
3947 if (tree_view->priv->height < bin_window_height)
3949 gtk_paint_flat_box (gtk_widget_get_style (widget),
3951 gtk_widget_get_state (widget),
3955 0, tree_view->priv->height,
3957 bin_window_height - tree_view->priv->height);
3963 /* find the path for the node */
3964 path = _pspp_sheet_view_find_path ((PsppSheetView *)widget, node);
3965 gtk_tree_model_get_iter (tree_view->priv->model,
3968 gtk_tree_path_free (path);
3971 drag_dest_path = NULL;
3973 if (tree_view->priv->cursor)
3974 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
3977 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor);
3979 if (tree_view->priv->drag_dest_row)
3980 drag_dest_path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
3983 _pspp_sheet_view_find_node (tree_view, drag_dest_path,
3987 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3988 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3990 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
3991 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
3993 if (draw_vgrid_lines || draw_hgrid_lines)
3994 gtk_widget_style_get (widget, "grid-line-width", &grid_line_width, NULL);
3996 n_visible_columns = 0;
3997 for (list = tree_view->priv->columns; list; list = list->next)
3999 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4001 n_visible_columns ++;
4004 /* Find the last column */
4005 for (last_column = g_list_last (tree_view->priv->columns);
4006 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
4007 last_column = last_column->prev)
4011 for (first_column = g_list_first (tree_view->priv->columns);
4012 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
4013 first_column = first_column->next)
4016 /* Actually process the expose event. To do this, we want to
4017 * start at the first node of the event, and walk the tree in
4018 * order, drawing each successive node.
4025 gboolean is_first = FALSE;
4026 gboolean is_last = FALSE;
4027 gboolean done = FALSE;
4030 max_height = ROW_HEIGHT (tree_view);
4034 background_area.y = y_offset + Zarea.y;
4035 background_area.height = max_height;
4036 max_y = background_area.y + max_height;
4040 if (node == tree_view->priv->prelight_node)
4041 flags |= GTK_CELL_RENDERER_PRELIT;
4043 selected = pspp_sheet_view_node_is_selected (tree_view, node);
4047 if (tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
4049 /* we *need* to set cell data on all cells before the call
4050 * to _has_special_cell, else _has_special_cell() does not
4051 * return a correct value.
4053 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4055 list = (rtl ? list->prev : list->next))
4057 PsppSheetViewColumn *column = list->data;
4058 pspp_sheet_view_column_cell_set_cell_data (column,
4059 tree_view->priv->model,
4063 has_special_cell = pspp_sheet_view_has_special_cell (tree_view);
4066 has_special_cell = tree_view->priv->special_cells == PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
4068 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
4070 list = (rtl ? list->prev : list->next))
4072 PsppSheetViewColumn *column = list->data;
4073 const gchar *detail = NULL;
4074 gboolean selected_column;
4077 if (!column->visible)
4080 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
4081 selected_column = column->selected && column->selectable;
4083 selected_column = TRUE;
4086 if (cell_offset > Zarea.x + Zarea.width ||
4087 cell_offset + column->width < Zarea.x)
4089 cell_offset += column->width;
4094 if (selected && selected_column)
4095 flags |= GTK_CELL_RENDERER_SELECTED;
4097 flags &= ~GTK_CELL_RENDERER_SELECTED;
4099 if (column->show_sort_indicator)
4100 flags |= GTK_CELL_RENDERER_SORTED;
4102 flags &= ~GTK_CELL_RENDERER_SORTED;
4105 flags |= GTK_CELL_RENDERER_FOCUSED;
4107 flags &= ~GTK_CELL_RENDERER_FOCUSED;
4109 background_area.x = cell_offset;
4110 background_area.width = column->width;
4112 cell_area = background_area;
4113 cell_area.y += vertical_separator / 2;
4114 cell_area.x += horizontal_separator / 2;
4115 cell_area.height -= vertical_separator;
4116 cell_area.width -= horizontal_separator;
4118 if (draw_vgrid_lines)
4120 if (list == first_column)
4122 cell_area.width -= grid_line_width / 2;
4124 else if (list == last_column)
4126 cell_area.x += grid_line_width / 2;
4127 cell_area.width -= grid_line_width / 2;
4131 cell_area.x += grid_line_width / 2;
4132 cell_area.width -= grid_line_width;
4136 if (draw_hgrid_lines)
4138 cell_area.y += grid_line_width / 2;
4139 cell_area.height -= grid_line_width;
4143 if (gdk_region_rect_in (event->region, &background_area) == GDK_OVERLAP_RECTANGLE_OUT)
4145 if (!gdk_rectangle_intersect (&background_area, &exposed_rect, NULL))
4148 cell_offset += column->width;
4153 pspp_sheet_view_column_cell_set_cell_data (column,
4154 tree_view->priv->model,
4157 /* Select the detail for drawing the cell. relevant
4158 * factors are parity, sortedness, and whether to
4161 if (allow_rules && tree_view->priv->has_rules)
4163 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4164 n_visible_columns >= 3)
4167 detail = "cell_odd_ruled_sorted";
4169 detail = "cell_even_ruled_sorted";
4174 detail = "cell_odd_ruled";
4176 detail = "cell_even_ruled";
4181 if ((flags & GTK_CELL_RENDERER_SORTED) &&
4182 n_visible_columns >= 3)
4185 detail = "cell_odd_sorted";
4187 detail = "cell_even_sorted";
4192 detail = "cell_odd";
4194 detail = "cell_even";
4200 gtk_style_context_save (context);
4201 state = gtk_cell_renderer_get_state (NULL, widget, flags);
4202 gtk_style_context_set_state (context, state);
4203 gtk_style_context_add_class (context, GTK_STYLE_CLASS_CELL);
4205 /* Draw background */
4206 gtk_render_background (context, cr,
4209 background_area.width,
4210 background_area.height);
4213 gtk_render_frame (context, cr,
4216 background_area.width,
4217 background_area.height);
4219 if (draw_hgrid_lines)
4221 cairo_set_line_width (cr, 1.0);
4222 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
4224 if (background_area.y >= 0)
4227 gdk_draw_line (event->window,
4228 tree_view->priv->grid_line_gc[widget->state],
4229 background_area.x, background_area.y,
4230 background_area.x + background_area.width,
4233 cairo_move_to (cr, background_area.x, background_area.y - 0.5);
4234 cairo_line_to (cr, background_area.x + background_area.width,
4235 background_area.y - 0.5);
4239 if (y_offset + max_height <= Zarea.height - 0.5)
4242 gdk_draw_line (event->window,
4243 tree_view->priv->grid_line_gc[widget->state],
4244 background_area.x, background_area.y + max_height,
4245 background_area.x + background_area.width,
4246 background_area.y + max_height);
4249 cairo_move_to (cr, background_area.x, background_area.y + max_height - 0.5);
4250 cairo_line_to (cr, background_area.x + background_area.width,
4251 background_area.y + max_height - 0.5);
4257 _pspp_sheet_view_column_cell_render (column,
4263 if (node == cursor && has_special_cell &&
4264 ((column == tree_view->priv->focus_column &&
4265 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4266 gtk_widget_has_focus (widget)) ||
4267 (column == tree_view->priv->edited_column)))
4269 _pspp_sheet_view_column_cell_draw_focus (column,
4276 cell_offset += column->width;
4277 gtk_style_context_restore (context);
4280 if (cell_offset < Zarea.x)
4282 gtk_paint_flat_box (gtk_widget_get_style (widget),
4290 Zarea.x - cell_offset,
4291 background_area.height);
4294 if (node == drag_highlight)
4296 /* Draw indicator for the drop
4298 gint highlight_y = -1;
4302 switch (tree_view->priv->drag_dest_pos)
4304 case PSPP_SHEET_VIEW_DROP_BEFORE:
4305 highlight_y = background_area.y - 1;
4306 if (highlight_y < 0)
4310 case PSPP_SHEET_VIEW_DROP_AFTER:
4311 highlight_y = background_area.y + background_area.height - 1;
4314 case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE:
4315 case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER:
4316 _pspp_sheet_view_find_node (tree_view, drag_dest_path, &node);
4320 width = gdk_window_get_width (tree_view->priv->bin_window);
4322 if (row_ending_details)
4323 gtk_paint_focus (gtk_widget_get_style (widget),
4325 gtk_widget_get_state (widget),
4328 ? (is_last ? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4329 : (is_last ? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4330 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4331 - focus_line_width / 2,
4332 width, ROW_HEIGHT (tree_view)
4333 - focus_line_width + 1);
4335 gtk_paint_focus (gtk_widget_get_style (widget),
4337 gtk_widget_get_state (widget),
4339 "treeview-drop-indicator",
4340 0, BACKGROUND_FIRST_PIXEL (tree_view, node)
4341 - focus_line_width / 2,
4342 width, ROW_HEIGHT (tree_view)
4343 - focus_line_width + 1);
4348 if (highlight_y >= 0)
4350 gdk_draw_line (event->window,
4351 widget->style->fg_gc[gtk_widget_get_state (widget)],
4354 rtl ? 0 : bin_window_width,
4360 /* draw the big row-spanning focus rectangle, if needed */
4361 if (!has_special_cell && node == cursor &&
4362 PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS) &&
4363 gtk_widget_has_focus (widget))
4365 gint tmp_y, tmp_height;
4367 GtkStateType focus_rect_state;
4370 flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
4371 (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
4372 (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE :
4375 width = gdk_window_get_width (tree_view->priv->bin_window);
4377 if (draw_hgrid_lines)
4379 tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node) + grid_line_width / 2;
4380 tmp_height = ROW_HEIGHT (tree_view) - grid_line_width;
4384 tmp_y = BACKGROUND_FIRST_PIXEL (tree_view, node);
4385 tmp_height = ROW_HEIGHT (tree_view);
4388 if (row_ending_details)
4389 gtk_paint_focus (gtk_widget_get_style (widget),
4394 ? (is_last ? "treeview" : "treeview-left" )
4395 : (is_last ? "treeview-right" : "treeview-middle" )),
4399 gtk_paint_focus (gtk_widget_get_style (widget),
4408 y_offset += max_height;
4412 node = pspp_sheet_view_node_next (tree_view, node);
4415 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
4419 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
4426 while (y_offset < Zarea.height);
4429 pspp_sheet_view_draw_vertical_grid_lines (tree_view, cr, n_visible_columns,
4433 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
4435 GdkRectangle *rectangles;
4438 gdk_region_get_rectangles (event->region,
4442 while (n_rectangles--)
4443 pspp_sheet_view_paint_rubber_band (tree_view, &rectangles[n_rectangles]);
4445 g_free (rectangles);
4450 gtk_tree_path_free (cursor_path);
4453 gtk_tree_path_free (drag_dest_path);
4460 pspp_sheet_view_draw (GtkWidget *widget,
4463 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
4464 GtkStyleContext *context;
4466 context = gtk_widget_get_style_context (widget);
4468 if (gtk_cairo_should_draw_window (cr, tree_view->priv->bin_window))
4473 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->bin_window);
4474 pspp_sheet_view_draw_bin (widget, cr);
4477 /* We can't just chain up to Container::expose as it will try to send the
4478 * event to the headers, so we handle propagating it to our children
4479 * (eg. widgets being edited) ourselves.
4481 tmp_list = tree_view->priv->children;
4484 PsppSheetViewChild *child = tmp_list->data;
4485 tmp_list = tmp_list->next;
4487 gtk_container_propagate_draw (GTK_CONTAINER (tree_view), child->widget, cr);
4492 gtk_render_background (context, cr,
4494 gtk_widget_get_allocated_width (widget),
4495 gtk_widget_get_allocated_height (widget));
4498 gtk_style_context_save (context);
4499 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_VIEW);
4501 if (gtk_cairo_should_draw_window (cr, tree_view->priv->header_window))
4503 gint n_visible_columns;
4506 for (list = tree_view->priv->columns; list != NULL; list = list->next)
4508 PsppSheetViewColumn *column = list->data;
4510 if (column == tree_view->priv->drag_column || !column->visible)
4513 if (span_intersects (column->allocation.x, column->allocation.width,
4514 (int) gtk_adjustment_get_value (tree_view->priv->hadjustment),
4515 (int) gtk_widget_get_allocated_width (widget))
4516 && column->button != NULL)
4517 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4518 column->button, cr);
4521 n_visible_columns = 0;
4522 for (list = tree_view->priv->columns; list; list = list->next)
4524 if (! PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
4526 n_visible_columns ++;
4529 gtk_cairo_transform_to_window(cr,widget,tree_view->priv->header_window);
4530 pspp_sheet_view_draw_vertical_grid_lines (tree_view,
4534 TREE_VIEW_HEADER_HEIGHT (tree_view));
4537 if (tree_view->priv->drag_window &&
4538 gtk_cairo_should_draw_window (cr, tree_view->priv->drag_window))
4540 gtk_container_propagate_draw (GTK_CONTAINER (tree_view),
4541 tree_view->priv->drag_column->button,
4545 gtk_style_context_restore (context);
4557 /* returns 0x1 when no column has been found -- yes it's hackish */
4558 static PsppSheetViewColumn *
4559 pspp_sheet_view_get_drop_column (PsppSheetView *tree_view,
4560 PsppSheetViewColumn *column,
4563 PsppSheetViewColumn *left_column = NULL;
4564 PsppSheetViewColumn *cur_column = NULL;
4567 if (!column->reorderable)
4568 return (PsppSheetViewColumn *)0x1;
4570 switch (drop_position)
4573 /* find first column where we can drop */
4574 tmp_list = tree_view->priv->columns;
4575 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4576 return (PsppSheetViewColumn *)0x1;
4580 g_assert (tmp_list);
4582 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4583 tmp_list = tmp_list->next;
4585 if (left_column && left_column->visible == FALSE)
4588 if (!tree_view->priv->column_drop_func)
4591 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4593 left_column = cur_column;
4600 if (!tree_view->priv->column_drop_func)
4603 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4606 return (PsppSheetViewColumn *)0x1;
4610 /* find first column after column where we can drop */
4611 tmp_list = tree_view->priv->columns;
4613 for (; tmp_list; tmp_list = tmp_list->next)
4614 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4617 if (!tmp_list || !tmp_list->next)
4618 return (PsppSheetViewColumn *)0x1;
4620 tmp_list = tmp_list->next;
4621 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4622 tmp_list = tmp_list->next;
4626 g_assert (tmp_list);
4628 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4629 tmp_list = tmp_list->next;
4631 if (left_column && left_column->visible == FALSE)
4633 left_column = cur_column;
4635 tmp_list = tmp_list->next;
4639 if (!tree_view->priv->column_drop_func)
4642 if (!tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4644 left_column = cur_column;
4651 if (!tree_view->priv->column_drop_func)
4654 if (tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data))
4657 return (PsppSheetViewColumn *)0x1;
4661 /* find first column before column where we can drop */
4662 tmp_list = tree_view->priv->columns;
4664 for (; tmp_list; tmp_list = tmp_list->next)
4665 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data) == column)
4668 if (!tmp_list || !tmp_list->prev)
4669 return (PsppSheetViewColumn *)0x1;
4671 tmp_list = tmp_list->prev;
4672 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4673 tmp_list = tmp_list->prev;
4677 g_assert (tmp_list);
4679 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4681 if (left_column && !left_column->visible)
4683 /*if (!tmp_list->prev)
4684 return (PsppSheetViewColumn *)0x1;
4687 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4688 tmp_list = tmp_list->prev->prev;
4691 cur_column = left_column;
4693 tmp_list = tmp_list->prev;
4697 if (!tree_view->priv->column_drop_func)
4700 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4703 cur_column = left_column;
4704 tmp_list = tmp_list->prev;
4707 if (!tree_view->priv->column_drop_func)
4710 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4713 return (PsppSheetViewColumn *)0x1;
4717 /* same as DROP_HOME case, but doing it backwards */
4718 tmp_list = g_list_last (tree_view->priv->columns);
4721 if (column == PSPP_SHEET_VIEW_COLUMN (tmp_list->data))
4722 return (PsppSheetViewColumn *)0x1;
4726 g_assert (tmp_list);
4728 left_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
4730 if (left_column && !left_column->visible)
4732 cur_column = left_column;
4733 tmp_list = tmp_list->prev;
4736 if (!tree_view->priv->column_drop_func)
4739 if (tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
4742 cur_column = left_column;
4743 tmp_list = tmp_list->prev;
4746 if (!tree_view->priv->column_drop_func)
4749 if (tree_view->priv->column_drop_func (tree_view, column, NULL, cur_column, tree_view->priv->column_drop_func_data))
4752 return (PsppSheetViewColumn *)0x1;
4756 return (PsppSheetViewColumn *)0x1;
4760 pspp_sheet_view_key_press (GtkWidget *widget,
4763 PsppSheetView *tree_view = (PsppSheetView *) widget;
4765 if (tree_view->priv->rubber_band_status)
4767 if (event->keyval == GDK_Escape)
4768 pspp_sheet_view_stop_rubber_band (tree_view);
4773 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG))
4775 if (event->keyval == GDK_Escape)
4777 tree_view->priv->cur_reorder = NULL;
4778 pspp_sheet_view_button_release_drag_column (widget, NULL);
4783 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
4785 GList *focus_column;
4788 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
4790 for (focus_column = tree_view->priv->columns;
4792 focus_column = focus_column->next)
4794 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4796 if (column->button && gtk_widget_has_focus (column->button))
4801 (event->state & GDK_SHIFT_MASK) && (event->state & GDK_MOD1_MASK) &&
4802 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4803 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right))
4805 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4807 if (!column->resizable)
4809 gtk_widget_error_bell (widget);
4813 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4814 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4816 gint old_width = column->resized_width;
4818 column->resized_width = MAX (column->resized_width,
4820 column->resized_width -= 2;
4821 if (column->resized_width < 0)
4822 column->resized_width = 0;
4824 if (column->min_width == -1)
4825 column->resized_width = MAX (column->button_request,
4826 column->resized_width);
4828 column->resized_width = MAX (column->min_width,
4829 column->resized_width);
4831 if (column->max_width != -1)
4832 column->resized_width = MIN (column->resized_width,
4835 column->use_resized_width = TRUE;
4837 if (column->resized_width != old_width)
4838 gtk_widget_queue_resize (widget);
4840 gtk_widget_error_bell (widget);
4842 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4843 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4845 gint old_width = column->resized_width;
4847 column->resized_width = MAX (column->resized_width,
4849 column->resized_width += 2;
4851 if (column->max_width != -1)
4852 column->resized_width = MIN (column->resized_width,
4855 column->use_resized_width = TRUE;
4857 if (column->resized_width != old_width)
4858 gtk_widget_queue_resize (widget);
4860 gtk_widget_error_bell (widget);
4867 (event->state & GDK_MOD1_MASK) &&
4868 (event->keyval == GDK_Left || event->keyval == GDK_KP_Left
4869 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
4870 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
4871 || event->keyval == GDK_End || event->keyval == GDK_KP_End))
4873 PsppSheetViewColumn *column = PSPP_SHEET_VIEW_COLUMN (focus_column->data);
4875 if (event->keyval == (rtl ? GDK_Right : GDK_Left)
4876 || event->keyval == (rtl ? GDK_KP_Right : GDK_KP_Left))
4878 PsppSheetViewColumn *col;
4879 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_LEFT);
4880 if (col != (PsppSheetViewColumn *)0x1)
4881 pspp_sheet_view_move_column_after (tree_view, column, col);
4883 gtk_widget_error_bell (widget);
4885 else if (event->keyval == (rtl ? GDK_Left : GDK_Right)
4886 || event->keyval == (rtl ? GDK_KP_Left : GDK_KP_Right))
4888 PsppSheetViewColumn *col;
4889 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_RIGHT);
4890 if (col != (PsppSheetViewColumn *)0x1)
4891 pspp_sheet_view_move_column_after (tree_view, column, col);
4893 gtk_widget_error_bell (widget);
4895 else if (event->keyval == GDK_Home || event->keyval == GDK_KP_Home)
4897 PsppSheetViewColumn *col;
4898 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_HOME);
4899 if (col != (PsppSheetViewColumn *)0x1)
4900 pspp_sheet_view_move_column_after (tree_view, column, col);
4902 gtk_widget_error_bell (widget);
4904 else if (event->keyval == GDK_End || event->keyval == GDK_KP_End)
4906 PsppSheetViewColumn *col;
4907 col = pspp_sheet_view_get_drop_column (tree_view, column, DROP_END);
4908 if (col != (PsppSheetViewColumn *)0x1)
4909 pspp_sheet_view_move_column_after (tree_view, column, col);
4911 gtk_widget_error_bell (widget);
4918 /* Chain up to the parent class. It handles the keybindings. */
4919 if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_press_event (widget, event))
4922 if (tree_view->priv->search_entry_avoid_unhandled_binding)
4924 tree_view->priv->search_entry_avoid_unhandled_binding = FALSE;
4928 /* We pass the event to the search_entry. If its text changes, then we start
4929 * the typeahead find capabilities. */
4930 if (gtk_widget_has_focus (GTK_WIDGET (tree_view))
4931 && tree_view->priv->enable_search
4932 && !tree_view->priv->search_custom_entry_set)
4934 GdkEvent *new_event;
4936 const char *new_text;
4939 gboolean text_modified;
4940 gulong popup_menu_id;
4942 pspp_sheet_view_ensure_interactive_directory (tree_view);
4944 /* Make a copy of the current text */
4945 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry)));
4946 new_event = gdk_event_copy ((GdkEvent *) event);
4947 g_object_unref (((GdkEventKey *) new_event)->window);
4948 ((GdkEventKey *) new_event)->window = g_object_ref (gtk_widget_get_window (tree_view->priv->search_window));
4949 gtk_widget_realize (tree_view->priv->search_window);
4951 popup_menu_id = g_signal_connect (tree_view->priv->search_entry,
4952 "popup-menu", G_CALLBACK (gtk_true),
4955 /* Move the entry off screen */
4956 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
4957 gtk_window_move (GTK_WINDOW (tree_view->priv->search_window),
4958 gdk_screen_get_width (screen) + 1,
4959 gdk_screen_get_height (screen) + 1);
4960 gtk_widget_show (tree_view->priv->search_window);
4962 /* Send the event to the window. If the preedit_changed signal is emitted
4963 * during this event, we will set priv->imcontext_changed */
4964 tree_view->priv->imcontext_changed = FALSE;
4965 retval = gtk_widget_event (tree_view->priv->search_window, new_event);
4966 gdk_event_free (new_event);
4967 gtk_widget_hide (tree_view->priv->search_window);
4969 g_signal_handler_disconnect (tree_view->priv->search_entry,
4972 /* We check to make sure that the entry tried to handle the text, and that
4973 * the text has changed.
4975 new_text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
4976 text_modified = strcmp (old_text, new_text) != 0;
4978 if (tree_view->priv->imcontext_changed || /* we're in a preedit */
4979 (retval && text_modified)) /* ...or the text was modified */
4981 if (pspp_sheet_view_real_start_interactive_search (tree_view, FALSE))
4983 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
4988 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
4998 pspp_sheet_view_key_release (GtkWidget *widget,
5001 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
5003 if (tree_view->priv->rubber_band_status)
5006 return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->key_release_event (widget, event);
5009 /* FIXME Is this function necessary? Can I get an enter_notify event
5010 * w/o either an expose event or a mouse motion event?
5013 pspp_sheet_view_enter_notify (GtkWidget *widget,
5014 GdkEventCrossing *event)
5016 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
5020 /* Sanity check it */
5021 if (event->window != tree_view->priv->bin_window)
5024 if (tree_view->priv->row_count == 0)
5027 if (event->mode == GDK_CROSSING_GRAB ||
5028 event->mode == GDK_CROSSING_GTK_GRAB ||
5029 event->mode == GDK_CROSSING_GTK_UNGRAB ||
5030 event->mode == GDK_CROSSING_STATE_CHANGED)
5033 /* find the node internally */
5034 new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y);
5037 pspp_sheet_view_find_offset (tree_view, new_y, &node);
5039 tree_view->priv->event_last_x = event->x;
5040 tree_view->priv->event_last_y = event->y;
5042 prelight_or_select (tree_view, node, event->x, event->y);
5048 pspp_sheet_view_leave_notify (GtkWidget *widget,
5049 GdkEventCrossing *event)
5051 PsppSheetView *tree_view;
5053 if (event->mode == GDK_CROSSING_GRAB)
5056 tree_view = PSPP_SHEET_VIEW (widget);
5058 if (tree_view->priv->prelight_node >= 0)
5059 _pspp_sheet_view_queue_draw_node (tree_view,
5060 tree_view->priv->prelight_node,
5063 tree_view->priv->event_last_x = -10000;
5064 tree_view->priv->event_last_y = -10000;
5066 prelight_or_select (tree_view,
5068 -1000, -1000); /* coords not possibly over an arrow */
5075 pspp_sheet_view_focus_out (GtkWidget *widget,
5076 GdkEventFocus *event)
5078 PsppSheetView *tree_view;
5080 tree_view = PSPP_SHEET_VIEW (widget);
5082 gtk_widget_queue_draw (widget);
5084 /* destroy interactive search dialog */
5085 if (tree_view->priv->search_window)
5086 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
5092 /* Incremental Reflow
5096 pspp_sheet_view_node_queue_redraw (PsppSheetView *tree_view,
5099 GtkAllocation allocation;
5100 gint y = pspp_sheet_view_node_find_offset (tree_view, node)
5101 - gtk_adjustment_get_value (tree_view->priv->vadjustment)
5102 + TREE_VIEW_HEADER_HEIGHT (tree_view);
5104 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5106 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view),
5109 tree_view->priv->fixed_height);
5113 node_is_visible (PsppSheetView *tree_view,
5119 y = pspp_sheet_view_node_find_offset (tree_view, node);
5120 height = ROW_HEIGHT (tree_view);
5122 if (y >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5123 y + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5124 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5130 /* Returns the row height. */
5132 validate_row (PsppSheetView *tree_view,
5137 PsppSheetViewColumn *column;
5138 GList *list, *first_column, *last_column;
5140 gint horizontal_separator;
5141 gint vertical_separator;
5142 gint focus_line_width;
5143 gboolean draw_vgrid_lines, draw_hgrid_lines;
5145 gint grid_line_width;
5146 gboolean wide_separators;
5147 gint separator_height;
5149 gtk_widget_style_get (GTK_WIDGET (tree_view),
5150 "focus-padding", &focus_pad,
5151 "focus-line-width", &focus_line_width,
5152 "horizontal-separator", &horizontal_separator,
5153 "vertical-separator", &vertical_separator,
5154 "grid-line-width", &grid_line_width,
5155 "wide-separators", &wide_separators,
5156 "separator-height", &separator_height,
5160 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5161 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5163 tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5164 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH;
5166 for (last_column = g_list_last (tree_view->priv->columns);
5167 last_column && !(PSPP_SHEET_VIEW_COLUMN (last_column->data)->visible);
5168 last_column = last_column->prev)
5171 for (first_column = g_list_first (tree_view->priv->columns);
5172 first_column && !(PSPP_SHEET_VIEW_COLUMN (first_column->data)->visible);
5173 first_column = first_column->next)
5176 for (list = tree_view->priv->columns; list; list = list->next)
5181 column = list->data;
5183 if (! column->visible)
5186 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, iter);
5187 pspp_sheet_view_column_cell_get_size (column,
5189 &tmp_width, &tmp_height);
5191 tmp_height += vertical_separator;
5192 height = MAX (height, tmp_height);
5194 tmp_width = tmp_width + horizontal_separator;
5196 if (draw_vgrid_lines)
5198 if (list->data == first_column || list->data == last_column)
5199 tmp_width += grid_line_width / 2.0;
5201 tmp_width += grid_line_width;
5204 if (tmp_width > column->requested_width)
5205 column->requested_width = tmp_width;
5208 if (draw_hgrid_lines)
5209 height += grid_line_width;
5211 tree_view->priv->post_validation_flag = TRUE;
5217 validate_visible_area (PsppSheetView *tree_view)
5219 GtkTreePath *path = NULL;
5220 GtkTreePath *above_path = NULL;
5223 gboolean size_changed = FALSE;
5225 gint area_above = 0;
5226 gint area_below = 0;
5227 GtkAllocation allocation;
5229 if (tree_view->priv->row_count == 0)
5232 if (tree_view->priv->scroll_to_path == NULL)
5235 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
5237 total_height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
5239 if (total_height == 0)
5242 path = gtk_tree_row_reference_get_path (tree_view->priv->scroll_to_path);
5245 /* we are going to scroll, and will update dy */
5246 _pspp_sheet_view_find_node (tree_view, path, &node);
5247 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5249 if (tree_view->priv->scroll_to_use_align)
5251 gint height = ROW_HEIGHT (tree_view);
5252 area_above = (total_height - height) *
5253 tree_view->priv->scroll_to_row_align;
5254 area_below = total_height - area_above - height;
5255 area_above = MAX (area_above, 0);
5256 area_below = MAX (area_below, 0);
5261 * 1) row not visible
5265 gint height = ROW_HEIGHT (tree_view);
5267 dy = pspp_sheet_view_node_find_offset (tree_view, node);
5269 if (dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment) &&
5270 dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
5271 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5273 /* row visible: keep the row at the same position */
5274 area_above = dy - gtk_adjustment_get_value (tree_view->priv->vadjustment);
5275 area_below = (gtk_adjustment_get_value (tree_view->priv->vadjustment) +
5276 gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5281 /* row not visible */
5283 && dy + height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5285 /* row at the beginning -- fixed */
5287 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment)
5288 - area_above - height;
5290 else if (dy >= (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5291 gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
5293 /* row at the end -- fixed */
5294 area_above = dy - (gtk_adjustment_get_upper (tree_view->priv->vadjustment) -
5295 gtk_adjustment_get_page_size (tree_view->priv->vadjustment));
5296 area_below = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) -
5297 area_above - height;
5301 area_above = gtk_adjustment_get_page_size (tree_view->priv->vadjustment) - height;
5307 /* row somewhere in the middle, bring it to the top
5311 area_below = total_height - height;
5317 /* the scroll to isn't valid; ignore it.
5320 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5321 tree_view->priv->scroll_to_path = NULL;
5325 above_path = gtk_tree_path_copy (path);
5327 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5328 * backwards is much slower then forward, as there is no iter_prev function.
5329 * We go forwards first in case we run out of tree. Then we go backwards to
5332 while (node >= 0 && area_below > 0)
5334 gboolean done = FALSE;
5337 node = pspp_sheet_view_node_next (tree_view, node);
5340 gboolean has_next = gtk_tree_model_iter_next (tree_view->priv->model, &iter);
5342 gtk_tree_path_next (path);
5345 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next);
5355 area_below -= ROW_HEIGHT (tree_view);
5357 gtk_tree_path_free (path);
5359 /* If we ran out of tree, and have extra area_below left, we need to add it
5362 area_above += area_below;
5364 _pspp_sheet_view_find_node (tree_view, above_path, &node);
5366 /* We walk backwards */
5367 while (area_above > 0)
5369 node = pspp_sheet_view_node_prev (tree_view, node);
5371 /* Always find the new path in the tree. We cannot just assume
5372 * a gtk_tree_path_prev() is enough here, as there might be children
5373 * in between this node and the previous sibling node. If this
5374 * appears to be a performance hotspot in profiles, we can look into
5375 * intrigate logic for keeping path, node and iter in sync like
5376 * we do for forward walks. (Which will be hard because of the lacking
5383 gtk_tree_path_free (above_path);
5384 above_path = _pspp_sheet_view_find_path (tree_view, node);
5386 gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path);
5388 area_above -= ROW_HEIGHT (tree_view);
5391 /* set the dy here to scroll to the path,
5392 * and sync the top row accordingly
5394 pspp_sheet_view_set_top_row (tree_view, above_path, -area_above);
5395 pspp_sheet_view_top_row_to_dy (tree_view);
5397 /* update width/height and queue a resize */
5400 GtkRequisition requisition;
5402 /* We temporarily guess a size, under the assumption that it will be the
5403 * same when we get our next size_allocate. If we don't do this, we'll be
5404 * in an inconsistent state if we call top_row_to_dy. */
5406 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5407 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5408 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5409 gtk_adjustment_changed (tree_view->priv->hadjustment);
5410 gtk_adjustment_changed (tree_view->priv->vadjustment);
5411 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5414 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
5415 tree_view->priv->scroll_to_path = NULL;
5418 gtk_tree_path_free (above_path);
5420 if (tree_view->priv->scroll_to_column)
5422 tree_view->priv->scroll_to_column = NULL;
5424 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
5428 initialize_fixed_height_mode (PsppSheetView *tree_view)
5430 if (!tree_view->priv->row_count)
5433 if (tree_view->priv->fixed_height_set)
5436 if (tree_view->priv->fixed_height < 0)
5443 path = _pspp_sheet_view_find_path (tree_view, node);
5444 gtk_tree_model_get_iter (tree_view->priv->model, &iter, path);
5446 tree_view->priv->fixed_height = validate_row (tree_view, node, &iter, path);
5448 gtk_tree_path_free (path);
5450 g_object_notify (G_OBJECT (tree_view), "fixed-height");
5454 /* Our strategy for finding nodes to validate is a little convoluted. We find
5455 * the left-most uninvalidated node. We then try walking right, validating
5456 * nodes. Once we find a valid node, we repeat the previous process of finding
5457 * the first invalid node.
5461 validate_rows_handler (PsppSheetView *tree_view)
5463 initialize_fixed_height_mode (tree_view);
5464 if (tree_view->priv->validate_rows_timer)
5466 g_source_remove (tree_view->priv->validate_rows_timer);
5467 tree_view->priv->validate_rows_timer = 0;
5474 do_presize_handler (PsppSheetView *tree_view)
5476 GtkRequisition requisition;
5478 validate_visible_area (tree_view);
5479 tree_view->priv->presize_handler_timer = 0;
5481 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5484 gtk_widget_size_request (GTK_WIDGET (tree_view), &requisition);
5486 gtk_adjustment_set_upper (tree_view->priv->hadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->hadjustment), (gfloat)requisition.width));
5487 gtk_adjustment_set_upper (tree_view->priv->vadjustment, MAX (gtk_adjustment_get_upper (tree_view->priv->vadjustment), (gfloat)requisition.height));
5488 gtk_adjustment_changed (tree_view->priv->hadjustment);
5489 gtk_adjustment_changed (tree_view->priv->vadjustment);
5490 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
5496 presize_handler_callback (gpointer data)
5498 do_presize_handler (PSPP_SHEET_VIEW (data));
5504 install_presize_handler (PsppSheetView *tree_view)
5506 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5509 if (! tree_view->priv->presize_handler_timer)
5511 tree_view->priv->presize_handler_timer =
5512 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE - 2, presize_handler_callback, tree_view, NULL);
5514 if (! tree_view->priv->validate_rows_timer)
5516 tree_view->priv->validate_rows_timer =
5517 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE, (GSourceFunc) validate_rows_handler, tree_view, NULL);
5522 scroll_sync_handler (PsppSheetView *tree_view)
5524 if (tree_view->priv->height <= gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
5525 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view->priv->vadjustment), 0);
5526 else if (gtk_tree_row_reference_valid (tree_view->priv->top_row))
5527 pspp_sheet_view_top_row_to_dy (tree_view);
5529 pspp_sheet_view_dy_to_top_row (tree_view);
5531 tree_view->priv->scroll_sync_timer = 0;
5537 install_scroll_sync_handler (PsppSheetView *tree_view)
5539 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
5542 if (!tree_view->priv->scroll_sync_timer)
5544 tree_view->priv->scroll_sync_timer =
5545 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC, (GSourceFunc) scroll_sync_handler, tree_view, NULL);
5550 pspp_sheet_view_set_top_row (PsppSheetView *tree_view,
5554 gtk_tree_row_reference_free (tree_view->priv->top_row);
5558 tree_view->priv->top_row = NULL;
5559 tree_view->priv->top_row_dy = 0;
5563 tree_view->priv->top_row = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
5564 tree_view->priv->top_row_dy = offset;
5568 /* Always call this iff dy is in the visible range. If the tree is empty, then
5569 * it's set to be NULL, and top_row_dy is 0;
5572 pspp_sheet_view_dy_to_top_row (PsppSheetView *tree_view)
5578 if (tree_view->priv->row_count == 0)
5580 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5584 offset = pspp_sheet_view_find_offset (tree_view,
5585 tree_view->priv->dy,
5590 pspp_sheet_view_set_top_row (tree_view, NULL, 0);
5594 path = _pspp_sheet_view_find_path (tree_view, node);
5595 pspp_sheet_view_set_top_row (tree_view, path, offset);
5596 gtk_tree_path_free (path);
5602 pspp_sheet_view_top_row_to_dy (PsppSheetView *tree_view)
5608 /* Avoid recursive calls */
5609 if (tree_view->priv->in_top_row_to_dy)
5612 if (tree_view->priv->top_row)
5613 path = gtk_tree_row_reference_get_path (tree_view->priv->top_row);
5620 _pspp_sheet_view_find_node (tree_view, path, &node);
5623 gtk_tree_path_free (path);
5627 /* keep dy and set new toprow */
5628 gtk_tree_row_reference_free (tree_view->priv->top_row);
5629 tree_view->priv->top_row = NULL;
5630 tree_view->priv->top_row_dy = 0;
5631 /* DO NOT install the idle handler */
5632 pspp_sheet_view_dy_to_top_row (tree_view);
5636 if (ROW_HEIGHT (tree_view) < tree_view->priv->top_row_dy)
5638 /* new top row -- do NOT install the idle handler */
5639 pspp_sheet_view_dy_to_top_row (tree_view);
5643 new_dy = pspp_sheet_view_node_find_offset (tree_view, node);
5644 new_dy += tree_view->priv->top_row_dy;
5646 if (new_dy + gtk_adjustment_get_page_size (tree_view->priv->vadjustment) > tree_view->priv->height)
5647 new_dy = tree_view->priv->height - gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
5649 new_dy = MAX (0, new_dy);
5651 tree_view->priv->in_top_row_to_dy = TRUE;
5652 gtk_adjustment_set_value (tree_view->priv->vadjustment, (gdouble)new_dy);
5653 tree_view->priv->in_top_row_to_dy = FALSE;
5658 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView *tree_view)
5660 install_presize_handler (tree_view);
5666 set_source_row (GdkDragContext *context,
5667 GtkTreeModel *model,
5668 GtkTreePath *source_row)
5670 g_object_set_data_full (G_OBJECT (context),
5671 "gtk-tree-view-source-row",
5672 source_row ? gtk_tree_row_reference_new (model, source_row) : NULL,
5673 (GDestroyNotify) (source_row ? gtk_tree_row_reference_free : NULL));
5677 get_source_row (GdkDragContext *context)
5679 GtkTreeRowReference *ref =
5680 g_object_get_data (G_OBJECT (context), "gtk-tree-view-source-row");
5683 return gtk_tree_row_reference_get_path (ref);
5690 GtkTreeRowReference *dest_row;
5691 guint path_down_mode : 1;
5692 guint empty_view_drop : 1;
5693 guint drop_append_mode : 1;
5698 dest_row_free (gpointer data)
5700 DestRow *dr = (DestRow *)data;
5702 gtk_tree_row_reference_free (dr->dest_row);
5703 g_slice_free (DestRow, dr);
5707 set_dest_row (GdkDragContext *context,
5708 GtkTreeModel *model,
5709 GtkTreePath *dest_row,
5710 gboolean path_down_mode,
5711 gboolean empty_view_drop,
5712 gboolean drop_append_mode)
5718 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5723 dr = g_slice_new (DestRow);
5725 dr->dest_row = gtk_tree_row_reference_new (model, dest_row);
5726 dr->path_down_mode = path_down_mode != FALSE;
5727 dr->empty_view_drop = empty_view_drop != FALSE;
5728 dr->drop_append_mode = drop_append_mode != FALSE;
5730 g_object_set_data_full (G_OBJECT (context), "gtk-tree-view-dest-row",
5731 dr, (GDestroyNotify) dest_row_free);
5735 get_dest_row (GdkDragContext *context,
5736 gboolean *path_down_mode)
5739 g_object_get_data (G_OBJECT (context), "gtk-tree-view-dest-row");
5743 GtkTreePath *path = NULL;
5746 *path_down_mode = dr->path_down_mode;
5749 path = gtk_tree_row_reference_get_path (dr->dest_row);
5750 else if (dr->empty_view_drop)
5751 path = gtk_tree_path_new_from_indices (0, -1);
5755 if (path && dr->drop_append_mode)
5756 gtk_tree_path_next (path);
5764 /* Get/set whether drag_motion requested the drag data and
5765 * drag_data_received should thus not actually insert the data,
5766 * since the data doesn't result from a drop.
5769 set_status_pending (GdkDragContext *context,
5770 GdkDragAction suggested_action)
5772 g_object_set_data (G_OBJECT (context),
5773 "gtk-tree-view-status-pending",
5774 GINT_TO_POINTER (suggested_action));
5777 static GdkDragAction
5778 get_status_pending (GdkDragContext *context)
5780 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
5781 "gtk-tree-view-status-pending"));
5784 static TreeViewDragInfo*
5785 get_info (PsppSheetView *tree_view)
5787 return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
5791 destroy_info (TreeViewDragInfo *di)
5793 g_slice_free (TreeViewDragInfo, di);
5796 static TreeViewDragInfo*
5797 ensure_info (PsppSheetView *tree_view)
5799 TreeViewDragInfo *di;
5801 di = get_info (tree_view);
5805 di = g_slice_new0 (TreeViewDragInfo);
5807 g_object_set_data_full (G_OBJECT (tree_view),
5808 "gtk-tree-view-drag-info",
5810 (GDestroyNotify) destroy_info);
5817 remove_info (PsppSheetView *tree_view)
5819 g_object_set_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info", NULL);
5824 drag_scan_timeout (gpointer data)
5826 PsppSheetView *tree_view;
5828 GdkModifierType state;
5829 GtkTreePath *path = NULL;
5830 PsppSheetViewColumn *column = NULL;
5831 GdkRectangle visible_rect;
5833 GDK_THREADS_ENTER ();
5835 tree_view = PSPP_SHEET_VIEW (data);
5837 gdk_window_get_pointer (tree_view->priv->bin_window,
5840 pspp_sheet_view_get_visible_rect (tree_view, &visible_rect);
5842 /* See if we are near the edge. */
5843 if ((x - visible_rect.x) < SCROLL_EDGE_SIZE ||
5844 (visible_rect.x + visible_rect.width - x) < SCROLL_EDGE_SIZE ||
5845 (y - visible_rect.y) < SCROLL_EDGE_SIZE ||
5846 (visible_rect.y + visible_rect.height - y) < SCROLL_EDGE_SIZE)
5848 pspp_sheet_view_get_path_at_pos (tree_view,
5849 tree_view->priv->bin_window,
5858 pspp_sheet_view_scroll_to_cell (tree_view,
5864 gtk_tree_path_free (path);
5868 GDK_THREADS_LEAVE ();
5875 add_scroll_timeout (PsppSheetView *tree_view)
5877 if (tree_view->priv->scroll_timeout == 0)
5879 tree_view->priv->scroll_timeout =
5880 gdk_threads_add_timeout (150, scroll_row_timeout, tree_view);
5885 remove_scroll_timeout (PsppSheetView *tree_view)
5887 if (tree_view->priv->scroll_timeout != 0)
5889 g_source_remove (tree_view->priv->scroll_timeout);
5890 tree_view->priv->scroll_timeout = 0;
5895 check_model_dnd (GtkTreeModel *model,
5896 GType required_iface,
5897 const gchar *signal)
5899 if (model == NULL || !G_TYPE_CHECK_INSTANCE_TYPE ((model), required_iface))
5901 g_warning ("You must override the default '%s' handler "
5902 "on PsppSheetView when using models that don't support "
5903 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5904 "is to connect to '%s' and call "
5905 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5906 "the default handler from running. Look at the source code "
5907 "for the default handler in gtktreeview.c to get an idea what "
5908 "your handler should do. (gtktreeview.c is in the GTK source "
5909 "code.) If you're using GTK from a language other than C, "
5910 "there may be a more natural way to override default handlers, e.g. via derivation.",
5911 signal, g_type_name (required_iface), signal);
5919 scroll_row_timeout (gpointer data)
5921 PsppSheetView *tree_view = data;
5923 pspp_sheet_view_horizontal_autoscroll (tree_view);
5924 pspp_sheet_view_vertical_autoscroll (tree_view);
5926 if (tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
5927 pspp_sheet_view_update_rubber_band (tree_view);
5932 /* Returns TRUE if event should not be propagated to parent widgets */
5934 set_destination_row (PsppSheetView *tree_view,
5935 GdkDragContext *context,
5936 /* coordinates relative to the widget */
5939 GdkDragAction *suggested_action,
5942 GtkTreePath *path = NULL;
5943 PsppSheetViewDropPosition pos;
5944 PsppSheetViewDropPosition old_pos;
5945 TreeViewDragInfo *di;
5947 GtkTreePath *old_dest_path = NULL;
5948 gboolean can_drop = FALSE;
5950 *suggested_action = 0;
5953 widget = GTK_WIDGET (tree_view);
5955 di = get_info (tree_view);
5957 if (di == NULL || y - TREE_VIEW_HEADER_HEIGHT (tree_view) < 0)
5959 /* someone unset us as a drag dest, note that if
5960 * we return FALSE drag_leave isn't called
5963 pspp_sheet_view_set_drag_dest_row (tree_view,
5965 PSPP_SHEET_VIEW_DROP_BEFORE);
5967 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
5969 return FALSE; /* no longer a drop site */
5972 *target = gtk_drag_dest_find_target (widget, context,
5973 gtk_drag_dest_get_target_list (widget));
5974 if (*target == GDK_NONE)
5979 if (!pspp_sheet_view_get_dest_row_at_pos (tree_view,
5985 GtkTreeModel *model;
5987 /* the row got dropped on empty space, let's setup a special case
5991 gtk_tree_path_free (path);
5993 model = pspp_sheet_view_get_model (tree_view);
5995 n_children = gtk_tree_model_iter_n_children (model, NULL);
5998 pos = PSPP_SHEET_VIEW_DROP_AFTER;
5999 path = gtk_tree_path_new_from_indices (n_children - 1, -1);
6003 pos = PSPP_SHEET_VIEW_DROP_BEFORE;
6004 path = gtk_tree_path_new_from_indices (0, -1);
6014 /* If we left the current row's "open" zone, unset the timeout for
6017 pspp_sheet_view_get_drag_dest_row (tree_view,
6022 gtk_tree_path_free (old_dest_path);
6024 if (TRUE /* FIXME if the location droppable predicate */)
6032 GtkWidget *source_widget;
6034 *suggested_action = gdk_drag_context_get_suggested_action (context);
6035 source_widget = gtk_drag_get_source_widget (context);
6037 if (source_widget == widget)
6039 /* Default to MOVE, unless the user has
6040 * pressed ctrl or shift to affect available actions
6042 if ((gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
6043 *suggested_action = GDK_ACTION_MOVE;
6046 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6051 /* can't drop here */
6052 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6054 PSPP_SHEET_VIEW_DROP_BEFORE);
6058 gtk_tree_path_free (path);
6064 get_logical_dest_row (PsppSheetView *tree_view,
6065 gboolean *path_down_mode,
6066 gboolean *drop_append_mode)
6068 /* adjust path to point to the row the drop goes in front of */
6069 GtkTreePath *path = NULL;
6070 PsppSheetViewDropPosition pos;
6072 g_return_val_if_fail (path_down_mode != NULL, NULL);
6073 g_return_val_if_fail (drop_append_mode != NULL, NULL);
6075 *path_down_mode = FALSE;
6076 *drop_append_mode = 0;
6078 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6083 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE)
6085 else if (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE ||
6086 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER)
6087 *path_down_mode = TRUE;
6091 GtkTreeModel *model = pspp_sheet_view_get_model (tree_view);
6093 g_assert (pos == PSPP_SHEET_VIEW_DROP_AFTER);
6095 if (!gtk_tree_model_get_iter (model, &iter, path) ||
6096 !gtk_tree_model_iter_next (model, &iter))
6097 *drop_append_mode = 1;
6100 *drop_append_mode = 0;
6101 gtk_tree_path_next (path);
6109 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView *tree_view,
6110 GdkEventMotion *event)
6112 GtkWidget *widget = GTK_WIDGET (tree_view);
6113 GdkDragContext *context;
6114 TreeViewDragInfo *di;
6115 GtkTreePath *path = NULL;
6117 gint cell_x, cell_y;
6118 GtkTreeModel *model;
6119 gboolean retval = FALSE;
6121 di = get_info (tree_view);
6123 if (di == NULL || !di->source_set)
6126 if (tree_view->priv->pressed_button < 0)
6129 if (!gtk_drag_check_threshold (widget,
6130 tree_view->priv->press_start_x,
6131 tree_view->priv->press_start_y,
6132 event->x, event->y))
6135 model = pspp_sheet_view_get_model (tree_view);
6140 button = tree_view->priv->pressed_button;
6141 tree_view->priv->pressed_button = -1;
6143 pspp_sheet_view_get_path_at_pos (tree_view,
6144 tree_view->priv->press_start_x,
6145 tree_view->priv->press_start_y,
6154 if (!GTK_IS_TREE_DRAG_SOURCE (model) ||
6155 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model),
6159 if (!(GDK_BUTTON1_MASK << (button - 1) & di->start_button_mask))
6162 /* Now we can begin the drag */
6166 context = gtk_drag_begin (widget,
6167 gtk_drag_source_get_target_list (widget),
6172 set_source_row (context, model, path);
6176 gtk_tree_path_free (path);
6184 pspp_sheet_view_drag_begin (GtkWidget *widget,
6185 GdkDragContext *context)
6188 PsppSheetView *tree_view;
6189 GtkTreePath *path = NULL;
6190 gint cell_x, cell_y;
6192 TreeViewDragInfo *di;
6194 tree_view = PSPP_SHEET_VIEW (widget);
6196 /* if the user uses a custom DND source impl, we don't set the icon here */
6197 di = get_info (tree_view);
6199 if (di == NULL || !di->source_set)
6202 pspp_sheet_view_get_path_at_pos (tree_view,
6203 tree_view->priv->press_start_x,
6204 tree_view->priv->press_start_y,
6210 g_return_if_fail (path != NULL);
6212 row_pix = pspp_sheet_view_create_row_drag_icon (tree_view,
6215 gtk_drag_set_icon_pixmap (context,
6216 gdk_drawable_get_colormap (row_pix),
6219 /* the + 1 is for the black border in the icon */
6220 tree_view->priv->press_start_x + 1,
6223 g_object_unref (row_pix);
6224 gtk_tree_path_free (path);
6230 pspp_sheet_view_drag_end (GtkWidget *widget,
6231 GdkDragContext *context)
6236 /* Default signal implementations for the drag signals */
6238 pspp_sheet_view_drag_data_get (GtkWidget *widget,
6239 GdkDragContext *context,
6240 GtkSelectionData *selection_data,
6244 PsppSheetView *tree_view;
6245 GtkTreeModel *model;
6246 TreeViewDragInfo *di;
6247 GtkTreePath *source_row;
6249 tree_view = PSPP_SHEET_VIEW (widget);
6251 model = pspp_sheet_view_get_model (tree_view);
6256 di = get_info (PSPP_SHEET_VIEW (widget));
6261 source_row = get_source_row (context);
6263 if (source_row == NULL)
6266 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6267 * any model; for DragSource models there are some other targets
6271 if (GTK_IS_TREE_DRAG_SOURCE (model) &&
6272 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model),
6277 /* If drag_data_get does nothing, try providing row data. */
6278 if (gtk_selection_data_get_target (selection_data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6280 gtk_tree_set_row_drag_data (selection_data,
6286 gtk_tree_path_free (source_row);
6291 pspp_sheet_view_drag_data_delete (GtkWidget *widget,
6292 GdkDragContext *context)
6294 TreeViewDragInfo *di;
6295 GtkTreeModel *model;
6296 PsppSheetView *tree_view;
6297 GtkTreePath *source_row;
6299 tree_view = PSPP_SHEET_VIEW (widget);
6300 model = pspp_sheet_view_get_model (tree_view);
6302 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_SOURCE, "drag_data_delete"))
6305 di = get_info (tree_view);
6310 source_row = get_source_row (context);
6312 if (source_row == NULL)
6315 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model),
6318 gtk_tree_path_free (source_row);
6320 set_source_row (context, NULL, NULL);
6324 pspp_sheet_view_drag_leave (GtkWidget *widget,
6325 GdkDragContext *context,
6328 /* unset any highlight row */
6329 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6331 PSPP_SHEET_VIEW_DROP_BEFORE);
6333 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6338 pspp_sheet_view_drag_motion (GtkWidget *widget,
6339 GdkDragContext *context,
6340 /* coordinates relative to the widget */
6346 GtkTreePath *path = NULL;
6347 PsppSheetViewDropPosition pos;
6348 PsppSheetView *tree_view;
6349 GdkDragAction suggested_action = 0;
6352 tree_view = PSPP_SHEET_VIEW (widget);
6354 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6357 pspp_sheet_view_get_drag_dest_row (tree_view, &path, &pos);
6359 /* we only know this *after* set_desination_row */
6360 empty = tree_view->priv->empty_view_drop;
6362 if (path == NULL && !empty)
6364 /* Can't drop here. */
6365 gdk_drag_status (context, 0, time);
6369 if (tree_view->priv->open_dest_timeout == 0 &&
6370 (pos == PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER ||
6371 pos == PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE))
6377 add_scroll_timeout (tree_view);
6380 if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6382 /* Request data so we can use the source row when
6383 * determining whether to accept the drop
6385 set_status_pending (context, suggested_action);
6386 gtk_drag_get_data (widget, context, target, time);
6390 set_status_pending (context, 0);
6391 gdk_drag_status (context, suggested_action, time);
6396 gtk_tree_path_free (path);
6403 pspp_sheet_view_drag_drop (GtkWidget *widget,
6404 GdkDragContext *context,
6405 /* coordinates relative to the widget */
6410 PsppSheetView *tree_view;
6412 GdkDragAction suggested_action = 0;
6413 GdkAtom target = GDK_NONE;
6414 TreeViewDragInfo *di;
6415 GtkTreeModel *model;
6416 gboolean path_down_mode;
6417 gboolean drop_append_mode;
6419 tree_view = PSPP_SHEET_VIEW (widget);
6421 model = pspp_sheet_view_get_model (tree_view);
6423 remove_scroll_timeout (PSPP_SHEET_VIEW (widget));
6425 di = get_info (tree_view);
6430 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_drop"))
6433 if (!set_destination_row (tree_view, context, x, y, &suggested_action, &target))
6436 path = get_logical_dest_row (tree_view, &path_down_mode, &drop_append_mode);
6438 if (target != GDK_NONE && path != NULL)
6440 /* in case a motion had requested drag data, change things so we
6441 * treat drag data receives as a drop.
6443 set_status_pending (context, 0);
6444 set_dest_row (context, model, path,
6445 path_down_mode, tree_view->priv->empty_view_drop,
6450 gtk_tree_path_free (path);
6452 /* Unset this thing */
6453 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6455 PSPP_SHEET_VIEW_DROP_BEFORE);
6457 if (target != GDK_NONE)
6459 gtk_drag_get_data (widget, context, target, time);
6467 pspp_sheet_view_drag_data_received (GtkWidget *widget,
6468 GdkDragContext *context,
6469 /* coordinates relative to the widget */
6472 GtkSelectionData *selection_data,
6477 TreeViewDragInfo *di;
6478 gboolean accepted = FALSE;
6479 GtkTreeModel *model;
6480 PsppSheetView *tree_view;
6481 GtkTreePath *dest_row;
6482 GdkDragAction suggested_action;
6483 gboolean path_down_mode;
6484 gboolean drop_append_mode;
6486 tree_view = PSPP_SHEET_VIEW (widget);
6488 model = pspp_sheet_view_get_model (tree_view);
6490 if (!check_model_dnd (model, GTK_TYPE_TREE_DRAG_DEST, "drag_data_received"))
6493 di = get_info (tree_view);
6498 suggested_action = get_status_pending (context);
6500 if (suggested_action)
6502 /* We are getting this data due to a request in drag_motion,
6503 * rather than due to a request in drag_drop, so we are just
6504 * supposed to call drag_status, not actually paste in the
6507 path = get_logical_dest_row (tree_view, &path_down_mode,
6511 suggested_action = 0;
6512 else if (path_down_mode)
6513 gtk_tree_path_down (path);
6515 if (suggested_action)
6517 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6523 path_down_mode = FALSE;
6524 gtk_tree_path_up (path);
6526 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6529 suggested_action = 0;
6532 suggested_action = 0;
6536 gdk_drag_status (context, suggested_action, time);
6539 gtk_tree_path_free (path);
6541 /* If you can't drop, remove user drop indicator until the next motion */
6542 if (suggested_action == 0)
6543 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget),
6545 PSPP_SHEET_VIEW_DROP_BEFORE);
6550 dest_row = get_dest_row (context, &path_down_mode);
6552 if (dest_row == NULL)
6555 if (gtk_selection_data_get_length (selection_data) >= 0)
6559 gtk_tree_path_down (dest_row);
6560 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model),
6561 dest_row, selection_data))
6562 gtk_tree_path_up (dest_row);
6566 if (gtk_selection_data_get_length (selection_data) >= 0)
6568 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model),
6574 gtk_drag_finish (context,
6576 (gdk_drag_context_get_actions (context) == GDK_ACTION_MOVE),
6579 if (gtk_tree_path_get_depth (dest_row) == 1
6580 && gtk_tree_path_get_indices (dest_row)[0] == 0)
6582 /* special special case drag to "0", scroll to first item */
6583 if (!tree_view->priv->scroll_to_path)
6584 pspp_sheet_view_scroll_to_cell (tree_view, dest_row, NULL, FALSE, 0.0, 0.0);
6587 gtk_tree_path_free (dest_row);
6590 set_dest_row (context, NULL, NULL, FALSE, FALSE, FALSE);
6595 /* GtkContainer Methods
6600 pspp_sheet_view_remove (GtkContainer *container,
6603 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6604 PsppSheetViewChild *child = NULL;
6607 tmp_list = tree_view->priv->children;
6610 child = tmp_list->data;
6611 if (child->widget == widget)
6613 gtk_widget_unparent (widget);
6615 tree_view->priv->children = g_list_remove_link (tree_view->priv->children, tmp_list);
6616 g_list_free_1 (tmp_list);
6617 g_slice_free (PsppSheetViewChild, child);
6621 tmp_list = tmp_list->next;
6624 tmp_list = tree_view->priv->columns;
6628 PsppSheetViewColumn *column;
6629 column = tmp_list->data;
6630 if (column->button == widget)
6632 gtk_widget_unparent (widget);
6635 tmp_list = tmp_list->next;
6640 pspp_sheet_view_forall (GtkContainer *container,
6641 gboolean include_internals,
6642 GtkCallback callback,
6643 gpointer callback_data)
6645 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6646 PsppSheetViewChild *child = NULL;
6647 PsppSheetViewColumn *column;
6650 tmp_list = tree_view->priv->children;
6653 child = tmp_list->data;
6654 tmp_list = tmp_list->next;
6656 (* callback) (child->widget, callback_data);
6658 if (include_internals == FALSE)
6661 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6663 column = tmp_list->data;
6666 (* callback) (column->button, callback_data);
6670 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6671 * cells. If so we draw one big row-spanning focus rectangle.
6674 pspp_sheet_view_has_special_cell (PsppSheetView *tree_view)
6678 if (tree_view->priv->special_cells != PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT)
6679 return tree_view->priv->special_cells = PSPP_SHEET_VIEW_SPECIAL_CELLS_YES;
6681 for (list = tree_view->priv->columns; list; list = list->next)
6683 if (!((PsppSheetViewColumn *)list->data)->visible)
6685 if (_pspp_sheet_view_column_count_special_cells (list->data))
6693 pspp_sheet_view_focus_column (PsppSheetView *tree_view,
6694 PsppSheetViewColumn *focus_column,
6695 gboolean clamp_column_visible)
6697 g_return_if_fail (focus_column != NULL);
6699 tree_view->priv->focus_column = focus_column;
6701 if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view)) != focus_column->button)
6702 gtk_widget_grab_focus (focus_column->button);
6704 if (clamp_column_visible)
6705 pspp_sheet_view_clamp_column_visible (tree_view, focus_column, FALSE);
6708 /* Returns TRUE if the focus is within the headers, after the focus operation is
6712 pspp_sheet_view_header_focus (PsppSheetView *tree_view,
6713 GtkDirectionType dir,
6714 gboolean clamp_column_visible)
6716 GtkWidget *focus_child;
6717 PsppSheetViewColumn *focus_column;
6718 GList *last_column, *first_column;
6722 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE))
6725 focus_child = gtk_container_get_focus_child (GTK_CONTAINER (tree_view));
6727 first_column = tree_view->priv->columns;
6728 while (first_column)
6730 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (first_column->data);
6732 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6734 first_column = first_column->next;
6737 /* No headers are visible, or are focusable. We can't focus in or out.
6739 if (first_column == NULL)
6742 last_column = g_list_last (tree_view->priv->columns);
6745 PsppSheetViewColumn *c = PSPP_SHEET_VIEW_COLUMN (last_column->data);
6747 if (pspp_sheet_view_column_can_focus (c) && c->visible)
6749 last_column = last_column->prev;
6753 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
6757 case GTK_DIR_TAB_BACKWARD:
6758 case GTK_DIR_TAB_FORWARD:
6761 if (focus_child == NULL)
6763 if (tree_view->priv->focus_column != NULL &&
6764 pspp_sheet_view_column_can_focus (tree_view->priv->focus_column))
6765 focus_column = tree_view->priv->focus_column;
6767 focus_column = first_column->data;
6768 pspp_sheet_view_focus_column (tree_view, focus_column,
6769 clamp_column_visible);
6776 if (focus_child == NULL)
6778 if (tree_view->priv->focus_column != NULL)
6779 focus_column = tree_view->priv->focus_column;
6780 else if (dir == GTK_DIR_LEFT)
6781 focus_column = last_column->data;
6783 focus_column = first_column->data;
6784 pspp_sheet_view_focus_column (tree_view, focus_column,
6785 clamp_column_visible);
6789 if (gtk_widget_child_focus (focus_child, dir))
6791 /* The focus moves inside the button. */
6792 /* This is probably a great example of bad UI */
6793 if (clamp_column_visible)
6794 pspp_sheet_view_clamp_column_visible (tree_view,
6795 tree_view->priv->focus_column,
6800 /* We need to move the focus among the row of buttons. */
6801 for (tmp_list = tree_view->priv->columns; tmp_list; tmp_list = tmp_list->next)
6802 if (PSPP_SHEET_VIEW_COLUMN (tmp_list->data)->button == focus_child)
6805 if ((tmp_list == first_column && dir == (rtl ? GTK_DIR_RIGHT : GTK_DIR_LEFT))
6806 || (tmp_list == last_column && dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT)))
6808 gtk_widget_error_bell (GTK_WIDGET (tree_view));
6814 PsppSheetViewColumn *column;
6816 if (dir == (rtl ? GTK_DIR_LEFT : GTK_DIR_RIGHT))
6817 tmp_list = tmp_list->next;
6819 tmp_list = tmp_list->prev;
6821 if (tmp_list == NULL)
6823 g_warning ("Internal button not found");
6826 column = tmp_list->data;
6827 if (column->visible &&
6828 pspp_sheet_view_column_can_focus (column))
6832 pspp_sheet_view_focus_column (tree_view, column,
6833 clamp_column_visible);
6841 g_assert_not_reached ();
6848 /* This function returns in 'path' the first focusable path, if the given path
6849 * is already focusable, it's the returned one.
6853 search_first_focusable_path (PsppSheetView *tree_view,
6855 gboolean search_forward,
6858 /* XXX this function is trivial given that the sheetview doesn't support
6862 if (!path || !*path)
6865 _pspp_sheet_view_find_node (tree_view, *path, &node);
6873 return (*path != NULL);
6877 pspp_sheet_view_focus (GtkWidget *widget,
6878 GtkDirectionType direction)
6880 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6881 GtkContainer *container = GTK_CONTAINER (widget);
6882 GtkWidget *focus_child;
6884 if (!gtk_widget_is_sensitive (widget) || !gtk_widget_get_can_focus (widget))
6887 focus_child = gtk_container_get_focus_child (container);
6889 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget), FALSE);
6890 /* Case 1. Headers currently have focus. */
6897 pspp_sheet_view_header_focus (tree_view, direction, TRUE);
6899 case GTK_DIR_TAB_BACKWARD:
6902 case GTK_DIR_TAB_FORWARD:
6904 gtk_widget_grab_focus (widget);
6907 g_assert_not_reached ();
6912 /* Case 2. We don't have focus at all. */
6913 if (!gtk_widget_has_focus (widget))
6915 if (!pspp_sheet_view_header_focus (tree_view, direction, FALSE))
6916 gtk_widget_grab_focus (widget);
6920 /* Case 3. We have focus already. */
6921 if (direction == GTK_DIR_TAB_BACKWARD)
6922 return (pspp_sheet_view_header_focus (tree_view, direction, FALSE));
6923 else if (direction == GTK_DIR_TAB_FORWARD)
6926 /* Other directions caught by the keybindings */
6927 gtk_widget_grab_focus (widget);
6932 pspp_sheet_view_grab_focus (GtkWidget *widget)
6934 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->grab_focus (widget);
6936 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget));
6940 pspp_sheet_view_style_updated (GtkWidget *widget)
6942 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
6944 PsppSheetViewColumn *column;
6945 GtkStyleContext *context;
6947 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class)->style_updated (widget);
6949 if (gtk_widget_get_realized (widget))
6951 context = gtk_widget_get_style_context (GTK_WIDGET (tree_view));
6952 gtk_style_context_set_background (context, gtk_widget_get_window (GTK_WIDGET (tree_view)));
6953 gtk_style_context_set_background (context, tree_view->priv->header_window);
6954 pspp_sheet_view_set_grid_lines (tree_view, tree_view->priv->grid_lines);
6957 gtk_widget_style_get (widget,
6958 "expander-size", &tree_view->priv->expander_size,
6960 tree_view->priv->expander_size += EXPANDER_EXTRA_PADDING;
6962 for (list = tree_view->priv->columns; list; list = list->next)
6964 column = list->data;
6965 _pspp_sheet_view_column_cell_set_dirty (column);
6968 tree_view->priv->fixed_height = -1;
6970 /* Invalidate cached button style. */
6971 if (tree_view->priv->button_style)
6973 g_object_unref (tree_view->priv->button_style);
6974 tree_view->priv->button_style = NULL;
6977 gtk_widget_queue_resize (widget);
6982 pspp_sheet_view_set_focus_child (GtkContainer *container,
6985 PsppSheetView *tree_view = PSPP_SHEET_VIEW (container);
6988 for (list = tree_view->priv->columns; list; list = list->next)
6990 if (PSPP_SHEET_VIEW_COLUMN (list->data)->button == child)
6992 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
6997 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class)->set_focus_child (container, child);
7001 pspp_sheet_view_set_adjustments (PsppSheetView *tree_view,
7002 GtkAdjustment *hadj,
7003 GtkAdjustment *vadj)
7005 gboolean need_adjust = FALSE;
7007 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7010 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
7012 hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7014 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
7016 vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7018 if (tree_view->priv->hadjustment && (tree_view->priv->hadjustment != hadj))
7020 g_signal_handlers_disconnect_by_func (tree_view->priv->hadjustment,
7021 pspp_sheet_view_adjustment_changed,
7023 g_object_unref (tree_view->priv->hadjustment);
7026 if (tree_view->priv->vadjustment && (tree_view->priv->vadjustment != vadj))
7028 g_signal_handlers_disconnect_by_func (tree_view->priv->vadjustment,
7029 pspp_sheet_view_adjustment_changed,
7031 g_object_unref (tree_view->priv->vadjustment);
7034 if (tree_view->priv->hadjustment != hadj)
7036 tree_view->priv->hadjustment = hadj;
7037 g_object_ref_sink (tree_view->priv->hadjustment);
7039 g_signal_connect (tree_view->priv->hadjustment, "value-changed",
7040 G_CALLBACK (pspp_sheet_view_adjustment_changed),
7045 if (tree_view->priv->vadjustment != vadj)
7047 tree_view->priv->vadjustment = vadj;
7048 g_object_ref_sink (tree_view->priv->vadjustment);
7050 g_signal_connect (tree_view->priv->vadjustment, "value-changed",
7051 G_CALLBACK (pspp_sheet_view_adjustment_changed),
7057 pspp_sheet_view_adjustment_changed (NULL, tree_view);
7062 pspp_sheet_view_real_move_cursor (PsppSheetView *tree_view,
7063 GtkMovementStep step,
7066 PsppSheetSelectMode mode;
7067 GdkModifierType state;
7069 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
7070 g_return_val_if_fail (step == GTK_MOVEMENT_LOGICAL_POSITIONS ||
7071 step == GTK_MOVEMENT_VISUAL_POSITIONS ||
7072 step == GTK_MOVEMENT_DISPLAY_LINES ||
7073 step == GTK_MOVEMENT_PAGES ||
7074 step == GTK_MOVEMENT_BUFFER_ENDS ||
7075 step == GTK_MOVEMENT_DISPLAY_LINE_ENDS, FALSE);
7077 if (tree_view->priv->row_count == 0)
7079 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7082 pspp_sheet_view_stop_editing (tree_view, FALSE);
7083 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7084 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7087 if (gtk_get_current_event_state (&state))
7089 if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7090 mode |= PSPP_SHEET_SELECT_MODE_TOGGLE;
7091 if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
7092 mode |= PSPP_SHEET_SELECT_MODE_EXTEND;
7094 /* else we assume not pressed */
7098 case GTK_MOVEMENT_LOGICAL_POSITIONS:
7099 pspp_sheet_view_move_cursor_tab (tree_view, count);
7101 case GTK_MOVEMENT_VISUAL_POSITIONS:
7102 pspp_sheet_view_move_cursor_left_right (tree_view, count, mode);
7104 case GTK_MOVEMENT_DISPLAY_LINES:
7105 pspp_sheet_view_move_cursor_up_down (tree_view, count, mode);
7107 case GTK_MOVEMENT_PAGES:
7108 pspp_sheet_view_move_cursor_page_up_down (tree_view, count, mode);
7110 case GTK_MOVEMENT_BUFFER_ENDS:
7111 pspp_sheet_view_move_cursor_start_end (tree_view, count, mode);
7113 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
7114 pspp_sheet_view_move_cursor_line_start_end (tree_view, count, mode);
7117 g_assert_not_reached ();
7124 pspp_sheet_view_put (PsppSheetView *tree_view,
7125 GtkWidget *child_widget,
7126 /* in bin_window coordinates */
7132 PsppSheetViewChild *child;
7134 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7135 g_return_if_fail (GTK_IS_WIDGET (child_widget));
7137 child = g_slice_new (PsppSheetViewChild);
7139 child->widget = child_widget;
7142 child->width = width;
7143 child->height = height;
7145 tree_view->priv->children = g_list_append (tree_view->priv->children, child);
7147 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7148 gtk_widget_set_parent_window (child->widget, tree_view->priv->bin_window);
7150 gtk_widget_set_parent (child_widget, GTK_WIDGET (tree_view));
7154 _pspp_sheet_view_child_move_resize (PsppSheetView *tree_view,
7156 /* in tree coordinates */
7162 PsppSheetViewChild *child = NULL;
7164 GdkRectangle allocation;
7166 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
7167 g_return_if_fail (GTK_IS_WIDGET (widget));
7169 for (list = tree_view->priv->children; list; list = list->next)
7171 if (((PsppSheetViewChild *)list->data)->widget == widget)
7180 allocation.x = child->x = x;
7181 allocation.y = child->y = y;
7182 allocation.width = child->width = width;
7183 allocation.height = child->height = height;
7185 if (gtk_widget_get_realized (widget))
7186 gtk_widget_size_allocate (widget, &allocation);
7190 /* TreeModel Callbacks
7194 pspp_sheet_view_row_changed (GtkTreeModel *model,
7199 PsppSheetView *tree_view = (PsppSheetView *)data;
7201 gboolean free_path = FALSE;
7202 GtkTreePath *cursor_path;
7204 g_return_if_fail (path != NULL || iter != NULL);
7206 if (tree_view->priv->cursor != NULL)
7207 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7211 if (tree_view->priv->edited_column &&
7212 (cursor_path == NULL || gtk_tree_path_compare (cursor_path, path) == 0))
7213 pspp_sheet_view_stop_editing (tree_view, TRUE);
7215 if (cursor_path != NULL)
7216 gtk_tree_path_free (cursor_path);
7220 path = gtk_tree_model_get_path (model, iter);
7223 else if (iter == NULL)
7224 gtk_tree_model_get_iter (model, iter, path);
7226 _pspp_sheet_view_find_node (tree_view,
7232 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7233 pspp_sheet_view_node_queue_redraw (tree_view, node);
7237 gtk_tree_path_free (path);
7241 pspp_sheet_view_row_inserted (GtkTreeModel *model,
7246 PsppSheetView *tree_view = (PsppSheetView *) data;
7249 gint height = tree_view->priv->fixed_height;
7250 gboolean free_path = FALSE;
7251 gboolean node_visible = TRUE;
7253 g_return_if_fail (path != NULL || iter != NULL);
7257 path = gtk_tree_model_get_path (model, iter);
7260 else if (iter == NULL)
7261 gtk_tree_model_get_iter (model, iter, path);
7263 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7265 /* Update all row-references */
7266 gtk_tree_row_reference_inserted (G_OBJECT (data), path);
7267 indices = gtk_tree_path_get_indices (path);
7268 tmpnode = indices[0];
7270 range_tower_insert0 (tree_view->priv->selected, tmpnode, 1);
7274 if (node_visible && node_is_visible (tree_view, tmpnode))
7275 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7277 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view));
7280 install_presize_handler (tree_view);
7282 gtk_tree_path_free (path);
7286 pspp_sheet_view_row_deleted (GtkTreeModel *model,
7290 PsppSheetView *tree_view = (PsppSheetView *)data;
7293 g_return_if_fail (path != NULL);
7295 gtk_tree_row_reference_deleted (G_OBJECT (data), path);
7297 _pspp_sheet_view_find_node (tree_view, path, &node);
7302 range_tower_delete (tree_view->priv->selected, node, 1);
7304 /* Ensure we don't have a dangling pointer to a dead node */
7305 ensure_unprelighted (tree_view);
7307 /* Cancel editting if we've started */
7308 pspp_sheet_view_stop_editing (tree_view, TRUE);
7310 if (tree_view->priv->destroy_count_func)
7312 gint child_count = 0;
7313 tree_view->priv->destroy_count_func (tree_view, path, child_count, tree_view->priv->destroy_count_data);
7316 tree_view->priv->row_count = gtk_tree_model_iter_n_children (model, NULL);
7318 if (! gtk_tree_row_reference_valid (tree_view->priv->top_row))
7320 gtk_tree_row_reference_free (tree_view->priv->top_row);
7321 tree_view->priv->top_row = NULL;
7324 install_scroll_sync_handler (tree_view);
7326 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
7329 if (helper_data.changed)
7330 g_signal_emit_by_name (tree_view->priv->selection, "changed");
7335 pspp_sheet_view_rows_reordered (GtkTreeModel *model,
7336 GtkTreePath *parent,
7341 PsppSheetView *tree_view = PSPP_SHEET_VIEW (data);
7344 /* XXX need to adjust selection */
7345 len = gtk_tree_model_iter_n_children (model, iter);
7350 gtk_tree_row_reference_reordered (G_OBJECT (data),
7355 if (gtk_tree_path_get_depth (parent) != 0)
7358 if (tree_view->priv->edited_column)
7359 pspp_sheet_view_stop_editing (tree_view, TRUE);
7361 /* we need to be unprelighted */
7362 ensure_unprelighted (tree_view);
7364 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
7366 pspp_sheet_view_dy_to_top_row (tree_view);
7370 /* Internal tree functions
7375 pspp_sheet_view_get_background_xrange (PsppSheetView *tree_view,
7376 PsppSheetViewColumn *column,
7380 PsppSheetViewColumn *tmp_column = NULL;
7391 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7394 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
7396 list = (rtl ? list->prev : list->next))
7398 tmp_column = list->data;
7400 if (tmp_column == column)
7403 if (tmp_column->visible)
7404 total_width += tmp_column->width;
7407 if (tmp_column != column)
7409 g_warning (G_STRLOC": passed-in column isn't in the tree");
7418 if (column->visible)
7419 *x2 = total_width + column->width;
7421 *x2 = total_width; /* width of 0 */
7425 /* Make sure the node is visible vertically */
7427 pspp_sheet_view_clamp_node_visible (PsppSheetView *tree_view,
7430 gint node_dy, height;
7431 GtkTreePath *path = NULL;
7433 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7436 /* just return if the node is visible, avoiding a costly expose */
7437 node_dy = pspp_sheet_view_node_find_offset (tree_view, node);
7438 height = ROW_HEIGHT (tree_view);
7439 if (node_dy >= gtk_adjustment_get_value (tree_view->priv->vadjustment)
7440 && node_dy + height <= (gtk_adjustment_get_value (tree_view->priv->vadjustment)
7441 + gtk_adjustment_get_page_size (tree_view->priv->vadjustment)))
7444 path = _pspp_sheet_view_find_path (tree_view, node);
7447 /* We process updates because we want to clear old selected items when we scroll.
7448 * if this is removed, we get a "selection streak" at the bottom. */
7449 gdk_window_process_updates (tree_view->priv->bin_window, TRUE);
7450 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
7451 gtk_tree_path_free (path);
7456 pspp_sheet_view_clamp_column_visible (PsppSheetView *tree_view,
7457 PsppSheetViewColumn *column,
7458 gboolean focus_to_cell)
7465 x = column->allocation.x;
7466 width = column->allocation.width;
7468 if (width > gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7470 /* The column is larger than the horizontal page size. If the
7471 * column has cells which can be focussed individually, then we make
7472 * sure the cell which gets focus is fully visible (if even the
7473 * focus cell is bigger than the page size, we make sure the
7474 * left-hand side of the cell is visible).
7476 * If the column does not have those so-called special cells, we
7477 * make sure the left-hand side of the column is visible.
7480 if (focus_to_cell && pspp_sheet_view_has_special_cell (tree_view))
7482 GtkTreePath *cursor_path;
7483 GdkRectangle background_area, cell_area, focus_area;
7485 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7487 pspp_sheet_view_get_cell_area (tree_view,
7488 cursor_path, column, &cell_area);
7489 pspp_sheet_view_get_background_area (tree_view,
7490 cursor_path, column,
7493 gtk_tree_path_free (cursor_path);
7495 _pspp_sheet_view_column_get_focus_area (column,
7501 width = focus_area.width;
7503 if (width < gtk_adjustment_get_page_size (tree_view->priv->hadjustment))
7505 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7506 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7507 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7508 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7509 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7513 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7515 gtk_adjustment_get_lower (tree_view->priv->hadjustment),
7516 gtk_adjustment_get_upper (tree_view->priv->hadjustment)
7517 - gtk_adjustment_get_page_size (tree_view->priv->hadjustment)));
7521 if ((gtk_adjustment_get_value (tree_view->priv->hadjustment) + gtk_adjustment_get_page_size (tree_view->priv->hadjustment)) < (x + width))
7522 gtk_adjustment_set_value (tree_view->priv->hadjustment,
7523 x + width - gtk_adjustment_get_page_size (tree_view->priv->hadjustment));
7524 else if (gtk_adjustment_get_value (tree_view->priv->hadjustment) > x)
7525 gtk_adjustment_set_value (tree_view->priv->hadjustment, x);
7530 _pspp_sheet_view_find_path (PsppSheetView *tree_view,
7535 path = gtk_tree_path_new ();
7537 gtk_tree_path_append_index (path, node);
7542 _pspp_sheet_view_find_node (PsppSheetView *tree_view,
7546 gint *indices = gtk_tree_path_get_indices (path);
7547 gint depth = gtk_tree_path_get_depth (path);
7550 if (depth == 0 || indices[0] < 0 || indices[0] >= tree_view->priv->row_count)
7556 pspp_sheet_view_add_move_binding (GtkBindingSet *binding_set,
7559 gboolean add_shifted_binding,
7560 GtkMovementStep step,
7564 gtk_binding_entry_add_signal (binding_set, keyval, modmask,
7569 if (add_shifted_binding)
7570 gtk_binding_entry_add_signal (binding_set, keyval, GDK_SHIFT_MASK,
7575 if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
7578 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
7583 gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
7590 pspp_sheet_view_set_column_drag_info (PsppSheetView *tree_view,
7591 PsppSheetViewColumn *column)
7593 PsppSheetViewColumn *left_column;
7594 PsppSheetViewColumn *cur_column = NULL;
7595 PsppSheetViewColumnReorder *reorder;
7600 /* We want to precalculate the motion list such that we know what column slots
7604 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
7606 /* First, identify all possible drop spots */
7608 tmp_list = g_list_last (tree_view->priv->columns);
7610 tmp_list = g_list_first (tree_view->priv->columns);
7614 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->data);
7615 tmp_list = rtl?g_list_previous (tmp_list):g_list_next (tmp_list);
7617 if (cur_column->visible == FALSE)
7620 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7621 if (left_column != column && cur_column != column &&
7622 tree_view->priv->column_drop_func &&
7623 ! tree_view->priv->column_drop_func (tree_view, column, left_column, cur_column, tree_view->priv->column_drop_func_data))
7625 left_column = cur_column;
7628 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7629 reorder->left_column = left_column;
7630 left_column = reorder->right_column = cur_column;
7632 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7635 /* Add the last one */
7636 if (tree_view->priv->column_drop_func == NULL ||
7637 ((left_column != column) &&
7638 tree_view->priv->column_drop_func (tree_view, column, left_column, NULL, tree_view->priv->column_drop_func_data)))
7640 reorder = g_slice_new0 (PsppSheetViewColumnReorder);
7641 reorder->left_column = left_column;
7642 reorder->right_column = NULL;
7643 tree_view->priv->column_drag_info = g_list_append (tree_view->priv->column_drag_info, reorder);
7646 /* We quickly check to see if it even makes sense to reorder columns. */
7647 /* If there is nothing that can be moved, then we return */
7649 if (tree_view->priv->column_drag_info == NULL)
7652 /* We know there are always 2 slots possbile, as you can always return column. */
7653 /* If that's all there is, return */
7654 if (tree_view->priv->column_drag_info->next == NULL ||
7655 (tree_view->priv->column_drag_info->next->next == NULL &&
7656 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->data)->right_column == column &&
7657 ((PsppSheetViewColumnReorder *)tree_view->priv->column_drag_info->next->data)->left_column == column))
7659 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7660 g_slice_free (PsppSheetViewColumnReorder, tmp_list->data);
7661 g_list_free (tree_view->priv->column_drag_info);
7662 tree_view->priv->column_drag_info = NULL;
7665 /* We fill in the ranges for the columns, now that we've isolated them */
7666 left = - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7668 for (tmp_list = tree_view->priv->column_drag_info; tmp_list; tmp_list = tmp_list->next)
7670 reorder = (PsppSheetViewColumnReorder *) tmp_list->data;
7672 reorder->left_align = left;
7673 if (tmp_list->next != NULL)
7675 g_assert (tmp_list->next->data);
7676 left = reorder->right_align = (reorder->right_column->allocation.x +
7677 reorder->right_column->allocation.width +
7678 ((PsppSheetViewColumnReorder *)tmp_list->next->data)->left_column->allocation.x)/2;
7682 gint width = gdk_window_get_width (tree_view->priv->header_window);
7683 reorder->right_align = width + TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view);
7689 _pspp_sheet_view_column_start_drag (PsppSheetView *tree_view,
7690 PsppSheetViewColumn *column)
7692 GdkEvent *send_event;
7693 GtkAllocation allocation;
7695 GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
7696 GdkDisplay *display = gdk_screen_get_display (screen);
7698 g_return_if_fail (tree_view->priv->column_drag_info == NULL);
7699 g_return_if_fail (tree_view->priv->cur_reorder == NULL);
7700 g_return_if_fail (column->button);
7702 pspp_sheet_view_set_column_drag_info (tree_view, column);
7704 if (tree_view->priv->column_drag_info == NULL)
7707 if (tree_view->priv->drag_window == NULL)
7709 GdkWindowAttr attributes;
7710 guint attributes_mask;
7712 attributes.window_type = GDK_WINDOW_CHILD;
7713 attributes.wclass = GDK_INPUT_OUTPUT;
7714 attributes.x = column->allocation.x;
7716 attributes.width = column->allocation.width;
7717 attributes.height = column->allocation.height;
7718 attributes.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
7719 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
7720 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL ;
7722 tree_view->priv->drag_window = gdk_window_new (tree_view->priv->bin_window,
7725 gdk_window_set_user_data (tree_view->priv->drag_window, GTK_WIDGET (tree_view));
7728 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
7729 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
7731 gtk_grab_remove (column->button);
7733 send_event = gdk_event_new (GDK_LEAVE_NOTIFY);
7734 send_event->crossing.send_event = TRUE;
7735 send_event->crossing.window = g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column->button)));
7736 send_event->crossing.subwindow = NULL;
7737 send_event->crossing.detail = GDK_NOTIFY_ANCESTOR;
7738 send_event->crossing.time = GDK_CURRENT_TIME;
7740 gtk_propagate_event (column->button, send_event);
7741 gdk_event_free (send_event);
7743 send_event = gdk_event_new (GDK_BUTTON_RELEASE);
7744 send_event->button.window = g_object_ref (gdk_screen_get_root_window (screen));
7745 send_event->button.send_event = TRUE;
7746 send_event->button.time = GDK_CURRENT_TIME;
7747 send_event->button.x = -1;
7748 send_event->button.y = -1;
7749 send_event->button.axes = NULL;
7750 send_event->button.state = 0;
7751 send_event->button.button = 1;
7752 send_event->button.device =
7753 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
7755 send_event->button.x_root = 0;
7756 send_event->button.y_root = 0;
7758 gtk_propagate_event (column->button, send_event);
7759 gdk_event_free (send_event);
7761 /* Kids, don't try this at home */
7762 g_object_ref (column->button);
7763 gtk_container_remove (GTK_CONTAINER (tree_view), column->button);
7764 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7765 gtk_widget_set_parent (column->button, GTK_WIDGET (tree_view));
7766 g_object_unref (column->button);
7768 tree_view->priv->drag_column_x = column->allocation.x;
7769 allocation = column->allocation;
7771 gtk_widget_size_allocate (column->button, &allocation);
7772 gtk_widget_set_parent_window (column->button, tree_view->priv->drag_window);
7774 tree_view->priv->drag_column = column;
7775 gdk_window_show (tree_view->priv->drag_window);
7777 gdk_window_get_origin (tree_view->priv->header_window, &x, &y);
7779 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
7780 while (gtk_events_pending ())
7781 gtk_main_iteration ();
7783 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_IN_COLUMN_DRAG);
7784 gdk_pointer_grab (tree_view->priv->drag_window,
7786 GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
7787 NULL, NULL, GDK_CURRENT_TIME);
7788 gdk_keyboard_grab (tree_view->priv->drag_window,
7794 _pspp_sheet_view_queue_draw_node (PsppSheetView *tree_view,
7796 const GdkRectangle *clip_rect)
7799 GtkAllocation allocation;
7801 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
7804 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
7806 rect.width = MAX (tree_view->priv->width, allocation.width);
7808 rect.y = BACKGROUND_FIRST_PIXEL (tree_view, node);
7809 rect.height = ROW_HEIGHT (tree_view);
7813 GdkRectangle new_rect;
7815 gdk_rectangle_intersect (clip_rect, &rect, &new_rect);
7817 gdk_window_invalidate_rect (tree_view->priv->bin_window, &new_rect, TRUE);
7821 gdk_window_invalidate_rect (tree_view->priv->bin_window, &rect, TRUE);
7826 pspp_sheet_view_queue_draw_path (PsppSheetView *tree_view,
7828 const GdkRectangle *clip_rect)
7832 _pspp_sheet_view_find_node (tree_view, path, &node);
7835 _pspp_sheet_view_queue_draw_node (tree_view, node, clip_rect);
7839 pspp_sheet_view_focus_to_cursor (PsppSheetView *tree_view)
7842 GtkTreePath *cursor_path;
7844 if ((tree_view->priv->row_count == 0) ||
7845 (! gtk_widget_get_realized (GTK_WIDGET (tree_view))))
7849 if (tree_view->priv->cursor)
7850 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7852 if (cursor_path == NULL)
7854 /* There's no cursor. Move the cursor to the first selected row, if any
7855 * are selected, otherwise to the first row in the sheetview.
7857 GList *selected_rows;
7858 GtkTreeModel *model;
7859 PsppSheetSelection *selection;
7861 selection = pspp_sheet_view_get_selection (tree_view);
7862 selected_rows = pspp_sheet_selection_get_selected_rows (selection, &model);
7866 /* XXX we could avoid doing O(n) work to get this result */
7867 cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
7868 g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
7869 g_list_free (selected_rows);
7873 cursor_path = gtk_tree_path_new_first ();
7874 search_first_focusable_path (tree_view, &cursor_path,
7878 gtk_tree_row_reference_free (tree_view->priv->cursor);
7879 tree_view->priv->cursor = NULL;
7883 if (tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7884 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE)
7885 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE, 0);
7887 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, 0);
7893 /* Now find a column for the cursor. */
7894 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
7896 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
7897 gtk_tree_path_free (cursor_path);
7899 if (tree_view->priv->focus_column == NULL)
7902 for (list = tree_view->priv->columns; list; list = list->next)
7904 if (PSPP_SHEET_VIEW_COLUMN (list->data)->visible)
7906 tree_view->priv->focus_column = PSPP_SHEET_VIEW_COLUMN (list->data);
7907 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
7908 pspp_sheet_selection_select_column (tree_view->priv->selection, tree_view->priv->focus_column);
7918 pspp_sheet_view_move_cursor_up_down (PsppSheetView *tree_view,
7920 PsppSheetSelectMode mode)
7922 gint selection_count;
7923 int cursor_node = -1;
7924 int new_cursor_node = -1;
7925 GtkTreePath *cursor_path = NULL;
7926 gboolean grab_focus = TRUE;
7928 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view)))
7932 if (!gtk_tree_row_reference_valid (tree_view->priv->cursor))
7933 /* FIXME: we lost the cursor; should we get the first? */
7936 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
7937 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
7939 if (cursor_node < 0)
7940 /* FIXME: we lost the cursor; should we get the first? */
7943 selection_count = pspp_sheet_selection_count_selected_rows (tree_view->priv->selection);
7945 if (selection_count == 0
7946 && tree_view->priv->selection->type != PSPP_SHEET_SELECTION_NONE
7947 && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
7949 /* Don't move the cursor, but just select the current node */
7950 new_cursor_node = cursor_node;
7955 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7957 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7960 gtk_tree_path_free (cursor_path);
7962 if (new_cursor_node)
7964 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
7966 search_first_focusable_path (tree_view, &cursor_path,
7971 gtk_tree_path_free (cursor_path);
7975 * If the list has only one item and multi-selection is set then select
7976 * the row (if not yet selected).
7978 if ((tree_view->priv->selection->type == PSPP_SHEET_SELECTION_MULTIPLE ||
7979 tree_view->priv->selection->type == PSPP_SHEET_SELECTION_RECTANGLE) &&
7980 new_cursor_node < 0)
7983 new_cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
7985 new_cursor_node = pspp_sheet_view_node_prev (tree_view, cursor_node);
7987 if (new_cursor_node < 0
7988 && !pspp_sheet_view_node_is_selected (tree_view, cursor_node))
7990 new_cursor_node = cursor_node;
7994 new_cursor_node = -1;
7998 if (new_cursor_node >= 0)
8000 cursor_path = _pspp_sheet_view_find_path (tree_view, new_cursor_node);
8001 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, TRUE, mode);
8002 gtk_tree_path_free (cursor_path);
8006 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8008 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8010 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view),
8012 GTK_DIR_UP : GTK_DIR_DOWN))
8014 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8017 gtk_widget_child_focus (toplevel,
8019 GTK_DIR_TAB_BACKWARD :
8020 GTK_DIR_TAB_FORWARD);
8027 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8032 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8034 return new_cursor_node >= 0;
8038 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView *tree_view,
8040 PsppSheetSelectMode mode)
8042 int cursor_node = -1;
8043 GtkTreePath *old_cursor_path = NULL;
8044 GtkTreePath *cursor_path = NULL;
8045 int start_cursor_node = -1;
8048 gint vertical_separator;
8050 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8053 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8054 old_cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8056 /* This is sorta weird. Focus in should give us a cursor */
8059 gtk_widget_style_get (GTK_WIDGET (tree_view), "vertical-separator", &vertical_separator, NULL);
8060 _pspp_sheet_view_find_node (tree_view, old_cursor_path, &cursor_node);
8062 if (cursor_node < 0)
8064 /* FIXME: we lost the cursor. Should we try to get one? */
8065 gtk_tree_path_free (old_cursor_path);
8069 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8070 window_y = RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, y);
8071 y += tree_view->priv->cursor_offset;
8072 y += count * (int)gtk_adjustment_get_page_increment (tree_view->priv->vadjustment);
8073 y = CLAMP (y, (gint)gtk_adjustment_get_lower (tree_view->priv->vadjustment), (gint)gtk_adjustment_get_upper (tree_view->priv->vadjustment) - vertical_separator);
8075 if (y >= tree_view->priv->height)
8076 y = tree_view->priv->height - 1;
8078 tree_view->priv->cursor_offset =
8079 pspp_sheet_view_find_offset (tree_view, y, &cursor_node);
8081 if (tree_view->priv->cursor_offset > BACKGROUND_HEIGHT (tree_view))
8083 cursor_node = pspp_sheet_view_node_next (tree_view, cursor_node);
8084 tree_view->priv->cursor_offset -= BACKGROUND_HEIGHT (tree_view);
8087 y -= tree_view->priv->cursor_offset;
8088 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8090 start_cursor_node = cursor_node;
8092 if (! search_first_focusable_path (tree_view, &cursor_path,
8096 /* It looks like we reached the end of the view without finding
8097 * a focusable row. We will step backwards to find the last
8100 cursor_node = start_cursor_node;
8101 cursor_path = _pspp_sheet_view_find_path (tree_view, cursor_node);
8103 search_first_focusable_path (tree_view, &cursor_path,
8112 y = pspp_sheet_view_node_find_offset (tree_view, cursor_node);
8114 pspp_sheet_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE, mode);
8117 pspp_sheet_view_scroll_to_point (tree_view, -1, y);
8118 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8119 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8121 if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
8122 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8124 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8127 gtk_tree_path_free (old_cursor_path);
8128 gtk_tree_path_free (cursor_path);
8132 pspp_sheet_view_move_cursor_left_right (PsppSheetView *tree_view,
8134 PsppSheetSelectMode mode)
8136 int cursor_node = -1;
8137 GtkTreePath *cursor_path = NULL;
8138 PsppSheetViewColumn *column;
8141 gboolean found_column = FALSE;
8144 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8146 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8149 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8150 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8154 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8155 if (cursor_node < 0)
8157 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8159 gtk_tree_path_free (cursor_path);
8162 gtk_tree_path_free (cursor_path);
8164 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8165 if (tree_view->priv->focus_column)
8167 for (; list; list = (rtl ? list->prev : list->next))
8169 if (list->data == tree_view->priv->focus_column)
8176 gboolean left, right;
8178 column = list->data;
8179 if (column->visible == FALSE || column->row_head)
8182 pspp_sheet_view_column_cell_set_cell_data (column,
8183 tree_view->priv->model,
8188 right = list->prev ? TRUE : FALSE;
8189 left = list->next ? TRUE : FALSE;
8193 left = list->prev ? TRUE : FALSE;
8194 right = list->next ? TRUE : FALSE;
8197 if (_pspp_sheet_view_column_cell_focus (column, count, left, right))
8199 tree_view->priv->focus_column = column;
8200 found_column = TRUE;
8205 list = rtl ? list->prev : list->next;
8207 list = rtl ? list->next : list->prev;
8212 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8213 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8214 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8218 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8221 pspp_sheet_view_clamp_column_visible (tree_view,
8222 tree_view->priv->focus_column, TRUE);
8226 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView *tree_view,
8228 PsppSheetSelectMode mode)
8230 int cursor_node = -1;
8231 GtkTreePath *cursor_path = NULL;
8232 PsppSheetViewColumn *column;
8233 PsppSheetViewColumn *found_column;
8238 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8240 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8243 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8244 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8248 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8249 if (cursor_node < 0)
8251 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8253 gtk_tree_path_free (cursor_path);
8256 gtk_tree_path_free (cursor_path);
8258 list = rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns);
8259 if (tree_view->priv->focus_column)
8261 for (; list; list = (rtl ? list->prev : list->next))
8263 if (list->data == tree_view->priv->focus_column)
8268 found_column = NULL;
8271 gboolean left, right;
8273 column = list->data;
8274 if (column->visible == FALSE || column->row_head)
8277 pspp_sheet_view_column_cell_set_cell_data (column,
8278 tree_view->priv->model,
8283 right = list->prev ? TRUE : FALSE;
8284 left = list->next ? TRUE : FALSE;
8288 left = list->prev ? TRUE : FALSE;
8289 right = list->next ? TRUE : FALSE;
8292 if (column->tabbable
8293 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8294 found_column = column;
8298 list = rtl ? list->prev : list->next;
8300 list = rtl ? list->next : list->prev;
8305 tree_view->priv->focus_column = found_column;
8306 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8307 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8308 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8311 pspp_sheet_view_clamp_column_visible (tree_view,
8312 tree_view->priv->focus_column, TRUE);
8316 try_move_cursor_tab (PsppSheetView *tree_view,
8317 gboolean start_at_focus_column,
8320 PsppSheetViewColumn *column;
8322 int cursor_node = -1;
8323 GtkTreePath *cursor_path = NULL;
8327 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
8328 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8332 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8333 if (cursor_node < 0)
8335 if (gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path) == FALSE)
8337 gtk_tree_path_free (cursor_path);
8340 gtk_tree_path_free (cursor_path);
8342 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
8343 if (start_at_focus_column)
8346 ? g_list_last (tree_view->priv->columns)
8347 : g_list_first (tree_view->priv->columns));
8348 if (tree_view->priv->focus_column)
8350 for (; list; list = (rtl ? list->prev : list->next))
8352 if (list->data == tree_view->priv->focus_column)
8359 list = (rtl ^ (count == 1)
8360 ? g_list_first (tree_view->priv->columns)
8361 : g_list_last (tree_view->priv->columns));
8366 gboolean left, right;
8368 column = list->data;
8369 if (column->visible == FALSE || !column->tabbable)
8372 pspp_sheet_view_column_cell_set_cell_data (column,
8373 tree_view->priv->model,
8378 right = list->prev ? TRUE : FALSE;
8379 left = list->next ? TRUE : FALSE;
8383 left = list->prev ? TRUE : FALSE;
8384 right = list->next ? TRUE : FALSE;
8387 if (column->tabbable
8388 && _pspp_sheet_view_column_cell_focus (column, count, left, right))
8390 tree_view->priv->focus_column = column;
8391 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8392 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
8393 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8398 list = rtl ? list->prev : list->next;
8400 list = rtl ? list->next : list->prev;
8407 pspp_sheet_view_move_cursor_tab (PsppSheetView *tree_view,
8410 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8413 if (!try_move_cursor_tab (tree_view, TRUE, count))
8415 if (pspp_sheet_view_move_cursor_up_down (tree_view, count, 0)
8416 && !try_move_cursor_tab (tree_view, FALSE, count))
8417 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8420 pspp_sheet_view_clamp_column_visible (tree_view,
8421 tree_view->priv->focus_column, TRUE);
8425 pspp_sheet_view_move_cursor_start_end (PsppSheetView *tree_view,
8427 PsppSheetSelectMode mode)
8431 GtkTreePath *old_path;
8433 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8436 g_return_if_fail (tree_view->priv->row_count > 0);
8438 pspp_sheet_view_get_cursor (tree_view, &old_path, NULL);
8442 /* Now go forward to find the first focusable row. */
8443 path = _pspp_sheet_view_find_path (tree_view, 0);
8444 search_first_focusable_path (tree_view, &path,
8445 TRUE, &cursor_node);
8449 /* Now go backwards to find last focusable row. */
8450 path = _pspp_sheet_view_find_path (tree_view, tree_view->priv->row_count - 1);
8451 search_first_focusable_path (tree_view, &path,
8452 FALSE, &cursor_node);
8458 if (gtk_tree_path_compare (old_path, path))
8460 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, mode);
8461 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8465 gtk_widget_error_bell (GTK_WIDGET (tree_view));
8469 gtk_tree_path_free (old_path);
8470 gtk_tree_path_free (path);
8474 pspp_sheet_view_real_select_all (PsppSheetView *tree_view)
8476 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8479 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8480 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8483 pspp_sheet_selection_select_all (tree_view->priv->selection);
8489 pspp_sheet_view_real_unselect_all (PsppSheetView *tree_view)
8491 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8494 if (tree_view->priv->selection->type != PSPP_SHEET_SELECTION_MULTIPLE &&
8495 tree_view->priv->selection->type != PSPP_SHEET_SELECTION_RECTANGLE)
8498 pspp_sheet_selection_unselect_all (tree_view->priv->selection);
8504 pspp_sheet_view_real_select_cursor_row (PsppSheetView *tree_view,
8505 gboolean start_editing,
8506 PsppSheetSelectMode mode)
8509 int cursor_node = -1;
8510 GtkTreePath *cursor_path = NULL;
8512 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8515 if (tree_view->priv->cursor)
8516 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8518 if (cursor_path == NULL)
8521 _pspp_sheet_view_find_node (tree_view, cursor_path,
8524 if (cursor_node < 0)
8526 gtk_tree_path_free (cursor_path);
8530 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND) && start_editing &&
8531 tree_view->priv->focus_column)
8533 if (pspp_sheet_view_start_editing (tree_view, cursor_path))
8535 gtk_tree_path_free (cursor_path);
8540 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8546 /* We bail out if the original (tree, node) don't exist anymore after
8547 * handling the selection-changed callback. We do return TRUE because
8548 * the key press has been handled at this point.
8550 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8552 if (cursor_node != new_node)
8555 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8557 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8558 _pspp_sheet_view_queue_draw_node (tree_view, cursor_node, NULL);
8560 if (!(mode & PSPP_SHEET_SELECT_MODE_EXTEND))
8561 pspp_sheet_view_row_activated (tree_view, cursor_path,
8562 tree_view->priv->focus_column);
8564 gtk_tree_path_free (cursor_path);
8570 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView *tree_view)
8573 int cursor_node = -1;
8574 GtkTreePath *cursor_path = NULL;
8576 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8580 if (tree_view->priv->cursor)
8581 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
8583 if (cursor_path == NULL)
8586 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
8587 if (cursor_node < 0)
8589 gtk_tree_path_free (cursor_path);
8593 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
8596 PSPP_SHEET_SELECT_MODE_TOGGLE,
8599 /* We bail out if the original (tree, node) don't exist anymore after
8600 * handling the selection-changed callback. We do return TRUE because
8601 * the key press has been handled at this point.
8603 _pspp_sheet_view_find_node (tree_view, cursor_path, &new_node);
8605 if (cursor_node != new_node)
8608 pspp_sheet_view_clamp_node_visible (tree_view, cursor_node);
8610 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
8611 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
8612 gtk_tree_path_free (cursor_path);
8618 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView *tree_view)
8620 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window, tree_view);
8621 tree_view->priv->typeselect_flush_timeout = 0;
8626 /* Cut and paste from gtkwindow.c */
8628 send_focus_change (GtkWidget *widget,
8631 GdkEvent *fevent = gdk_event_new (GDK_FOCUS_CHANGE);
8633 fevent->focus_change.type = GDK_FOCUS_CHANGE;
8634 fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
8635 fevent->focus_change.in = in;
8637 gtk_widget_send_focus_change (widget, fevent);
8638 gdk_event_free (fevent);
8642 pspp_sheet_view_ensure_interactive_directory (PsppSheetView *tree_view)
8644 GtkWidget *frame, *vbox, *toplevel;
8647 if (tree_view->priv->search_custom_entry_set)
8650 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (tree_view));
8651 screen = gtk_widget_get_screen (GTK_WIDGET (tree_view));
8653 if (tree_view->priv->search_window != NULL)
8655 if (gtk_window_get_group (GTK_WINDOW (toplevel)))
8656 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
8657 GTK_WINDOW (tree_view->priv->search_window));
8658 else if (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)))
8659 gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view->priv->search_window)),
8660 GTK_WINDOW (tree_view->priv->search_window));
8661 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
8665 tree_view->priv->search_window = gtk_window_new (GTK_WINDOW_POPUP);
8666 gtk_window_set_screen (GTK_WINDOW (tree_view->priv->search_window), screen);
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));
8672 gtk_window_set_type_hint (GTK_WINDOW (tree_view->priv->search_window),
8673 GDK_WINDOW_TYPE_HINT_UTILITY);
8674 gtk_window_set_modal (GTK_WINDOW (tree_view->priv->search_window), TRUE);
8675 g_signal_connect (tree_view->priv->search_window, "delete-event",
8676 G_CALLBACK (pspp_sheet_view_search_delete_event),
8678 g_signal_connect (tree_view->priv->search_window, "key-press-event",
8679 G_CALLBACK (pspp_sheet_view_search_key_press_event),
8681 g_signal_connect (tree_view->priv->search_window, "button-press-event",
8682 G_CALLBACK (pspp_sheet_view_search_button_press_event),
8684 g_signal_connect (tree_view->priv->search_window, "scroll-event",
8685 G_CALLBACK (pspp_sheet_view_search_scroll_event),
8688 frame = gtk_frame_new (NULL);
8689 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
8690 gtk_widget_show (frame);
8691 gtk_container_add (GTK_CONTAINER (tree_view->priv->search_window), frame);
8693 vbox = gtk_vbox_new (FALSE, 0);
8694 gtk_widget_show (vbox);
8695 gtk_container_add (GTK_CONTAINER (frame), vbox);
8696 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
8699 tree_view->priv->search_entry = gtk_entry_new ();
8700 gtk_widget_show (tree_view->priv->search_entry);
8701 g_signal_connect (tree_view->priv->search_entry, "populate-popup",
8702 G_CALLBACK (pspp_sheet_view_search_disable_popdown),
8704 g_signal_connect (tree_view->priv->search_entry,
8705 "activate", G_CALLBACK (pspp_sheet_view_search_activate),
8709 g_signal_connect (GTK_ENTRY (tree_view->priv->search_entry)->im_context,
8711 G_CALLBACK (pspp_sheet_view_search_preedit_changed),
8715 gtk_container_add (GTK_CONTAINER (vbox),
8716 tree_view->priv->search_entry);
8718 gtk_widget_realize (tree_view->priv->search_entry);
8721 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8722 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8725 pspp_sheet_view_real_start_interactive_search (PsppSheetView *tree_view,
8726 gboolean keybinding)
8728 /* We only start interactive search if we have focus or the columns
8729 * have focus. If one of our children have focus, we don't want to
8733 gboolean found_focus = FALSE;
8734 GtkWidgetClass *entry_parent_class;
8736 if (!tree_view->priv->enable_search && !keybinding)
8739 if (tree_view->priv->search_custom_entry_set)
8742 if (tree_view->priv->search_window != NULL &&
8743 gtk_widget_get_visible (tree_view->priv->search_window))
8746 for (list = tree_view->priv->columns; list; list = list->next)
8748 PsppSheetViewColumn *column;
8750 column = list->data;
8751 if (! column->visible)
8754 if (column->button && gtk_widget_has_focus (column->button))
8761 if (gtk_widget_has_focus (GTK_WIDGET (tree_view)))
8767 if (tree_view->priv->search_column < 0)
8770 pspp_sheet_view_ensure_interactive_directory (tree_view);
8773 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
8776 tree_view->priv->search_position_func (tree_view, tree_view->priv->search_window, tree_view->priv->search_position_user_data);
8777 gtk_widget_show (tree_view->priv->search_window);
8778 if (tree_view->priv->search_entry_changed_id == 0)
8780 tree_view->priv->search_entry_changed_id =
8781 g_signal_connect (tree_view->priv->search_entry, "changed",
8782 G_CALLBACK (pspp_sheet_view_search_init),
8786 tree_view->priv->typeselect_flush_timeout =
8787 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
8788 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
8791 /* Grab focus will select all the text. We don't want that to happen, so we
8792 * call the parent instance and bypass the selection change. This is probably
8793 * really non-kosher. */
8794 entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view->priv->search_entry));
8795 (entry_parent_class->grab_focus) (tree_view->priv->search_entry);
8797 /* send focus-in event */
8798 send_focus_change (tree_view->priv->search_entry, TRUE);
8800 /* search first matching iter */
8801 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
8807 pspp_sheet_view_start_interactive_search (PsppSheetView *tree_view)
8809 return pspp_sheet_view_real_start_interactive_search (tree_view, TRUE);
8812 /* this function returns the new width of the column being resized given
8813 * the column and x position of the cursor; the x cursor position is passed
8814 * in as a pointer and automagicly corrected if it's beyond min/max limits
8817 pspp_sheet_view_new_column_width (PsppSheetView *tree_view,
8821 PsppSheetViewColumn *column;
8825 /* first translate the x position from gtk_widget_get_window (widget)
8826 * to clist->clist_window
8828 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
8829 column = g_list_nth (tree_view->priv->columns, i)->data;
8830 width = rtl ? (column->allocation.x + column->allocation.width - *x) : (*x - column->allocation.x);
8832 /* Clamp down the value */
8833 if (column->min_width == -1)
8834 width = MAX (column->button_request, width);
8836 width = MAX (column->min_width, width);
8837 if (column->max_width != -1)
8838 width = MIN (width, column->max_width);
8840 *x = rtl ? (column->allocation.x + column->allocation.width - width) : (column->allocation.x + width);
8846 /* FIXME this adjust_allocation is a big cut-and-paste from
8847 * GtkCList, needs to be some "official" way to do this
8857 /* The window to which gtk_widget_get_window (widget) is relative */
8858 #define ALLOCATION_WINDOW(widget) \
8859 (!gtk_widget_get_has_window (widget) ? \
8860 gtk_widget_get_window (widget) : \
8861 gdk_window_get_parent (gtk_widget_get_window (widget)))
8864 adjust_allocation_recurse (GtkWidget *widget,
8867 ScrollData *scroll_data = data;
8868 GtkAllocation allocation;
8869 gtk_widget_get_allocation (widget, &allocation);
8870 /* Need to really size allocate instead of just poking
8871 * into widget->allocation if the widget is not realized.
8872 * FIXME someone figure out why this was.
8874 if (!gtk_widget_get_realized (widget))
8876 if (gtk_widget_get_visible (widget))
8878 GdkRectangle tmp_rectangle = allocation;
8879 tmp_rectangle.x += scroll_data->dx;
8880 tmp_rectangle.y += scroll_data->dy;
8882 gtk_widget_size_allocate (widget, &tmp_rectangle);
8887 if (ALLOCATION_WINDOW (widget) == scroll_data->window)
8889 allocation.x += scroll_data->dx;
8890 allocation.y += scroll_data->dy;
8892 if (GTK_IS_CONTAINER (widget))
8893 gtk_container_forall (GTK_CONTAINER (widget),
8894 adjust_allocation_recurse,
8901 adjust_allocation (GtkWidget *widget,
8905 ScrollData scroll_data;
8907 if (gtk_widget_get_realized (widget))
8908 scroll_data.window = ALLOCATION_WINDOW (widget);
8910 scroll_data.window = NULL;
8912 scroll_data.dx = dx;
8913 scroll_data.dy = dy;
8915 adjust_allocation_recurse (widget, &scroll_data);
8919 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
8923 pspp_sheet_view_adjustment_changed (GtkAdjustment *adjustment,
8924 PsppSheetView *tree_view)
8926 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
8931 gdk_window_move (tree_view->priv->bin_window,
8932 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8933 TREE_VIEW_HEADER_HEIGHT (tree_view));
8934 gdk_window_move (tree_view->priv->header_window,
8935 - gtk_adjustment_get_value (tree_view->priv->hadjustment),
8937 dy = tree_view->priv->dy - (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8940 update_prelight (tree_view,
8941 tree_view->priv->event_last_x,
8942 tree_view->priv->event_last_y - dy);
8944 if (tree_view->priv->edited_column &&
8945 GTK_IS_WIDGET (tree_view->priv->edited_column->editable_widget))
8949 PsppSheetViewChild *child = NULL;
8951 widget = GTK_WIDGET (tree_view->priv->edited_column->editable_widget);
8952 adjust_allocation (widget, 0, dy);
8954 for (list = tree_view->priv->children; list; list = list->next)
8956 child = (PsppSheetViewChild *)list->data;
8957 if (child->widget == widget)
8965 gdk_window_scroll (tree_view->priv->bin_window, 0, dy);
8967 if (tree_view->priv->dy != (int) gtk_adjustment_get_value (tree_view->priv->vadjustment))
8969 /* update our dy and top_row */
8970 tree_view->priv->dy = (int) gtk_adjustment_get_value (tree_view->priv->vadjustment);
8972 if (!tree_view->priv->in_top_row_to_dy)
8973 pspp_sheet_view_dy_to_top_row (tree_view);
8984 * pspp_sheet_view_new:
8986 * Creates a new #PsppSheetView widget.
8988 * Return value: A newly created #PsppSheetView widget.
8991 pspp_sheet_view_new (void)
8993 return g_object_new (PSPP_TYPE_SHEET_VIEW, NULL);
8997 * pspp_sheet_view_new_with_model:
8998 * @model: the model.
9000 * Creates a new #PsppSheetView widget with the model initialized to @model.
9002 * Return value: A newly created #PsppSheetView widget.
9005 pspp_sheet_view_new_with_model (GtkTreeModel *model)
9007 return g_object_new (PSPP_TYPE_SHEET_VIEW, "model", model, NULL);
9014 * pspp_sheet_view_get_model:
9015 * @tree_view: a #PsppSheetView
9017 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
9020 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
9023 pspp_sheet_view_get_model (PsppSheetView *tree_view)
9025 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9027 return tree_view->priv->model;
9031 * pspp_sheet_view_set_model:
9032 * @tree_view: A #GtkTreeNode.
9033 * @model: (allow-none): The model.
9035 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
9036 * set, it will remove it before setting the new model. If @model is %NULL,
9037 * then it will unset the old model.
9040 pspp_sheet_view_set_model (PsppSheetView *tree_view,
9041 GtkTreeModel *model)
9043 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9044 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
9046 if (model == tree_view->priv->model)
9049 if (tree_view->priv->scroll_to_path)
9051 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9052 tree_view->priv->scroll_to_path = NULL;
9055 if (tree_view->priv->model)
9057 GList *tmplist = tree_view->priv->columns;
9059 if (tree_view->priv->selected)
9060 range_tower_set0 (tree_view->priv->selected, 0, ULONG_MAX);
9061 pspp_sheet_view_stop_editing (tree_view, TRUE);
9063 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9064 pspp_sheet_view_row_changed,
9066 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9067 pspp_sheet_view_row_inserted,
9069 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9070 pspp_sheet_view_row_deleted,
9072 g_signal_handlers_disconnect_by_func (tree_view->priv->model,
9073 pspp_sheet_view_rows_reordered,
9076 for (; tmplist; tmplist = tmplist->next)
9077 _pspp_sheet_view_column_unset_model (tmplist->data,
9078 tree_view->priv->model);
9080 tree_view->priv->prelight_node = -1;
9082 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
9083 tree_view->priv->drag_dest_row = NULL;
9084 gtk_tree_row_reference_free (tree_view->priv->cursor);
9085 tree_view->priv->cursor = NULL;
9086 gtk_tree_row_reference_free (tree_view->priv->anchor);
9087 tree_view->priv->anchor = NULL;
9088 gtk_tree_row_reference_free (tree_view->priv->top_row);
9089 tree_view->priv->top_row = NULL;
9090 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9091 tree_view->priv->scroll_to_path = NULL;
9093 tree_view->priv->scroll_to_column = NULL;
9095 g_object_unref (tree_view->priv->model);
9097 tree_view->priv->search_column = -1;
9098 tree_view->priv->fixed_height = -1;
9099 tree_view->priv->dy = tree_view->priv->top_row_dy = 0;
9100 tree_view->priv->last_button_x = -1;
9101 tree_view->priv->last_button_y = -1;
9104 tree_view->priv->model = model;
9106 if (tree_view->priv->model)
9110 if (tree_view->priv->search_column == -1)
9112 for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
9114 GType type = gtk_tree_model_get_column_type (model, i);
9116 if (g_value_type_transformable (type, G_TYPE_STRING))
9118 tree_view->priv->search_column = i;
9124 g_object_ref (tree_view->priv->model);
9125 g_signal_connect (tree_view->priv->model,
9127 G_CALLBACK (pspp_sheet_view_row_changed),
9129 g_signal_connect (tree_view->priv->model,
9131 G_CALLBACK (pspp_sheet_view_row_inserted),
9133 g_signal_connect (tree_view->priv->model,
9135 G_CALLBACK (pspp_sheet_view_row_deleted),
9137 g_signal_connect (tree_view->priv->model,
9139 G_CALLBACK (pspp_sheet_view_rows_reordered),
9142 tree_view->priv->row_count = gtk_tree_model_iter_n_children (tree_view->priv->model, NULL);
9144 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
9145 install_presize_handler (tree_view);
9148 g_object_notify (G_OBJECT (tree_view), "model");
9150 if (tree_view->priv->selection)
9151 _pspp_sheet_selection_emit_changed (tree_view->priv->selection);
9153 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9154 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9158 * pspp_sheet_view_get_selection:
9159 * @tree_view: A #PsppSheetView.
9161 * Gets the #PsppSheetSelection associated with @tree_view.
9163 * Return value: A #PsppSheetSelection object.
9165 PsppSheetSelection *
9166 pspp_sheet_view_get_selection (PsppSheetView *tree_view)
9168 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9170 return tree_view->priv->selection;
9174 * pspp_sheet_view_get_hadjustment:
9175 * @tree_view: A #PsppSheetView
9177 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
9179 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9183 pspp_sheet_view_get_hadjustment (PsppSheetView *tree_view)
9185 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9187 return pspp_sheet_view_do_get_hadjustment (tree_view);
9190 static GtkAdjustment *
9191 pspp_sheet_view_do_get_hadjustment (PsppSheetView *tree_view)
9193 return tree_view->priv->hadjustment;
9197 * pspp_sheet_view_set_hadjustment:
9198 * @tree_view: A #PsppSheetView
9199 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9201 * Sets the #GtkAdjustment for the current horizontal aspect.
9204 pspp_sheet_view_set_hadjustment (PsppSheetView *tree_view,
9205 GtkAdjustment *adjustment)
9207 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9209 pspp_sheet_view_set_adjustments (tree_view,
9211 tree_view->priv->vadjustment);
9213 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9217 pspp_sheet_view_do_set_hadjustment (PsppSheetView *tree_view,
9218 GtkAdjustment *adjustment)
9220 PsppSheetViewPrivate *priv = tree_view->priv;
9222 if (adjustment && priv->hadjustment == adjustment)
9225 if (priv->hadjustment != NULL)
9227 g_signal_handlers_disconnect_by_func (priv->hadjustment,
9228 pspp_sheet_view_adjustment_changed,
9230 g_object_unref (priv->hadjustment);
9233 if (adjustment == NULL)
9234 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9237 g_signal_connect (adjustment, "value-changed",
9238 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9239 priv->hadjustment = g_object_ref_sink (adjustment);
9240 /* FIXME: Adjustment should probably be populated here with fresh values, but
9241 * internal details are too complicated for me to decipher right now.
9243 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9245 g_object_notify (G_OBJECT (tree_view), "hadjustment");
9249 * pspp_sheet_view_get_vadjustment:
9250 * @tree_view: A #PsppSheetView
9252 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9254 * Return value: (transfer none): A #GtkAdjustment object, or %NULL
9255 * if none is currently being used.
9257 * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
9260 pspp_sheet_view_get_vadjustment (PsppSheetView *tree_view)
9262 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9264 return pspp_sheet_view_do_get_vadjustment (tree_view);
9267 static GtkAdjustment *
9268 pspp_sheet_view_do_get_vadjustment (PsppSheetView *tree_view)
9270 return tree_view->priv->vadjustment;
9274 * pspp_sheet_view_set_vadjustment:
9275 * @tree_view: A #PsppSheetView
9276 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9278 * Sets the #GtkAdjustment for the current vertical aspect.
9280 * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
9283 pspp_sheet_view_set_vadjustment (PsppSheetView *tree_view,
9284 GtkAdjustment *adjustment)
9286 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9287 g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
9289 pspp_sheet_view_do_set_vadjustment (tree_view, adjustment);
9293 pspp_sheet_view_do_set_vadjustment (PsppSheetView *tree_view,
9294 GtkAdjustment *adjustment)
9296 PsppSheetViewPrivate *priv = tree_view->priv;
9298 if (adjustment && priv->vadjustment == adjustment)
9301 if (priv->vadjustment != NULL)
9303 g_signal_handlers_disconnect_by_func (priv->vadjustment,
9304 pspp_sheet_view_adjustment_changed,
9306 g_object_unref (priv->vadjustment);
9309 if (adjustment == NULL)
9310 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0,
9313 g_signal_connect (adjustment, "value-changed",
9314 G_CALLBACK (pspp_sheet_view_adjustment_changed), tree_view);
9315 priv->vadjustment = g_object_ref_sink (adjustment);
9316 /* FIXME: Adjustment should probably be populated here with fresh values, but
9317 * internal details are too complicated for me to decipher right now.
9319 pspp_sheet_view_adjustment_changed (NULL, tree_view);
9320 g_object_notify (G_OBJECT (tree_view), "vadjustment");
9323 /* Column and header operations */
9326 * pspp_sheet_view_get_headers_visible:
9327 * @tree_view: A #PsppSheetView.
9329 * Returns %TRUE if the headers on the @tree_view are visible.
9331 * Return value: Whether the headers are visible or not.
9334 pspp_sheet_view_get_headers_visible (PsppSheetView *tree_view)
9336 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9338 return PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9342 * pspp_sheet_view_set_headers_visible:
9343 * @tree_view: A #PsppSheetView.
9344 * @headers_visible: %TRUE if the headers are visible
9346 * Sets the visibility state of the headers.
9349 pspp_sheet_view_set_headers_visible (PsppSheetView *tree_view,
9350 gboolean headers_visible)
9354 PsppSheetViewColumn *column;
9355 GtkAllocation allocation;
9357 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9359 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
9361 headers_visible = !! headers_visible;
9363 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE) == headers_visible)
9366 if (headers_visible)
9367 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9369 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view, PSPP_SHEET_VIEW_HEADERS_VISIBLE);
9371 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9373 gdk_window_get_position (tree_view->priv->bin_window, &x, &y);
9374 if (headers_visible)
9376 gdk_window_move_resize (tree_view->priv->bin_window, x, y + TREE_VIEW_HEADER_HEIGHT (tree_view),
9377 tree_view->priv->width, allocation.height - + TREE_VIEW_HEADER_HEIGHT (tree_view));
9379 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view)))
9380 pspp_sheet_view_map_buttons (tree_view);
9384 gdk_window_move_resize (tree_view->priv->bin_window, x, y, tree_view->priv->width, tree_view->priv->height);
9386 for (list = tree_view->priv->columns; list; list = list->next)
9388 column = list->data;
9390 gtk_widget_unmap (column->button);
9392 gdk_window_hide (tree_view->priv->header_window);
9396 gtk_adjustment_set_page_size (tree_view->priv->vadjustment, allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view));
9397 gtk_adjustment_set_page_increment (tree_view->priv->vadjustment, (allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view)) / 2);
9398 gtk_adjustment_set_lower (tree_view->priv->vadjustment, 0);
9399 gtk_adjustment_set_upper (tree_view->priv->vadjustment, tree_view->priv->height);
9400 gtk_adjustment_changed (tree_view->priv->vadjustment);
9402 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9404 g_object_notify (G_OBJECT (tree_view), "headers-visible");
9408 * pspp_sheet_view_columns_autosize:
9409 * @tree_view: A #PsppSheetView.
9411 * Resizes all columns to their optimal width. Only works after the
9412 * treeview has been realized.
9415 pspp_sheet_view_columns_autosize (PsppSheetView *tree_view)
9417 gboolean dirty = FALSE;
9419 PsppSheetViewColumn *column;
9421 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9423 for (list = tree_view->priv->columns; list; list = list->next)
9425 column = list->data;
9426 _pspp_sheet_view_column_cell_set_dirty (column);
9431 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9435 * pspp_sheet_view_set_headers_clickable:
9436 * @tree_view: A #PsppSheetView.
9437 * @setting: %TRUE if the columns are clickable.
9439 * Allow the column title buttons to be clicked.
9442 pspp_sheet_view_set_headers_clickable (PsppSheetView *tree_view,
9447 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9449 for (list = tree_view->priv->columns; list; list = list->next)
9450 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list->data), setting);
9452 g_object_notify (G_OBJECT (tree_view), "headers-clickable");
9457 * pspp_sheet_view_get_headers_clickable:
9458 * @tree_view: A #PsppSheetView.
9460 * Returns whether all header columns are clickable.
9462 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9467 pspp_sheet_view_get_headers_clickable (PsppSheetView *tree_view)
9471 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9473 for (list = tree_view->priv->columns; list; list = list->next)
9474 if (!PSPP_SHEET_VIEW_COLUMN (list->data)->clickable)
9481 * pspp_sheet_view_set_rules_hint
9482 * @tree_view: a #PsppSheetView
9483 * @setting: %TRUE if the tree requires reading across rows
9485 * This function tells GTK+ that the user interface for your
9486 * application requires users to read across tree rows and associate
9487 * cells with one another. By default, GTK+ will then render the tree
9488 * with alternating row colors. Do <emphasis>not</emphasis> use it
9489 * just because you prefer the appearance of the ruled tree; that's a
9490 * question for the theme. Some themes will draw tree rows in
9491 * alternating colors even when rules are turned off, and users who
9492 * prefer that appearance all the time can choose those themes. You
9493 * should call this function only as a <emphasis>semantic</emphasis>
9494 * hint to the theme engine that your tree makes alternating colors
9495 * useful from a functional standpoint (since it has lots of columns,
9500 pspp_sheet_view_set_rules_hint (PsppSheetView *tree_view,
9503 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9505 setting = setting != FALSE;
9507 if (tree_view->priv->has_rules != setting)
9509 tree_view->priv->has_rules = setting;
9510 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
9513 g_object_notify (G_OBJECT (tree_view), "rules-hint");
9517 * pspp_sheet_view_get_rules_hint
9518 * @tree_view: a #PsppSheetView
9520 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9522 * Return value: %TRUE if rules are useful for the user of this tree
9525 pspp_sheet_view_get_rules_hint (PsppSheetView *tree_view)
9527 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
9529 return tree_view->priv->has_rules;
9532 /* Public Column functions
9536 * pspp_sheet_view_append_column:
9537 * @tree_view: A #PsppSheetView.
9538 * @column: The #PsppSheetViewColumn to add.
9540 * Appends @column to the list of columns.
9542 * Return value: The number of columns in @tree_view after appending.
9545 pspp_sheet_view_append_column (PsppSheetView *tree_view,
9546 PsppSheetViewColumn *column)
9548 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9549 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9550 g_return_val_if_fail (column->tree_view == NULL, -1);
9552 return pspp_sheet_view_insert_column (tree_view, column, -1);
9557 * pspp_sheet_view_remove_column:
9558 * @tree_view: A #PsppSheetView.
9559 * @column: The #PsppSheetViewColumn to remove.
9561 * Removes @column from @tree_view.
9563 * Return value: The number of columns in @tree_view after removing.
9566 pspp_sheet_view_remove_column (PsppSheetView *tree_view,
9567 PsppSheetViewColumn *column)
9569 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9570 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9571 g_return_val_if_fail (column->tree_view == GTK_WIDGET (tree_view), -1);
9573 if (tree_view->priv->focus_column == column)
9574 tree_view->priv->focus_column = NULL;
9576 if (tree_view->priv->edited_column == column)
9578 pspp_sheet_view_stop_editing (tree_view, TRUE);
9580 /* no need to, but just to be sure ... */
9581 tree_view->priv->edited_column = NULL;
9584 _pspp_sheet_view_column_unset_tree_view (column);
9586 tree_view->priv->columns = g_list_remove (tree_view->priv->columns, column);
9587 tree_view->priv->n_columns--;
9589 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9593 _pspp_sheet_view_column_unrealize_button (column);
9594 for (list = tree_view->priv->columns; list; list = list->next)
9596 PsppSheetViewColumn *tmp_column;
9598 tmp_column = PSPP_SHEET_VIEW_COLUMN (list->data);
9599 if (tmp_column->visible)
9600 _pspp_sheet_view_column_cell_set_dirty (tmp_column);
9603 if (tree_view->priv->n_columns == 0 &&
9604 pspp_sheet_view_get_headers_visible (tree_view) &&
9605 tree_view->priv->header_window)
9606 gdk_window_hide (tree_view->priv->header_window);
9608 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9611 g_object_unref (column);
9612 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9614 return tree_view->priv->n_columns;
9618 * pspp_sheet_view_insert_column:
9619 * @tree_view: A #PsppSheetView.
9620 * @column: The #PsppSheetViewColumn to be inserted.
9621 * @position: The position to insert @column in.
9623 * This inserts the @column into the @tree_view at @position. If @position is
9624 * -1, then the column is inserted at the end.
9626 * Return value: The number of columns in @tree_view after insertion.
9629 pspp_sheet_view_insert_column (PsppSheetView *tree_view,
9630 PsppSheetViewColumn *column,
9633 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9634 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), -1);
9635 g_return_val_if_fail (column->tree_view == NULL, -1);
9637 g_object_ref_sink (column);
9639 if (tree_view->priv->n_columns == 0 &&
9640 gtk_widget_get_realized (GTK_WIDGET (tree_view)) &&
9641 pspp_sheet_view_get_headers_visible (tree_view))
9643 gdk_window_show (tree_view->priv->header_window);
9646 tree_view->priv->columns = g_list_insert (tree_view->priv->columns,
9648 tree_view->priv->n_columns++;
9650 _pspp_sheet_view_column_set_tree_view (column, tree_view);
9652 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9656 _pspp_sheet_view_column_realize_button (column);
9658 for (list = tree_view->priv->columns; list; list = list->next)
9660 column = PSPP_SHEET_VIEW_COLUMN (list->data);
9661 if (column->visible)
9662 _pspp_sheet_view_column_cell_set_dirty (column);
9664 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9667 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9669 return tree_view->priv->n_columns;
9673 * pspp_sheet_view_insert_column_with_attributes:
9674 * @tree_view: A #PsppSheetView
9675 * @position: The position to insert the new column in.
9676 * @title: The title to set the header to.
9677 * @cell: The #GtkCellRenderer.
9678 * @Varargs: A %NULL-terminated list of attributes.
9680 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9681 * @position. If @position is -1, then the newly created column is inserted at
9682 * the end. The column is initialized with the attributes given.
9684 * Return value: The number of columns in @tree_view after insertion.
9687 pspp_sheet_view_insert_column_with_attributes (PsppSheetView *tree_view,
9690 GtkCellRenderer *cell,
9693 PsppSheetViewColumn *column;
9698 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9700 column = pspp_sheet_view_column_new ();
9701 pspp_sheet_view_column_set_title (column, title);
9702 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9704 va_start (args, cell);
9706 attribute = va_arg (args, gchar *);
9708 while (attribute != NULL)
9710 column_id = va_arg (args, gint);
9711 pspp_sheet_view_column_add_attribute (column, cell, attribute, column_id);
9712 attribute = va_arg (args, gchar *);
9717 pspp_sheet_view_insert_column (tree_view, column, position);
9719 return tree_view->priv->n_columns;
9723 * pspp_sheet_view_insert_column_with_data_func:
9724 * @tree_view: a #PsppSheetView
9725 * @position: Position to insert, -1 for append
9726 * @title: column title
9727 * @cell: cell renderer for column
9728 * @func: function to set attributes of cell renderer
9729 * @data: data for @func
9730 * @dnotify: destroy notifier for @data
9732 * Convenience function that inserts a new column into the #PsppSheetView
9733 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9734 * attributes (normally using data from the model). See also
9735 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9737 * Return value: number of columns in the tree view post-insert
9740 pspp_sheet_view_insert_column_with_data_func (PsppSheetView *tree_view,
9743 GtkCellRenderer *cell,
9744 PsppSheetCellDataFunc func,
9746 GDestroyNotify dnotify)
9748 PsppSheetViewColumn *column;
9750 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
9752 column = pspp_sheet_view_column_new ();
9753 pspp_sheet_view_column_set_title (column, title);
9754 pspp_sheet_view_column_pack_start (column, cell, TRUE);
9755 pspp_sheet_view_column_set_cell_data_func (column, cell, func, data, dnotify);
9757 pspp_sheet_view_insert_column (tree_view, column, position);
9759 return tree_view->priv->n_columns;
9763 * pspp_sheet_view_get_column:
9764 * @tree_view: A #PsppSheetView.
9765 * @n: The position of the column, counting from 0.
9767 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9769 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9772 PsppSheetViewColumn *
9773 pspp_sheet_view_get_column (PsppSheetView *tree_view,
9776 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9778 if (n < 0 || n >= tree_view->priv->n_columns)
9781 if (tree_view->priv->columns == NULL)
9784 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view->priv->columns, n)->data);
9788 * pspp_sheet_view_get_columns:
9789 * @tree_view: A #PsppSheetView
9791 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9792 * The returned list must be freed with g_list_free ().
9794 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9797 pspp_sheet_view_get_columns (PsppSheetView *tree_view)
9799 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
9801 return g_list_copy (tree_view->priv->columns);
9805 * pspp_sheet_view_move_column_after:
9806 * @tree_view: A #PsppSheetView
9807 * @column: The #PsppSheetViewColumn to be moved.
9808 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9810 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9811 * @column is placed in the first position.
9814 pspp_sheet_view_move_column_after (PsppSheetView *tree_view,
9815 PsppSheetViewColumn *column,
9816 PsppSheetViewColumn *base_column)
9818 GList *column_list_el, *base_el = NULL;
9820 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9822 column_list_el = g_list_find (tree_view->priv->columns, column);
9823 g_return_if_fail (column_list_el != NULL);
9827 base_el = g_list_find (tree_view->priv->columns, base_column);
9828 g_return_if_fail (base_el != NULL);
9831 if (column_list_el->prev == base_el)
9834 tree_view->priv->columns = g_list_remove_link (tree_view->priv->columns, column_list_el);
9835 if (base_el == NULL)
9837 column_list_el->prev = NULL;
9838 column_list_el->next = tree_view->priv->columns;
9839 if (column_list_el->next)
9840 column_list_el->next->prev = column_list_el;
9841 tree_view->priv->columns = column_list_el;
9845 column_list_el->prev = base_el;
9846 column_list_el->next = base_el->next;
9847 if (column_list_el->next)
9848 column_list_el->next->prev = column_list_el;
9849 base_el->next = column_list_el;
9852 if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
9854 gtk_widget_queue_resize (GTK_WIDGET (tree_view));
9855 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view), NULL);
9858 g_signal_emit (tree_view, tree_view_signals[COLUMNS_CHANGED], 0);
9862 * pspp_sheet_view_set_column_drag_function:
9863 * @tree_view: A #PsppSheetView.
9864 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9865 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9866 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9868 * Sets a user function for determining where a column may be dropped when
9869 * dragged. This function is called on every column pair in turn at the
9870 * beginning of a column drag to determine where a drop can take place. The
9871 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9872 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9873 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9874 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9875 * @tree_view reverts to the default behavior of allowing all columns to be
9876 * dropped everywhere.
9879 pspp_sheet_view_set_column_drag_function (PsppSheetView *tree_view,
9880 PsppSheetViewColumnDropFunc func,
9882 GDestroyNotify destroy)
9884 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9886 if (tree_view->priv->column_drop_func_data_destroy)
9887 tree_view->priv->column_drop_func_data_destroy (tree_view->priv->column_drop_func_data);
9889 tree_view->priv->column_drop_func = func;
9890 tree_view->priv->column_drop_func_data = user_data;
9891 tree_view->priv->column_drop_func_data_destroy = destroy;
9895 * pspp_sheet_view_scroll_to_point:
9896 * @tree_view: a #PsppSheetView
9897 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9898 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9900 * Scrolls the tree view such that the top-left corner of the visible
9901 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9902 * in tree coordinates. The @tree_view must be realized before
9903 * this function is called. If it isn't, you probably want to be
9904 * using pspp_sheet_view_scroll_to_cell().
9906 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9909 pspp_sheet_view_scroll_to_point (PsppSheetView *tree_view,
9913 GtkAdjustment *hadj;
9914 GtkAdjustment *vadj;
9916 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9917 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
9919 hadj = tree_view->priv->hadjustment;
9920 vadj = tree_view->priv->vadjustment;
9923 gtk_adjustment_set_value (hadj, CLAMP (tree_x, gtk_adjustment_get_lower (hadj), gtk_adjustment_get_upper (hadj) - gtk_adjustment_get_page_size (hadj)));
9925 gtk_adjustment_set_value (vadj, CLAMP (tree_y, gtk_adjustment_get_lower (vadj), gtk_adjustment_get_upper (vadj) - gtk_adjustment_get_page_size (vadj)));
9929 * pspp_sheet_view_scroll_to_cell:
9930 * @tree_view: A #PsppSheetView.
9931 * @path: (allow-none): The path of the row to move to, or %NULL.
9932 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9933 * @use_align: whether to use alignment arguments, or %FALSE.
9934 * @row_align: The vertical alignment of the row specified by @path.
9935 * @col_align: The horizontal alignment of the column specified by @column.
9937 * Moves the alignments of @tree_view to the position specified by @column and
9938 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9939 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9940 * or @path need to be non-%NULL. @row_align determines where the row is
9941 * placed, and @col_align determines where @column is placed. Both are expected
9942 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9943 * right/bottom alignment, 0.5 means center.
9945 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9946 * tree does the minimum amount of work to scroll the cell onto the screen.
9947 * This means that the cell will be scrolled to the edge closest to its current
9948 * position. If the cell is currently visible on the screen, nothing is done.
9950 * This function only works if the model is set, and @path is a valid row on the
9951 * model. If the model changes before the @tree_view is realized, the centered
9952 * path will be modified to reflect this change.
9955 pspp_sheet_view_scroll_to_cell (PsppSheetView *tree_view,
9957 PsppSheetViewColumn *column,
9962 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
9963 g_return_if_fail (tree_view->priv->model != NULL);
9964 g_return_if_fail (row_align >= 0.0 && row_align <= 1.0);
9965 g_return_if_fail (col_align >= 0.0 && col_align <= 1.0);
9966 g_return_if_fail (path != NULL || column != NULL);
9969 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9970 gtk_tree_path_to_string (path), column?"non-null":"null", use_align, row_align, col_align);
9972 row_align = CLAMP (row_align, 0.0, 1.0);
9973 col_align = CLAMP (col_align, 0.0, 1.0);
9976 /* Note: Despite the benefits that come from having one code path for the
9977 * scrolling code, we short-circuit validate_visible_area's immplementation as
9978 * it is much slower than just going to the point.
9980 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view)) ||
9981 !gtk_widget_get_realized (GTK_WIDGET (tree_view))
9982 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9984 if (tree_view->priv->scroll_to_path)
9985 gtk_tree_row_reference_free (tree_view->priv->scroll_to_path);
9987 tree_view->priv->scroll_to_path = NULL;
9988 tree_view->priv->scroll_to_column = NULL;
9991 tree_view->priv->scroll_to_path = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
9993 tree_view->priv->scroll_to_column = column;
9994 tree_view->priv->scroll_to_use_align = use_align;
9995 tree_view->priv->scroll_to_row_align = row_align;
9996 tree_view->priv->scroll_to_col_align = col_align;
9998 install_presize_handler (tree_view);
10002 GdkRectangle cell_rect;
10003 GdkRectangle vis_rect;
10004 gint dest_x, dest_y;
10006 pspp_sheet_view_get_background_area (tree_view, path, column, &cell_rect);
10007 pspp_sheet_view_get_visible_rect (tree_view, &vis_rect);
10009 cell_rect.y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, cell_rect.y);
10011 dest_x = vis_rect.x;
10012 dest_y = vis_rect.y;
10018 dest_x = cell_rect.x - ((vis_rect.width - cell_rect.width) * col_align);
10022 if (cell_rect.x < vis_rect.x)
10023 dest_x = cell_rect.x;
10024 if (cell_rect.x + cell_rect.width > vis_rect.x + vis_rect.width)
10025 dest_x = cell_rect.x + cell_rect.width - vis_rect.width;
10033 dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * row_align);
10034 dest_y = MAX (dest_y, 0);
10038 if (cell_rect.y < vis_rect.y)
10039 dest_y = cell_rect.y;
10040 if (cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height)
10041 dest_y = cell_rect.y + cell_rect.height - vis_rect.height;
10045 pspp_sheet_view_scroll_to_point (tree_view, dest_x, dest_y);
10050 * pspp_sheet_view_row_activated:
10051 * @tree_view: A #PsppSheetView
10052 * @path: The #GtkTreePath to be activated.
10053 * @column: The #PsppSheetViewColumn to be activated.
10055 * Activates the cell determined by @path and @column.
10058 pspp_sheet_view_row_activated (PsppSheetView *tree_view,
10060 PsppSheetViewColumn *column)
10062 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10064 g_signal_emit (tree_view, tree_view_signals[ROW_ACTIVATED], 0, path, column);
10069 * pspp_sheet_view_get_reorderable:
10070 * @tree_view: a #PsppSheetView
10072 * Retrieves whether the user can reorder the tree via drag-and-drop. See
10073 * pspp_sheet_view_set_reorderable().
10075 * Return value: %TRUE if the tree can be reordered.
10078 pspp_sheet_view_get_reorderable (PsppSheetView *tree_view)
10080 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
10082 return tree_view->priv->reorderable;
10086 * pspp_sheet_view_set_reorderable:
10087 * @tree_view: A #PsppSheetView.
10088 * @reorderable: %TRUE, if the tree can be reordered.
10090 * This function is a convenience function to allow you to reorder
10091 * models that support the #GtkDragSourceIface and the
10092 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
10093 * these. If @reorderable is %TRUE, then the user can reorder the
10094 * model by dragging and dropping rows. The developer can listen to
10095 * these changes by connecting to the model's row_inserted and
10096 * row_deleted signals. The reordering is implemented by setting up
10097 * the tree view as a drag source and destination. Therefore, drag and
10098 * drop can not be used in a reorderable view for any other purpose.
10100 * This function does not give you any degree of control over the order -- any
10101 * reordering is allowed. If more control is needed, you should probably
10102 * handle drag and drop manually.
10105 pspp_sheet_view_set_reorderable (PsppSheetView *tree_view,
10106 gboolean reorderable)
10108 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10110 reorderable = reorderable != FALSE;
10112 if (tree_view->priv->reorderable == reorderable)
10117 const GtkTargetEntry row_targets[] = {
10118 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
10121 pspp_sheet_view_enable_model_drag_source (tree_view,
10124 G_N_ELEMENTS (row_targets),
10126 pspp_sheet_view_enable_model_drag_dest (tree_view,
10128 G_N_ELEMENTS (row_targets),
10133 pspp_sheet_view_unset_rows_drag_source (tree_view);
10134 pspp_sheet_view_unset_rows_drag_dest (tree_view);
10137 tree_view->priv->reorderable = reorderable;
10139 g_object_notify (G_OBJECT (tree_view), "reorderable");
10142 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
10143 is pressed, other rows will be unselected.
10145 If CLAMP_NODE is true, then the sheetview will scroll to make the row
10148 pspp_sheet_view_real_set_cursor (PsppSheetView *tree_view,
10150 gboolean clear_and_select,
10151 gboolean clamp_node,
10152 PsppSheetSelectMode mode)
10156 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10158 GtkTreePath *cursor_path;
10159 cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10160 pspp_sheet_view_queue_draw_path (tree_view, cursor_path, NULL);
10161 gtk_tree_path_free (cursor_path);
10164 gtk_tree_row_reference_free (tree_view->priv->cursor);
10165 tree_view->priv->cursor = NULL;
10167 _pspp_sheet_view_find_node (tree_view, path, &node);
10168 tree_view->priv->cursor =
10169 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
10170 tree_view->priv->model,
10173 if (tree_view->priv->row_count > 0)
10177 if (clear_and_select && !(mode & PSPP_SHEET_SELECT_MODE_TOGGLE))
10178 _pspp_sheet_selection_internal_select_node (tree_view->priv->selection,
10182 /* We have to re-find tree and node here again, somebody might have
10183 * cleared the node or the whole tree in the PsppSheetSelection::changed
10184 * callback. If the nodes differ we bail out here.
10186 _pspp_sheet_view_find_node (tree_view, path, &new_node);
10188 if (node != new_node)
10193 pspp_sheet_view_clamp_node_visible (tree_view, node);
10194 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
10198 g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0);
10202 * pspp_sheet_view_get_cursor:
10203 * @tree_view: A #PsppSheetView
10204 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
10205 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
10207 * Fills in @path and @focus_column with the current path and focus column. If
10208 * the cursor isn't currently set, then *@path will be %NULL. If no column
10209 * currently has focus, then *@focus_column will be %NULL.
10211 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
10212 * you are done with it.
10215 pspp_sheet_view_get_cursor (PsppSheetView *tree_view,
10216 GtkTreePath **path,
10217 PsppSheetViewColumn **focus_column)
10219 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10223 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
10224 *path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
10231 *focus_column = tree_view->priv->focus_column;
10236 * pspp_sheet_view_set_cursor:
10237 * @tree_view: A #PsppSheetView
10238 * @path: A #GtkTreePath
10239 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10240 * @start_editing: %TRUE if the specified cell should start being edited.
10242 * Sets the current keyboard focus to be at @path, and selects it. This is
10243 * useful when you want to focus the user's attention on a particular row. If
10244 * @focus_column is not %NULL, then focus is given to the column specified by
10245 * it. Additionally, if @focus_column is specified, and @start_editing is
10246 * %TRUE, then editing should be started in the specified cell.
10247 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10248 * in order to give keyboard focus to the widget. Please note that editing
10249 * can only happen when the widget is realized.
10251 * If @path is invalid for @model, the current cursor (if any) will be unset
10252 * and the function will return without failing.
10255 pspp_sheet_view_set_cursor (PsppSheetView *tree_view,
10257 PsppSheetViewColumn *focus_column,
10258 gboolean start_editing)
10260 pspp_sheet_view_set_cursor_on_cell (tree_view, path, focus_column,
10261 NULL, start_editing);
10265 * pspp_sheet_view_set_cursor_on_cell:
10266 * @tree_view: A #PsppSheetView
10267 * @path: A #GtkTreePath
10268 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10269 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10270 * @start_editing: %TRUE if the specified cell should start being edited.
10272 * Sets the current keyboard focus to be at @path, and selects it. This is
10273 * useful when you want to focus the user's attention on a particular row. If
10274 * @focus_column is not %NULL, then focus is given to the column specified by
10275 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10276 * contains 2 or more editable or activatable cells, then focus is given to
10277 * the cell specified by @focus_cell. Additionally, if @focus_column is
10278 * specified, and @start_editing is %TRUE, then editing should be started in
10279 * the specified cell. This function is often followed by
10280 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10281 * widget. Please note that editing can only happen when the widget is
10284 * If @path is invalid for @model, the current cursor (if any) will be unset
10285 * and the function will return without failing.
10290 pspp_sheet_view_set_cursor_on_cell (PsppSheetView *tree_view,
10292 PsppSheetViewColumn *focus_column,
10293 GtkCellRenderer *focus_cell,
10294 gboolean start_editing)
10296 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10297 g_return_if_fail (path != NULL);
10298 g_return_if_fail (focus_column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (focus_column));
10300 if (!tree_view->priv->model)
10305 g_return_if_fail (focus_column);
10306 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell));
10309 /* cancel the current editing, if it exists */
10310 if (tree_view->priv->edited_column &&
10311 tree_view->priv->edited_column->editable_widget)
10312 pspp_sheet_view_stop_editing (tree_view, TRUE);
10314 pspp_sheet_view_real_set_cursor (tree_view, path, TRUE, TRUE, 0);
10316 if (focus_column && focus_column->visible)
10319 gboolean column_in_tree = FALSE;
10321 for (list = tree_view->priv->columns; list; list = list->next)
10322 if (list->data == focus_column)
10324 column_in_tree = TRUE;
10327 g_return_if_fail (column_in_tree);
10328 tree_view->priv->focus_column = focus_column;
10330 pspp_sheet_view_column_focus_cell (focus_column, focus_cell);
10332 pspp_sheet_view_start_editing (tree_view, path);
10334 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
10335 pspp_sheet_selection_select_column (tree_view->priv->selection, focus_column);
10341 * pspp_sheet_view_get_bin_window:
10342 * @tree_view: A #PsppSheetView
10344 * Returns the window that @tree_view renders to. This is used primarily to
10345 * compare to <literal>event->window</literal> to confirm that the event on
10346 * @tree_view is on the right window.
10348 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10351 pspp_sheet_view_get_bin_window (PsppSheetView *tree_view)
10353 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
10355 return tree_view->priv->bin_window;
10359 * pspp_sheet_view_get_path_at_pos:
10360 * @tree_view: A #PsppSheetView.
10361 * @x: The x position to be identified (relative to bin_window).
10362 * @y: The y position to be identified (relative to bin_window).
10363 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10364 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10365 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10366 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10368 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10369 * (please see pspp_sheet_view_get_bin_window()).
10370 * That is, @x and @y are relative to an events coordinates. @x and @y must
10371 * come from an event on the @tree_view only where <literal>event->window ==
10372 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10373 * things like popup menus. If @path is non-%NULL, then it will be filled
10374 * with the #GtkTreePath at that point. This path should be freed with
10375 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10376 * with the column at that point. @cell_x and @cell_y return the coordinates
10377 * relative to the cell background (i.e. the @background_area passed to
10378 * gtk_cell_renderer_render()). This function is only meaningful if
10379 * @tree_view is realized. Therefore this function will always return %FALSE
10380 * if @tree_view is not realized or does not have a model.
10382 * For converting widget coordinates (eg. the ones you get from
10383 * GtkWidget::query-tooltip), please see
10384 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10386 * Return value: %TRUE if a row exists at that coordinate.
10389 pspp_sheet_view_get_path_at_pos (PsppSheetView *tree_view,
10392 GtkTreePath **path,
10393 PsppSheetViewColumn **column,
10400 g_return_val_if_fail (tree_view != NULL, FALSE);
10407 if (tree_view->priv->bin_window == NULL)
10410 if (tree_view->priv->row_count == 0)
10413 if (x > gtk_adjustment_get_upper (tree_view->priv->hadjustment))
10416 if (x < 0 || y < 0)
10419 if (column || cell_x)
10421 PsppSheetViewColumn *tmp_column;
10422 PsppSheetViewColumn *last_column = NULL;
10424 gint remaining_x = x;
10425 gboolean found = FALSE;
10428 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
10429 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
10431 list = (rtl ? list->prev : list->next))
10433 tmp_column = list->data;
10435 if (tmp_column->visible == FALSE)
10438 last_column = tmp_column;
10439 if (remaining_x <= tmp_column->width)
10444 *column = tmp_column;
10447 *cell_x = remaining_x;
10451 remaining_x -= tmp_column->width;
10454 /* If found is FALSE and there is a last_column, then it the remainder
10455 * space is in that area
10462 *column = last_column;
10465 *cell_x = last_column->width + remaining_x;
10474 y_offset = pspp_sheet_view_find_offset (tree_view,
10475 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, y),
10482 *cell_y = y_offset;
10485 *path = _pspp_sheet_view_find_path (tree_view, node);
10490 /* Computes 'cell_area' from 'background_area', which must be the background
10491 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10492 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10493 the cell area as passed to _pspp_sheet_view_column_cell_render().
10495 'column' is required to properly adjust 'cell_area->x' and
10496 'cell_area->width'. It may be set to NULL if these values are not of
10497 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10500 pspp_sheet_view_adjust_cell_area (PsppSheetView *tree_view,
10501 PsppSheetViewColumn *column,
10502 const GdkRectangle *background_area,
10503 gboolean subtract_focus_rect,
10504 GdkRectangle *cell_area)
10506 gint vertical_separator;
10507 gint horizontal_separator;
10509 *cell_area = *background_area;
10511 gtk_widget_style_get (GTK_WIDGET (tree_view),
10512 "vertical-separator", &vertical_separator,
10513 "horizontal-separator", &horizontal_separator,
10515 cell_area->x += horizontal_separator / 2;
10516 cell_area->y += vertical_separator / 2;
10517 cell_area->width -= horizontal_separator;
10518 cell_area->height -= vertical_separator;
10520 if (subtract_focus_rect)
10522 int focus_line_width;
10524 gtk_widget_style_get (GTK_WIDGET (tree_view),
10525 "focus-line-width", &focus_line_width,
10527 cell_area->x += focus_line_width;
10528 cell_area->y += focus_line_width;
10529 cell_area->width -= 2 * focus_line_width;
10530 cell_area->height -= 2 * focus_line_width;
10533 if (tree_view->priv->grid_lines != PSPP_SHEET_VIEW_GRID_LINES_NONE)
10535 gint grid_line_width;
10536 gtk_widget_style_get (GTK_WIDGET (tree_view),
10537 "grid-line-width", &grid_line_width,
10540 if ((tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10541 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10544 PsppSheetViewColumn *first_column, *last_column;
10547 /* Find the last visible column. */
10548 last_column = NULL;
10549 for (list = g_list_last (tree_view->priv->columns);
10553 PsppSheetViewColumn *c = list->data;
10561 /* Find the first visible column. */
10562 first_column = NULL;
10563 for (list = g_list_first (tree_view->priv->columns);
10567 PsppSheetViewColumn *c = list->data;
10575 if (column == first_column)
10577 cell_area->width -= grid_line_width / 2;
10579 else if (column == last_column)
10581 cell_area->x += grid_line_width / 2;
10582 cell_area->width -= grid_line_width / 2;
10586 cell_area->x += grid_line_width / 2;
10587 cell_area->width -= grid_line_width;
10591 if (tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10592 || tree_view->priv->grid_lines == PSPP_SHEET_VIEW_GRID_LINES_BOTH)
10594 cell_area->y += grid_line_width / 2;
10595 cell_area->height -= grid_line_width;
10599 if (column == NULL)
10602 cell_area->width = 0;
10607 * pspp_sheet_view_get_cell_area:
10608 * @tree_view: a #PsppSheetView
10609 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10610 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10611 * @rect: rectangle to fill with cell rect
10613 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10614 * row specified by @path and the column specified by @column. If @path is
10615 * %NULL, or points to a path not currently displayed, the @y and @height fields
10616 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10617 * fields will be filled with 0. The sum of all cell rects does not cover the
10618 * entire tree; there are extra pixels in between rows, for example. The
10619 * returned rectangle is equivalent to the @cell_area passed to
10620 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10624 pspp_sheet_view_get_cell_area (PsppSheetView *tree_view,
10626 PsppSheetViewColumn *column,
10627 GdkRectangle *rect)
10629 GdkRectangle background_area;
10631 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10632 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10633 g_return_if_fail (rect != NULL);
10634 g_return_if_fail (!column || column->tree_view == (GtkWidget *) tree_view);
10635 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
10637 pspp_sheet_view_get_background_area (tree_view, path, column,
10639 pspp_sheet_view_adjust_cell_area (tree_view, column, &background_area,
10644 * pspp_sheet_view_get_background_area:
10645 * @tree_view: a #PsppSheetView
10646 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10647 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10648 * @rect: rectangle to fill with cell background rect
10650 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10651 * row specified by @path and the column specified by @column. If @path is
10652 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10653 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10654 * fields will be filled with 0. The returned rectangle is equivalent to the
10655 * @background_area passed to gtk_cell_renderer_render(). These background
10656 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10657 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10658 * itself, excluding surrounding borders.
10662 pspp_sheet_view_get_background_area (PsppSheetView *tree_view,
10664 PsppSheetViewColumn *column,
10665 GdkRectangle *rect)
10669 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10670 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
10671 g_return_if_fail (rect != NULL);
10680 /* Get vertical coords */
10682 _pspp_sheet_view_find_node (tree_view, path, &node);
10686 rect->y = BACKGROUND_FIRST_PIXEL (tree_view, node);
10688 rect->height = ROW_HEIGHT (tree_view);
10695 pspp_sheet_view_get_background_xrange (tree_view, column, &rect->x, &x2);
10696 rect->width = x2 - rect->x;
10701 * pspp_sheet_view_get_visible_rect:
10702 * @tree_view: a #PsppSheetView
10703 * @visible_rect: rectangle to fill
10705 * Fills @visible_rect with the currently-visible region of the
10706 * buffer, in tree coordinates. Convert to bin_window coordinates with
10707 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10708 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10709 * scrollable area of the tree.
10712 pspp_sheet_view_get_visible_rect (PsppSheetView *tree_view,
10713 GdkRectangle *visible_rect)
10717 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10719 widget = GTK_WIDGET (tree_view);
10723 GtkAllocation allocation;
10724 gtk_widget_get_allocation (widget, &allocation);
10725 visible_rect->x = gtk_adjustment_get_value (tree_view->priv->hadjustment);
10726 visible_rect->y = gtk_adjustment_get_value (tree_view->priv->vadjustment);
10727 visible_rect->width = allocation.width;
10728 visible_rect->height = allocation.height - TREE_VIEW_HEADER_HEIGHT (tree_view);
10733 * pspp_sheet_view_widget_to_tree_coords:
10734 * @tree_view: a #PsppSheetView
10735 * @wx: X coordinate relative to bin_window
10736 * @wy: Y coordinate relative to bin_window
10737 * @tx: return location for tree X coordinate
10738 * @ty: return location for tree Y coordinate
10740 * Converts bin_window coordinates to coordinates for the
10741 * tree (the full scrollable area of the tree).
10743 * Deprecated: 2.12: Due to historial reasons the name of this function is
10744 * incorrect. For converting coordinates relative to the widget to
10745 * bin_window coordinates, please see
10746 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10750 pspp_sheet_view_widget_to_tree_coords (PsppSheetView *tree_view,
10756 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10759 *tx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10761 *ty = wy + tree_view->priv->dy;
10765 * pspp_sheet_view_tree_to_widget_coords:
10766 * @tree_view: a #PsppSheetView
10767 * @tx: tree X coordinate
10768 * @ty: tree Y coordinate
10769 * @wx: return location for X coordinate relative to bin_window
10770 * @wy: return location for Y coordinate relative to bin_window
10772 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10773 * to bin_window coordinates.
10775 * Deprecated: 2.12: Due to historial reasons the name of this function is
10776 * incorrect. For converting bin_window coordinates to coordinates relative
10777 * to bin_window, please see
10778 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10782 pspp_sheet_view_tree_to_widget_coords (PsppSheetView *tree_view,
10788 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10791 *wx = tx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10793 *wy = ty - tree_view->priv->dy;
10798 * pspp_sheet_view_convert_widget_to_tree_coords:
10799 * @tree_view: a #PsppSheetView
10800 * @wx: X coordinate relative to the widget
10801 * @wy: Y coordinate relative to the widget
10802 * @tx: return location for tree X coordinate
10803 * @ty: return location for tree Y coordinate
10805 * Converts widget coordinates to coordinates for the
10806 * tree (the full scrollable area of the tree).
10811 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView *tree_view,
10819 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10821 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
10824 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view,
10830 * pspp_sheet_view_convert_tree_to_widget_coords:
10831 * @tree_view: a #PsppSheetView
10832 * @tx: X coordinate relative to the tree
10833 * @ty: Y coordinate relative to the tree
10834 * @wx: return location for widget X coordinate
10835 * @wy: return location for widget Y coordinate
10837 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10838 * to widget coordinates.
10843 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView *tree_view,
10851 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10853 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view,
10856 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
10862 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10863 * @tree_view: a #PsppSheetView
10864 * @wx: X coordinate relative to the widget
10865 * @wy: Y coordinate relative to the widget
10866 * @bx: return location for bin_window X coordinate
10867 * @by: return location for bin_window Y coordinate
10869 * Converts widget coordinates to coordinates for the bin_window
10870 * (see pspp_sheet_view_get_bin_window()).
10875 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView *tree_view,
10881 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10884 *bx = wx + gtk_adjustment_get_value (tree_view->priv->hadjustment);
10886 *by = wy - TREE_VIEW_HEADER_HEIGHT (tree_view);
10890 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10891 * @tree_view: a #PsppSheetView
10892 * @bx: bin_window X coordinate
10893 * @by: bin_window Y coordinate
10894 * @wx: return location for widget X coordinate
10895 * @wy: return location for widget Y coordinate
10897 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10898 * to widget relative coordinates.
10903 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView *tree_view,
10909 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10912 *wx = bx - gtk_adjustment_get_value (tree_view->priv->hadjustment);
10914 *wy = by + TREE_VIEW_HEADER_HEIGHT (tree_view);
10918 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10919 * @tree_view: a #PsppSheetView
10920 * @tx: tree X coordinate
10921 * @ty: tree Y coordinate
10922 * @bx: return location for X coordinate relative to bin_window
10923 * @by: return location for Y coordinate relative to bin_window
10925 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10926 * to bin_window coordinates.
10931 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView *tree_view,
10937 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10942 *by = ty - tree_view->priv->dy;
10946 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10947 * @tree_view: a #PsppSheetView
10948 * @bx: X coordinate relative to bin_window
10949 * @by: Y coordinate relative to bin_window
10950 * @tx: return location for tree X coordinate
10951 * @ty: return location for tree Y coordinate
10953 * Converts bin_window coordinates to coordinates for the
10954 * tree (the full scrollable area of the tree).
10959 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView *tree_view,
10965 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
10970 *ty = by + tree_view->priv->dy;
10976 * pspp_sheet_view_get_visible_range:
10977 * @tree_view: A #PsppSheetView
10978 * @start_path: (allow-none): Return location for start of region, or %NULL.
10979 * @end_path: (allow-none): Return location for end of region, or %NULL.
10981 * Sets @start_path and @end_path to be the first and last visible path.
10982 * Note that there may be invisible paths in between.
10984 * The paths should be freed with gtk_tree_path_free() after use.
10986 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
10991 pspp_sheet_view_get_visible_range (PsppSheetView *tree_view,
10992 GtkTreePath **start_path,
10993 GtkTreePath **end_path)
10998 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11000 if (!tree_view->priv->row_count)
11007 pspp_sheet_view_find_offset (tree_view,
11008 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, 0),
11011 *start_path = _pspp_sheet_view_find_path (tree_view, node);
11020 if (tree_view->priv->height < gtk_adjustment_get_page_size (tree_view->priv->vadjustment))
11021 y = tree_view->priv->height - 1;
11023 y = TREE_WINDOW_Y_TO_RBTREE_Y (tree_view, gtk_adjustment_get_page_size (tree_view->priv->vadjustment)) - 1;
11025 pspp_sheet_view_find_offset (tree_view, y, &node);
11027 *end_path = _pspp_sheet_view_find_path (tree_view, node);
11036 unset_reorderable (PsppSheetView *tree_view)
11038 if (tree_view->priv->reorderable)
11040 tree_view->priv->reorderable = FALSE;
11041 g_object_notify (G_OBJECT (tree_view), "reorderable");
11046 * pspp_sheet_view_enable_model_drag_source:
11047 * @tree_view: a #PsppSheetView
11048 * @start_button_mask: Mask of allowed buttons to start drag
11049 * @targets: the table of targets that the drag will support
11050 * @n_targets: the number of items in @targets
11051 * @actions: the bitmask of possible actions for a drag from this
11054 * Turns @tree_view into a drag source for automatic DND. Calling this
11055 * method sets #PsppSheetView:reorderable to %FALSE.
11058 pspp_sheet_view_enable_model_drag_source (PsppSheetView *tree_view,
11059 GdkModifierType start_button_mask,
11060 const GtkTargetEntry *targets,
11062 GdkDragAction actions)
11064 TreeViewDragInfo *di;
11066 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11068 gtk_drag_source_set (GTK_WIDGET (tree_view),
11074 di = ensure_info (tree_view);
11076 di->start_button_mask = start_button_mask;
11077 di->source_actions = actions;
11078 di->source_set = TRUE;
11080 unset_reorderable (tree_view);
11084 * pspp_sheet_view_enable_model_drag_dest:
11085 * @tree_view: a #PsppSheetView
11086 * @targets: the table of targets that the drag will support
11087 * @n_targets: the number of items in @targets
11088 * @actions: the bitmask of possible actions for a drag from this
11091 * Turns @tree_view into a drop destination for automatic DND. Calling
11092 * this method sets #PsppSheetView:reorderable to %FALSE.
11095 pspp_sheet_view_enable_model_drag_dest (PsppSheetView *tree_view,
11096 const GtkTargetEntry *targets,
11098 GdkDragAction actions)
11100 TreeViewDragInfo *di;
11102 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11104 gtk_drag_dest_set (GTK_WIDGET (tree_view),
11110 di = ensure_info (tree_view);
11111 di->dest_set = TRUE;
11113 unset_reorderable (tree_view);
11117 * pspp_sheet_view_unset_rows_drag_source:
11118 * @tree_view: a #PsppSheetView
11120 * Undoes the effect of
11121 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
11122 * #PsppSheetView:reorderable to %FALSE.
11125 pspp_sheet_view_unset_rows_drag_source (PsppSheetView *tree_view)
11127 TreeViewDragInfo *di;
11129 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11131 di = get_info (tree_view);
11135 if (di->source_set)
11137 gtk_drag_source_unset (GTK_WIDGET (tree_view));
11138 di->source_set = FALSE;
11141 if (!di->dest_set && !di->source_set)
11142 remove_info (tree_view);
11145 unset_reorderable (tree_view);
11149 * pspp_sheet_view_unset_rows_drag_dest:
11150 * @tree_view: a #PsppSheetView
11152 * Undoes the effect of
11153 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
11154 * #PsppSheetView:reorderable to %FALSE.
11157 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView *tree_view)
11159 TreeViewDragInfo *di;
11161 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11163 di = get_info (tree_view);
11169 gtk_drag_dest_unset (GTK_WIDGET (tree_view));
11170 di->dest_set = FALSE;
11173 if (!di->dest_set && !di->source_set)
11174 remove_info (tree_view);
11177 unset_reorderable (tree_view);
11181 * pspp_sheet_view_set_drag_dest_row:
11182 * @tree_view: a #PsppSheetView
11183 * @path: (allow-none): The path of the row to highlight, or %NULL.
11184 * @pos: Specifies whether to drop before, after or into the row
11186 * Sets the row that is highlighted for feedback.
11189 pspp_sheet_view_set_drag_dest_row (PsppSheetView *tree_view,
11191 PsppSheetViewDropPosition pos)
11193 GtkTreePath *current_dest;
11195 /* Note; this function is exported to allow a custom DND
11196 * implementation, so it can't touch TreeViewDragInfo
11199 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11201 current_dest = NULL;
11203 if (tree_view->priv->drag_dest_row)
11205 current_dest = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11206 gtk_tree_row_reference_free (tree_view->priv->drag_dest_row);
11209 /* special case a drop on an empty model */
11210 tree_view->priv->empty_view_drop = 0;
11212 if (pos == PSPP_SHEET_VIEW_DROP_BEFORE && path
11213 && gtk_tree_path_get_depth (path) == 1
11214 && gtk_tree_path_get_indices (path)[0] == 0)
11218 n_children = gtk_tree_model_iter_n_children (tree_view->priv->model,
11222 tree_view->priv->empty_view_drop = 1;
11225 tree_view->priv->drag_dest_pos = pos;
11229 tree_view->priv->drag_dest_row =
11230 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), tree_view->priv->model, path);
11231 pspp_sheet_view_queue_draw_path (tree_view, path, NULL);
11234 tree_view->priv->drag_dest_row = NULL;
11238 int node, new_node;
11240 _pspp_sheet_view_find_node (tree_view, current_dest, &node);
11241 _pspp_sheet_view_queue_draw_node (tree_view, node, NULL);
11245 new_node = pspp_sheet_view_node_next (tree_view, node);
11247 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11249 new_node = pspp_sheet_view_node_prev (tree_view, node);
11251 _pspp_sheet_view_queue_draw_node (tree_view, new_node, NULL);
11253 gtk_tree_path_free (current_dest);
11258 * pspp_sheet_view_get_drag_dest_row:
11259 * @tree_view: a #PsppSheetView
11260 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11261 * @pos: (allow-none): Return location for the drop position, or %NULL
11263 * Gets information about the row that is highlighted for feedback.
11266 pspp_sheet_view_get_drag_dest_row (PsppSheetView *tree_view,
11267 GtkTreePath **path,
11268 PsppSheetViewDropPosition *pos)
11270 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11274 if (tree_view->priv->drag_dest_row)
11275 *path = gtk_tree_row_reference_get_path (tree_view->priv->drag_dest_row);
11278 if (tree_view->priv->empty_view_drop)
11279 *path = gtk_tree_path_new_from_indices (0, -1);
11286 *pos = tree_view->priv->drag_dest_pos;
11290 * pspp_sheet_view_get_dest_row_at_pos:
11291 * @tree_view: a #PsppSheetView
11292 * @drag_x: the position to determine the destination row for
11293 * @drag_y: the position to determine the destination row for
11294 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11295 * @pos: (allow-none): Return location for the drop position, or %NULL
11297 * Determines the destination row for a given position. @drag_x and
11298 * @drag_y are expected to be in widget coordinates. This function is only
11299 * meaningful if @tree_view is realized. Therefore this function will always
11300 * return %FALSE if @tree_view is not realized or does not have a model.
11302 * Return value: whether there is a row at the given position, %TRUE if this
11303 * is indeed the case.
11306 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView *tree_view,
11309 GtkTreePath **path,
11310 PsppSheetViewDropPosition *pos)
11314 gdouble offset_into_row;
11317 PsppSheetViewColumn *column = NULL;
11318 GtkTreePath *tmp_path = NULL;
11320 /* Note; this function is exported to allow a custom DND
11321 * implementation, so it can't touch TreeViewDragInfo
11324 g_return_val_if_fail (tree_view != NULL, FALSE);
11325 g_return_val_if_fail (drag_x >= 0, FALSE);
11326 g_return_val_if_fail (drag_y >= 0, FALSE);
11331 if (tree_view->priv->bin_window == NULL)
11334 if (tree_view->priv->row_count == 0)
11337 /* If in the top third of a row, we drop before that row; if
11338 * in the bottom third, drop after that row; if in the middle,
11339 * and the row has children, drop into the row.
11341 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, drag_x, drag_y,
11344 if (!pspp_sheet_view_get_path_at_pos (tree_view,
11353 pspp_sheet_view_get_background_area (tree_view, tmp_path, column,
11356 offset_into_row = cell_y;
11361 gtk_tree_path_free (tmp_path);
11365 third = cell.height / 3.0;
11369 if (offset_into_row < third)
11371 *pos = PSPP_SHEET_VIEW_DROP_BEFORE;
11373 else if (offset_into_row < (cell.height / 2.0))
11375 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE;
11377 else if (offset_into_row < third * 2.0)
11379 *pos = PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER;
11383 *pos = PSPP_SHEET_VIEW_DROP_AFTER;
11391 #if GTK3_TRANSITION
11392 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11394 * pspp_sheet_view_create_row_drag_icon:
11395 * @tree_view: a #PsppSheetView
11396 * @path: a #GtkTreePath in @tree_view
11398 * Creates a #GdkPixmap representation of the row at @path.
11399 * This image is used for a drag icon.
11401 * Return value: a newly-allocated pixmap of the drag icon.
11404 pspp_sheet_view_create_row_drag_icon (PsppSheetView *tree_view,
11411 GdkRectangle background_area;
11412 GdkRectangle expose_area;
11414 /* start drawing inside the black outline */
11416 GdkDrawable *drawable;
11417 gint bin_window_width;
11420 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11421 g_return_val_if_fail (path != NULL, NULL);
11423 widget = GTK_WIDGET (tree_view);
11425 if (!gtk_widget_get_realized (widget))
11428 _pspp_sheet_view_find_node (tree_view,
11435 if (!gtk_tree_model_get_iter (tree_view->priv->model,
11442 background_area.y = y;
11443 background_area.height = ROW_HEIGHT (tree_view);
11445 bin_window_width = gdk_window_get_width (tree_view->priv->bin_window);
11447 drawable = gdk_pixmap_new (tree_view->priv->bin_window,
11448 bin_window_width + 2,
11449 background_area.height + 2,
11454 expose_area.width = bin_window_width + 2;
11455 expose_area.height = background_area.height + 2;
11457 #if GTK3_TRANSITION
11458 gdk_draw_rectangle (drawable,
11459 widget->style->base_gc [gtk_widget_get_state (widget)],
11462 bin_window_width + 2,
11463 background_area.height + 2);
11466 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL;
11468 for (list = (rtl ? g_list_last (tree_view->priv->columns) : g_list_first (tree_view->priv->columns));
11470 list = (rtl ? list->prev : list->next))
11472 PsppSheetViewColumn *column = list->data;
11473 GdkRectangle cell_area;
11474 gint vertical_separator;
11476 if (!column->visible)
11479 pspp_sheet_view_column_cell_set_cell_data (column, tree_view->priv->model, &iter);
11481 background_area.x = cell_offset;
11482 background_area.width = column->width;
11484 gtk_widget_style_get (widget,
11485 "vertical-separator", &vertical_separator,
11488 cell_area = background_area;
11490 cell_area.y += vertical_separator / 2;
11491 cell_area.height -= vertical_separator;
11493 if (pspp_sheet_view_column_cell_is_visible (column))
11494 _pspp_sheet_view_column_cell_render (column,
11500 cell_offset += column->width;
11503 #if GTK3_TRANSITION
11504 gdk_draw_rectangle (drawable,
11505 widget->style->black_gc,
11508 bin_window_width + 1,
11509 background_area.height + 1);
11517 * pspp_sheet_view_set_destroy_count_func:
11518 * @tree_view: A #PsppSheetView
11519 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11520 * @data: (allow-none): User data to be passed to @func, or %NULL
11521 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11523 * This function should almost never be used. It is meant for private use by
11524 * ATK for determining the number of visible children that are removed when a row is deleted.
11527 pspp_sheet_view_set_destroy_count_func (PsppSheetView *tree_view,
11528 PsppSheetDestroyCountFunc func,
11530 GDestroyNotify destroy)
11532 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11534 if (tree_view->priv->destroy_count_destroy)
11535 tree_view->priv->destroy_count_destroy (tree_view->priv->destroy_count_data);
11537 tree_view->priv->destroy_count_func = func;
11538 tree_view->priv->destroy_count_data = data;
11539 tree_view->priv->destroy_count_destroy = destroy;
11544 * Interactive search
11548 * pspp_sheet_view_set_enable_search:
11549 * @tree_view: A #PsppSheetView
11550 * @enable_search: %TRUE, if the user can search interactively
11552 * If @enable_search is set, then the user can type in text to search through
11553 * the tree interactively (this is sometimes called "typeahead find").
11555 * Note that even if this is %FALSE, the user can still initiate a search
11556 * using the "start-interactive-search" key binding.
11559 pspp_sheet_view_set_enable_search (PsppSheetView *tree_view,
11560 gboolean enable_search)
11562 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11564 enable_search = !!enable_search;
11566 if (tree_view->priv->enable_search != enable_search)
11568 tree_view->priv->enable_search = enable_search;
11569 g_object_notify (G_OBJECT (tree_view), "enable-search");
11574 * pspp_sheet_view_get_enable_search:
11575 * @tree_view: A #PsppSheetView
11577 * Returns whether or not the tree allows to start interactive searching
11578 * by typing in text.
11580 * Return value: whether or not to let the user search interactively
11583 pspp_sheet_view_get_enable_search (PsppSheetView *tree_view)
11585 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
11587 return tree_view->priv->enable_search;
11592 * pspp_sheet_view_get_search_column:
11593 * @tree_view: A #PsppSheetView
11595 * Gets the column searched on by the interactive search code.
11597 * Return value: the column the interactive search code searches in.
11600 pspp_sheet_view_get_search_column (PsppSheetView *tree_view)
11602 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), -1);
11604 return (tree_view->priv->search_column);
11608 * pspp_sheet_view_set_search_column:
11609 * @tree_view: A #PsppSheetView
11610 * @column: the column of the model to search in, or -1 to disable searching
11612 * Sets @column as the column where the interactive search code should
11613 * search in for the current model.
11615 * If the search column is set, users can use the "start-interactive-search"
11616 * key binding to bring up search popup. The enable-search property controls
11617 * whether simply typing text will also start an interactive search.
11619 * Note that @column refers to a column of the current model. The search
11620 * column is reset to -1 when the model is changed.
11623 pspp_sheet_view_set_search_column (PsppSheetView *tree_view,
11626 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11627 g_return_if_fail (column >= -1);
11629 if (tree_view->priv->search_column == column)
11632 tree_view->priv->search_column = column;
11633 g_object_notify (G_OBJECT (tree_view), "search-column");
11637 * pspp_sheet_view_get_search_equal_func:
11638 * @tree_view: A #PsppSheetView
11640 * Returns the compare function currently in use.
11642 * Return value: the currently used compare function for the search code.
11645 PsppSheetViewSearchEqualFunc
11646 pspp_sheet_view_get_search_equal_func (PsppSheetView *tree_view)
11648 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11650 return tree_view->priv->search_equal_func;
11654 * pspp_sheet_view_set_search_equal_func:
11655 * @tree_view: A #PsppSheetView
11656 * @search_equal_func: the compare function to use during the search
11657 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11658 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11660 * Sets the compare function for the interactive search capabilities; note
11661 * that somewhat like strcmp() returning 0 for equality
11662 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11665 pspp_sheet_view_set_search_equal_func (PsppSheetView *tree_view,
11666 PsppSheetViewSearchEqualFunc search_equal_func,
11667 gpointer search_user_data,
11668 GDestroyNotify search_destroy)
11670 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11671 g_return_if_fail (search_equal_func != NULL);
11673 if (tree_view->priv->search_destroy)
11674 tree_view->priv->search_destroy (tree_view->priv->search_user_data);
11676 tree_view->priv->search_equal_func = search_equal_func;
11677 tree_view->priv->search_user_data = search_user_data;
11678 tree_view->priv->search_destroy = search_destroy;
11679 if (tree_view->priv->search_equal_func == NULL)
11680 tree_view->priv->search_equal_func = pspp_sheet_view_search_equal_func;
11684 * pspp_sheet_view_get_search_entry:
11685 * @tree_view: A #PsppSheetView
11687 * Returns the #GtkEntry which is currently in use as interactive search
11688 * entry for @tree_view. In case the built-in entry is being used, %NULL
11689 * will be returned.
11691 * Return value: the entry currently in use as search entry.
11696 pspp_sheet_view_get_search_entry (PsppSheetView *tree_view)
11698 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11700 if (tree_view->priv->search_custom_entry_set)
11701 return GTK_ENTRY (tree_view->priv->search_entry);
11707 * pspp_sheet_view_set_search_entry:
11708 * @tree_view: A #PsppSheetView
11709 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11711 * Sets the entry which the interactive search code will use for this
11712 * @tree_view. This is useful when you want to provide a search entry
11713 * in our interface at all time at a fixed position. Passing %NULL for
11714 * @entry will make the interactive search code use the built-in popup
11720 pspp_sheet_view_set_search_entry (PsppSheetView *tree_view,
11723 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11724 g_return_if_fail (entry == NULL || GTK_IS_ENTRY (entry));
11726 if (tree_view->priv->search_custom_entry_set)
11728 if (tree_view->priv->search_entry_changed_id)
11730 g_signal_handler_disconnect (tree_view->priv->search_entry,
11731 tree_view->priv->search_entry_changed_id);
11732 tree_view->priv->search_entry_changed_id = 0;
11734 g_signal_handlers_disconnect_by_func (tree_view->priv->search_entry,
11735 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11738 g_object_unref (tree_view->priv->search_entry);
11740 else if (tree_view->priv->search_window)
11742 gtk_widget_destroy (tree_view->priv->search_window);
11744 tree_view->priv->search_window = NULL;
11749 tree_view->priv->search_entry = g_object_ref (entry);
11750 tree_view->priv->search_custom_entry_set = TRUE;
11752 if (tree_view->priv->search_entry_changed_id == 0)
11754 tree_view->priv->search_entry_changed_id =
11755 g_signal_connect (tree_view->priv->search_entry, "changed",
11756 G_CALLBACK (pspp_sheet_view_search_init),
11760 g_signal_connect (tree_view->priv->search_entry, "key-press-event",
11761 G_CALLBACK (pspp_sheet_view_search_key_press_event),
11764 pspp_sheet_view_search_init (tree_view->priv->search_entry, tree_view);
11768 tree_view->priv->search_entry = NULL;
11769 tree_view->priv->search_custom_entry_set = FALSE;
11774 * pspp_sheet_view_set_search_position_func:
11775 * @tree_view: A #PsppSheetView
11776 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11777 * to use the default search position function
11778 * @data: (allow-none): user data to pass to @func, or %NULL
11779 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11781 * Sets the function to use when positioning the search dialog.
11786 pspp_sheet_view_set_search_position_func (PsppSheetView *tree_view,
11787 PsppSheetViewSearchPositionFunc func,
11788 gpointer user_data,
11789 GDestroyNotify destroy)
11791 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
11793 if (tree_view->priv->search_position_destroy)
11794 tree_view->priv->search_position_destroy (tree_view->priv->search_position_user_data);
11796 tree_view->priv->search_position_func = func;
11797 tree_view->priv->search_position_user_data = user_data;
11798 tree_view->priv->search_position_destroy = destroy;
11799 if (tree_view->priv->search_position_func == NULL)
11800 tree_view->priv->search_position_func = pspp_sheet_view_search_position_func;
11804 * pspp_sheet_view_get_search_position_func:
11805 * @tree_view: A #PsppSheetView
11807 * Returns the positioning function currently in use.
11809 * Return value: the currently used function for positioning the search dialog.
11813 PsppSheetViewSearchPositionFunc
11814 pspp_sheet_view_get_search_position_func (PsppSheetView *tree_view)
11816 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), NULL);
11818 return tree_view->priv->search_position_func;
11823 pspp_sheet_view_search_dialog_hide (GtkWidget *search_dialog,
11824 PsppSheetView *tree_view)
11826 if (tree_view->priv->disable_popdown)
11829 if (tree_view->priv->search_entry_changed_id)
11831 g_signal_handler_disconnect (tree_view->priv->search_entry,
11832 tree_view->priv->search_entry_changed_id);
11833 tree_view->priv->search_entry_changed_id = 0;
11835 if (tree_view->priv->typeselect_flush_timeout)
11837 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11838 tree_view->priv->typeselect_flush_timeout = 0;
11841 if (gtk_widget_get_visible (search_dialog))
11843 /* send focus-in event */
11844 send_focus_change (GTK_WIDGET (tree_view->priv->search_entry), FALSE);
11845 gtk_widget_hide (search_dialog);
11846 gtk_entry_set_text (GTK_ENTRY (tree_view->priv->search_entry), "");
11847 send_focus_change (GTK_WIDGET (tree_view), TRUE);
11852 pspp_sheet_view_search_position_func (PsppSheetView *tree_view,
11853 GtkWidget *search_dialog,
11854 gpointer user_data)
11857 gint tree_x, tree_y;
11858 gint tree_width, tree_height;
11859 GdkWindow *tree_window = gtk_widget_get_window (GTK_WIDGET (tree_view));
11860 GdkScreen *screen = gdk_window_get_screen (tree_window);
11861 GtkRequisition requisition;
11863 GdkRectangle monitor;
11865 monitor_num = gdk_screen_get_monitor_at_window (screen, tree_window);
11866 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
11868 gtk_widget_realize (search_dialog);
11870 gdk_window_get_origin (tree_window, &tree_x, &tree_y);
11871 tree_width = gdk_window_get_width (tree_window);
11872 tree_height = gdk_window_get_height (tree_window);
11874 gtk_widget_size_request (search_dialog, &requisition);
11876 if (tree_x + tree_width > gdk_screen_get_width (screen))
11877 x = gdk_screen_get_width (screen) - requisition.width;
11878 else if (tree_x + tree_width - requisition.width < 0)
11881 x = tree_x + tree_width - requisition.width;
11883 if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
11884 y = gdk_screen_get_height (screen) - requisition.height;
11885 else if (tree_y + tree_height < 0) /* isn't really possible ... */
11888 y = tree_y + tree_height;
11890 gtk_window_move (GTK_WINDOW (search_dialog), x, y);
11894 pspp_sheet_view_search_disable_popdown (GtkEntry *entry,
11898 PsppSheetView *tree_view = (PsppSheetView *)data;
11900 tree_view->priv->disable_popdown = 1;
11901 g_signal_connect (menu, "hide",
11902 G_CALLBACK (pspp_sheet_view_search_enable_popdown), data);
11905 #if GTK3_TRANSITION
11906 /* Because we're visible but offscreen, we just set a flag in the preedit
11910 pspp_sheet_view_search_preedit_changed (GtkIMContext *im_context,
11911 PsppSheetView *tree_view)
11913 tree_view->priv->imcontext_changed = 1;
11914 if (tree_view->priv->typeselect_flush_timeout)
11916 g_source_remove (tree_view->priv->typeselect_flush_timeout);
11917 tree_view->priv->typeselect_flush_timeout =
11918 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
11919 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
11927 pspp_sheet_view_search_activate (GtkEntry *entry,
11928 PsppSheetView *tree_view)
11933 pspp_sheet_view_search_dialog_hide (tree_view->priv->search_window,
11936 /* If we have a row selected and it's the cursor row, we activate
11938 if (gtk_tree_row_reference_valid (tree_view->priv->cursor))
11940 path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
11942 _pspp_sheet_view_find_node (tree_view, path, &node);
11944 if (node >= 0 && pspp_sheet_view_node_is_selected (tree_view, node))
11945 pspp_sheet_view_row_activated (tree_view, path, tree_view->priv->focus_column);
11947 gtk_tree_path_free (path);
11952 pspp_sheet_view_real_search_enable_popdown (gpointer data)
11954 PsppSheetView *tree_view = (PsppSheetView *)data;
11956 tree_view->priv->disable_popdown = 0;
11962 pspp_sheet_view_search_enable_popdown (GtkWidget *widget,
11965 gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, pspp_sheet_view_real_search_enable_popdown, g_object_ref (data), g_object_unref);
11969 pspp_sheet_view_search_delete_event (GtkWidget *widget,
11970 GdkEventAny *event,
11971 PsppSheetView *tree_view)
11973 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11975 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11981 pspp_sheet_view_search_button_press_event (GtkWidget *widget,
11982 GdkEventButton *event,
11983 PsppSheetView *tree_view)
11985 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
11987 pspp_sheet_view_search_dialog_hide (widget, tree_view);
11989 if (event->window == tree_view->priv->bin_window)
11990 pspp_sheet_view_button_press (GTK_WIDGET (tree_view), event);
11996 pspp_sheet_view_search_scroll_event (GtkWidget *widget,
11997 GdkEventScroll *event,
11998 PsppSheetView *tree_view)
12000 gboolean retval = FALSE;
12002 if (event->direction == GDK_SCROLL_UP)
12004 pspp_sheet_view_search_move (widget, tree_view, TRUE);
12007 else if (event->direction == GDK_SCROLL_DOWN)
12009 pspp_sheet_view_search_move (widget, tree_view, FALSE);
12013 /* renew the flush timeout */
12014 if (retval && tree_view->priv->typeselect_flush_timeout
12015 && !tree_view->priv->search_custom_entry_set)
12017 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12018 tree_view->priv->typeselect_flush_timeout =
12019 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12020 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12028 pspp_sheet_view_search_key_press_event (GtkWidget *widget,
12029 GdkEventKey *event,
12030 PsppSheetView *tree_view)
12032 gboolean retval = FALSE;
12034 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
12035 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12037 /* close window and cancel the search */
12038 if (!tree_view->priv->search_custom_entry_set
12039 && (event->keyval == GDK_Escape ||
12040 event->keyval == GDK_Tab ||
12041 event->keyval == GDK_KP_Tab ||
12042 event->keyval == GDK_ISO_Left_Tab))
12044 pspp_sheet_view_search_dialog_hide (widget, tree_view);
12048 /* select previous matching iter */
12049 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
12051 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
12052 gtk_widget_error_bell (widget);
12057 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK))
12058 && (event->keyval == GDK_g || event->keyval == GDK_G))
12060 if (!pspp_sheet_view_search_move (widget, tree_view, TRUE))
12061 gtk_widget_error_bell (widget);
12066 /* select next matching iter */
12067 if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
12069 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
12070 gtk_widget_error_bell (widget);
12075 if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK)
12076 && (event->keyval == GDK_g || event->keyval == GDK_G))
12078 if (!pspp_sheet_view_search_move (widget, tree_view, FALSE))
12079 gtk_widget_error_bell (widget);
12084 /* renew the flush timeout */
12085 if (retval && tree_view->priv->typeselect_flush_timeout
12086 && !tree_view->priv->search_custom_entry_set)
12088 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12089 tree_view->priv->typeselect_flush_timeout =
12090 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12091 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12098 /* this function returns FALSE if there is a search string but
12099 * nothing was found, and TRUE otherwise.
12102 pspp_sheet_view_search_move (GtkWidget *window,
12103 PsppSheetView *tree_view,
12111 GtkTreeModel *model;
12112 PsppSheetSelection *selection;
12114 text = gtk_entry_get_text (GTK_ENTRY (tree_view->priv->search_entry));
12116 g_return_val_if_fail (text != NULL, FALSE);
12118 len = strlen (text);
12120 if (up && tree_view->priv->selected_iter == 1)
12121 return strlen (text) < 1;
12123 len = strlen (text);
12128 model = pspp_sheet_view_get_model (tree_view);
12129 selection = pspp_sheet_view_get_selection (tree_view);
12132 pspp_sheet_selection_unselect_all (selection);
12133 if (!gtk_tree_model_get_iter_first (model, &iter))
12136 ret = pspp_sheet_view_search_iter (model, selection, &iter, text,
12137 &count, up?((tree_view->priv->selected_iter) - 1):((tree_view->priv->selected_iter + 1)));
12142 tree_view->priv->selected_iter += up?(-1):(1);
12147 /* return to old iter */
12149 gtk_tree_model_get_iter_first (model, &iter);
12150 pspp_sheet_view_search_iter (model, selection,
12152 &count, tree_view->priv->selected_iter);
12158 pspp_sheet_view_search_equal_func (GtkTreeModel *model,
12162 gpointer search_data)
12164 gboolean retval = TRUE;
12166 gchar *normalized_string;
12167 gchar *normalized_key;
12168 gchar *case_normalized_string = NULL;
12169 gchar *case_normalized_key = NULL;
12170 GValue value = {0,};
12171 GValue transformed = {0,};
12173 gtk_tree_model_get_value (model, iter, column, &value);
12175 g_value_init (&transformed, G_TYPE_STRING);
12177 if (!g_value_transform (&value, &transformed))
12179 g_value_unset (&value);
12183 g_value_unset (&value);
12185 str = g_value_get_string (&transformed);
12188 g_value_unset (&transformed);
12192 normalized_string = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
12193 normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
12195 if (normalized_string && normalized_key)
12197 case_normalized_string = g_utf8_casefold (normalized_string, -1);
12198 case_normalized_key = g_utf8_casefold (normalized_key, -1);
12200 if (strncmp (case_normalized_key, case_normalized_string, strlen (case_normalized_key)) == 0)
12204 g_value_unset (&transformed);
12205 g_free (normalized_key);
12206 g_free (normalized_string);
12207 g_free (case_normalized_key);
12208 g_free (case_normalized_string);
12214 pspp_sheet_view_search_iter (GtkTreeModel *model,
12215 PsppSheetSelection *selection,
12224 PsppSheetView *tree_view = pspp_sheet_selection_get_tree_view (selection);
12226 path = gtk_tree_model_get_path (model, iter);
12227 _pspp_sheet_view_find_node (tree_view, path, &node);
12231 gboolean done = FALSE;
12233 if (! tree_view->priv->search_equal_func (model, tree_view->priv->search_column, text, iter, tree_view->priv->search_user_data))
12238 pspp_sheet_view_scroll_to_cell (tree_view, path, NULL,
12240 pspp_sheet_selection_select_iter (selection, iter);
12241 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12244 gtk_tree_path_free (path);
12253 node = pspp_sheet_view_node_next (tree_view, node);
12259 has_next = gtk_tree_model_iter_next (model, iter);
12262 gtk_tree_path_next (path);
12265 TREE_VIEW_INTERNAL_ASSERT (has_next, FALSE);
12270 gtk_tree_path_free (path);
12272 /* we've run out of tree, done with this func */
12284 pspp_sheet_view_search_init (GtkWidget *entry,
12285 PsppSheetView *tree_view)
12291 GtkTreeModel *model;
12292 PsppSheetSelection *selection;
12294 g_return_if_fail (GTK_IS_ENTRY (entry));
12295 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12297 text = gtk_entry_get_text (GTK_ENTRY (entry));
12299 model = pspp_sheet_view_get_model (tree_view);
12300 selection = pspp_sheet_view_get_selection (tree_view);
12303 pspp_sheet_selection_unselect_all (selection);
12304 if (tree_view->priv->typeselect_flush_timeout
12305 && !tree_view->priv->search_custom_entry_set)
12307 g_source_remove (tree_view->priv->typeselect_flush_timeout);
12308 tree_view->priv->typeselect_flush_timeout =
12309 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT,
12310 (GSourceFunc) pspp_sheet_view_search_entry_flush_timeout,
12317 if (!gtk_tree_model_get_iter_first (model, &iter))
12320 ret = pspp_sheet_view_search_iter (model, selection,
12325 tree_view->priv->selected_iter = 1;
12329 pspp_sheet_view_remove_widget (GtkCellEditable *cell_editable,
12330 PsppSheetView *tree_view)
12332 if (tree_view->priv->edited_column == NULL)
12335 _pspp_sheet_view_column_stop_editing (tree_view->priv->edited_column);
12336 tree_view->priv->edited_column = NULL;
12338 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable)))
12339 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12341 g_signal_handlers_disconnect_by_func (cell_editable,
12342 pspp_sheet_view_remove_widget,
12344 g_signal_handlers_disconnect_by_func (cell_editable,
12345 pspp_sheet_view_editable_button_press_event,
12347 g_signal_handlers_disconnect_by_func (cell_editable,
12348 pspp_sheet_view_editable_clicked,
12351 gtk_container_remove (GTK_CONTAINER (tree_view),
12352 GTK_WIDGET (cell_editable));
12354 /* FIXME should only redraw a single node */
12355 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12359 pspp_sheet_view_start_editing (PsppSheetView *tree_view,
12360 GtkTreePath *cursor_path)
12363 GdkRectangle background_area;
12364 GdkRectangle cell_area;
12365 GtkCellEditable *editable_widget = NULL;
12366 gchar *path_string;
12367 guint flags = 0; /* can be 0, as the flags are primarily for rendering */
12368 gint retval = FALSE;
12371 g_assert (tree_view->priv->focus_column);
12373 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view)))
12376 _pspp_sheet_view_find_node (tree_view, cursor_path, &cursor_node);
12377 if (cursor_node < 0)
12380 path_string = gtk_tree_path_to_string (cursor_path);
12381 gtk_tree_model_get_iter (tree_view->priv->model, &iter, cursor_path);
12383 pspp_sheet_view_column_cell_set_cell_data (tree_view->priv->focus_column,
12384 tree_view->priv->model,
12386 pspp_sheet_view_get_background_area (tree_view,
12388 tree_view->priv->focus_column,
12390 pspp_sheet_view_get_cell_area (tree_view,
12392 tree_view->priv->focus_column,
12395 if (_pspp_sheet_view_column_cell_event (tree_view->priv->focus_column,
12404 if (editable_widget != NULL)
12408 GtkCellRenderer *cell;
12411 cell = _pspp_sheet_view_column_get_edited_cell (tree_view->priv->focus_column);
12413 _pspp_sheet_view_column_get_neighbor_sizes (tree_view->priv->focus_column, cell, &left, &right);
12416 area.width -= right + left;
12418 pspp_sheet_view_real_start_editing (tree_view,
12419 tree_view->priv->focus_column,
12428 g_free (path_string);
12433 pspp_sheet_view_editable_button_press_event (GtkWidget *widget,
12434 GdkEventButton *event,
12435 PsppSheetView *sheet_view)
12439 node = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
12440 "pspp-sheet-view-node"));
12441 return pspp_sheet_view_row_head_clicked (sheet_view,
12443 sheet_view->priv->edited_column,
12448 pspp_sheet_view_editable_clicked (GtkButton *button,
12449 PsppSheetView *sheet_view)
12451 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button), NULL,
12456 is_all_selected (GtkWidget *widget)
12458 GtkEntryBuffer *buffer;
12459 gint start_pos, end_pos;
12461 if (!GTK_IS_ENTRY (widget))
12464 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12465 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget),
12466 &start_pos, &end_pos)
12468 && end_pos == gtk_entry_buffer_get_length (buffer));
12472 is_at_left (GtkWidget *widget)
12474 return (GTK_IS_ENTRY (widget)
12475 && gtk_editable_get_position (GTK_EDITABLE (widget)) == 0);
12479 is_at_right (GtkWidget *widget)
12481 GtkEntryBuffer *buffer;
12484 if (!GTK_IS_ENTRY (widget))
12487 buffer = gtk_entry_get_buffer (GTK_ENTRY (widget));
12488 length = gtk_entry_buffer_get_length (buffer);
12489 return gtk_editable_get_position (GTK_EDITABLE (widget)) == length;
12493 pspp_sheet_view_event (GtkWidget *widget,
12494 GdkEventKey *event,
12495 PsppSheetView *tree_view)
12497 PsppSheetViewColumn *column;
12504 /* Intercept only key press events.
12505 It would make sense to use "key-press-event" instead of "event", but
12506 GtkEntry attaches its own signal handler to "key-press-event" that runs
12507 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12509 if (event->type != GDK_KEY_PRESS)
12512 keyval = event->keyval;
12514 switch (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK))
12517 switch (event->keyval)
12519 case GDK_Left: case GDK_KP_Left:
12520 case GDK_Home: case GDK_KP_Home:
12521 if (!is_all_selected (widget) && !is_at_left (widget))
12525 case GDK_Right: case GDK_KP_Right:
12526 case GDK_End: case GDK_KP_End:
12527 if (!is_all_selected (widget) && !is_at_right (widget))
12531 case GDK_Up: case GDK_KP_Up:
12532 case GDK_Down: case GDK_KP_Down:
12535 case GDK_Page_Up: case GDK_KP_Page_Up:
12536 case GDK_Page_Down: case GDK_KP_Page_Down:
12547 case GDK_Tab: case GDK_KP_Tab:
12548 case GDK_ISO_Left_Tab:
12557 case GDK_SHIFT_MASK:
12558 switch (event->keyval)
12561 case GDK_ISO_Left_Tab:
12570 case GDK_CONTROL_MASK:
12571 switch (event->keyval)
12573 case GDK_Left: case GDK_KP_Left:
12574 if (!is_all_selected (widget) && !is_at_left (widget))
12578 case GDK_Right: case GDK_KP_Right:
12579 if (!is_all_selected (widget) && !is_at_right (widget))
12583 case GDK_Up: case GDK_KP_Up:
12584 case GDK_Down: case GDK_KP_Down:
12596 row = tree_view->priv->edited_row;
12597 column = tree_view->priv->edited_column;
12598 path = gtk_tree_path_new_from_indices (row, -1);
12600 pspp_sheet_view_stop_editing (tree_view, cancel);
12601 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
12603 pspp_sheet_view_set_cursor (tree_view, path, column, FALSE);
12604 gtk_tree_path_free (path);
12606 handled = gtk_binding_set_activate (edit_bindings, keyval, event->state,
12607 G_OBJECT (tree_view));
12609 g_signal_stop_emission_by_name (widget, "event");
12611 pspp_sheet_view_get_cursor (tree_view, &path, NULL);
12612 pspp_sheet_view_start_editing (tree_view, path);
12613 gtk_tree_path_free (path);
12619 pspp_sheet_view_override_cell_keypresses (GtkWidget *widget,
12622 PsppSheetView *sheet_view = data;
12624 g_signal_connect (widget, "event",
12625 G_CALLBACK (pspp_sheet_view_event),
12628 if (GTK_IS_CONTAINER (widget))
12629 gtk_container_foreach (GTK_CONTAINER (widget),
12630 pspp_sheet_view_override_cell_keypresses,
12635 pspp_sheet_view_real_start_editing (PsppSheetView *tree_view,
12636 PsppSheetViewColumn *column,
12638 GtkCellEditable *cell_editable,
12639 GdkRectangle *cell_area,
12643 PsppSheetSelectionMode mode = pspp_sheet_selection_get_mode (tree_view->priv->selection);
12644 gint pre_val = gtk_adjustment_get_value (tree_view->priv->vadjustment);
12645 GtkRequisition requisition;
12648 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
12650 tree_view->priv->edited_column = column;
12651 _pspp_sheet_view_column_start_editing (column, GTK_CELL_EDITABLE (cell_editable));
12653 row = gtk_tree_path_get_indices (path)[0];
12654 tree_view->priv->edited_row = row;
12655 pspp_sheet_view_real_set_cursor (tree_view, path, FALSE, TRUE, 0);
12656 cell_area->y += pre_val - (int)gtk_adjustment_get_value (tree_view->priv->vadjustment);
12658 pspp_sheet_selection_unselect_all_columns (tree_view->priv->selection);
12659 pspp_sheet_selection_select_column (tree_view->priv->selection, column);
12660 tree_view->priv->anchor_column = column;
12662 gtk_widget_size_request (GTK_WIDGET (cell_editable), &requisition);
12664 PSPP_SHEET_VIEW_SET_FLAG (tree_view, PSPP_SHEET_VIEW_DRAW_KEYFOCUS);
12666 if (requisition.height < cell_area->height)
12668 gint diff = cell_area->height - requisition.height;
12669 pspp_sheet_view_put (tree_view,
12670 GTK_WIDGET (cell_editable),
12671 cell_area->x, cell_area->y + diff/2,
12672 cell_area->width, requisition.height);
12676 pspp_sheet_view_put (tree_view,
12677 GTK_WIDGET (cell_editable),
12678 cell_area->x, cell_area->y,
12679 cell_area->width, cell_area->height);
12682 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable),
12683 (GdkEvent *)event);
12685 gtk_widget_grab_focus (GTK_WIDGET (cell_editable));
12686 g_signal_connect (cell_editable, "remove-widget",
12687 G_CALLBACK (pspp_sheet_view_remove_widget), tree_view);
12688 if (mode == PSPP_SHEET_SELECTION_RECTANGLE && column->row_head &&
12689 GTK_IS_BUTTON (cell_editable))
12691 g_signal_connect (cell_editable, "button-press-event",
12692 G_CALLBACK (pspp_sheet_view_editable_button_press_event),
12694 g_object_set_data (G_OBJECT (cell_editable), "pspp-sheet-view-node",
12695 GINT_TO_POINTER (row));
12696 g_signal_connect (cell_editable, "clicked",
12697 G_CALLBACK (pspp_sheet_view_editable_clicked),
12701 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable),
12706 pspp_sheet_view_stop_editing (PsppSheetView *tree_view,
12707 gboolean cancel_editing)
12709 PsppSheetViewColumn *column;
12710 GtkCellRenderer *cell;
12712 if (tree_view->priv->edited_column == NULL)
12716 * This is very evil. We need to do this, because
12717 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12718 * later on. If pspp_sheet_view_row_changed notices
12719 * tree_view->priv->edited_column != NULL, it'll call
12720 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12722 * Please read that again if you intend to modify anything here.
12725 column = tree_view->priv->edited_column;
12726 tree_view->priv->edited_column = NULL;
12728 cell = _pspp_sheet_view_column_get_edited_cell (column);
12729 gtk_cell_renderer_stop_editing (cell, cancel_editing);
12731 if (!cancel_editing)
12732 gtk_cell_editable_editing_done (column->editable_widget);
12734 tree_view->priv->edited_column = column;
12736 gtk_cell_editable_remove_widget (column->editable_widget);
12741 * pspp_sheet_view_set_hover_selection:
12742 * @tree_view: a #PsppSheetView
12743 * @hover: %TRUE to enable hover selection mode
12745 * Enables of disables the hover selection mode of @tree_view.
12746 * Hover selection makes the selected row follow the pointer.
12747 * Currently, this works only for the selection modes
12748 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12753 pspp_sheet_view_set_hover_selection (PsppSheetView *tree_view,
12756 hover = hover != FALSE;
12758 if (hover != tree_view->priv->hover_selection)
12760 tree_view->priv->hover_selection = hover;
12762 g_object_notify (G_OBJECT (tree_view), "hover-selection");
12767 * pspp_sheet_view_get_hover_selection:
12768 * @tree_view: a #PsppSheetView
12770 * Returns whether hover selection mode is turned on for @tree_view.
12772 * Return value: %TRUE if @tree_view is in hover selection mode
12777 pspp_sheet_view_get_hover_selection (PsppSheetView *tree_view)
12779 return tree_view->priv->hover_selection;
12783 * pspp_sheet_view_set_rubber_banding:
12784 * @tree_view: a #PsppSheetView
12785 * @enable: %TRUE to enable rubber banding
12787 * Enables or disables rubber banding in @tree_view. If the selection mode is
12788 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12789 * banding will allow the user to select multiple rows by dragging the mouse.
12794 pspp_sheet_view_set_rubber_banding (PsppSheetView *tree_view,
12797 enable = enable != FALSE;
12799 if (enable != tree_view->priv->rubber_banding_enable)
12801 tree_view->priv->rubber_banding_enable = enable;
12803 g_object_notify (G_OBJECT (tree_view), "rubber-banding");
12808 * pspp_sheet_view_get_rubber_banding:
12809 * @tree_view: a #PsppSheetView
12811 * Returns whether rubber banding is turned on for @tree_view. If the
12812 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12813 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12814 * select multiple rows by dragging the mouse.
12816 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12821 pspp_sheet_view_get_rubber_banding (PsppSheetView *tree_view)
12823 return tree_view->priv->rubber_banding_enable;
12827 * pspp_sheet_view_is_rubber_banding_active:
12828 * @tree_view: a #PsppSheetView
12830 * Returns whether a rubber banding operation is currently being done
12833 * Return value: %TRUE if a rubber banding operation is currently being
12834 * done in @tree_view.
12839 pspp_sheet_view_is_rubber_banding_active (PsppSheetView *tree_view)
12841 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
12843 if (tree_view->priv->rubber_banding_enable
12844 && tree_view->priv->rubber_band_status == RUBBER_BAND_ACTIVE)
12851 pspp_sheet_view_grab_notify (GtkWidget *widget,
12852 gboolean was_grabbed)
12854 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12856 tree_view->priv->in_grab = !was_grabbed;
12860 tree_view->priv->pressed_button = -1;
12862 if (tree_view->priv->rubber_band_status)
12863 pspp_sheet_view_stop_rubber_band (tree_view);
12868 pspp_sheet_view_state_changed (GtkWidget *widget,
12869 GtkStateType previous_state)
12871 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
12873 if (gtk_widget_get_realized (widget))
12875 GtkStyle *style = gtk_widget_get_style (widget);
12876 gdk_window_set_background (tree_view->priv->bin_window, &style->base[gtk_widget_get_state (widget)]);
12879 gtk_widget_queue_draw (widget);
12883 * pspp_sheet_view_get_grid_lines:
12884 * @tree_view: a #PsppSheetView
12886 * Returns which grid lines are enabled in @tree_view.
12888 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12893 PsppSheetViewGridLines
12894 pspp_sheet_view_get_grid_lines (PsppSheetView *tree_view)
12896 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12898 return tree_view->priv->grid_lines;
12902 * pspp_sheet_view_set_grid_lines:
12903 * @tree_view: a #PsppSheetView
12904 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12907 * Sets which grid lines to draw in @tree_view.
12912 pspp_sheet_view_set_grid_lines (PsppSheetView *tree_view,
12913 PsppSheetViewGridLines grid_lines)
12915 PsppSheetViewPrivate *priv;
12916 PsppSheetViewGridLines old_grid_lines;
12918 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12920 priv = tree_view->priv;
12922 old_grid_lines = priv->grid_lines;
12923 priv->grid_lines = grid_lines;
12925 if (old_grid_lines != grid_lines)
12927 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12929 g_object_notify (G_OBJECT (tree_view), "enable-grid-lines");
12934 * pspp_sheet_view_get_special_cells:
12935 * @tree_view: a #PsppSheetView
12937 * Returns which grid lines are enabled in @tree_view.
12939 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12940 * the sheet view contain special cells.
12942 PsppSheetViewSpecialCells
12943 pspp_sheet_view_get_special_cells (PsppSheetView *tree_view)
12945 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
12947 return tree_view->priv->special_cells;
12951 * pspp_sheet_view_set_special_cells:
12952 * @tree_view: a #PsppSheetView
12953 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12954 * the sheet view contain special cells.
12956 * Sets whether rows in the sheet view contain special cells, controlling the
12957 * rendering of row selections.
12960 pspp_sheet_view_set_special_cells (PsppSheetView *tree_view,
12961 PsppSheetViewSpecialCells special_cells)
12963 PsppSheetViewPrivate *priv;
12965 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
12967 priv = tree_view->priv;
12969 if (priv->special_cells != special_cells)
12971 priv->special_cells = special_cells;
12972 gtk_widget_queue_draw (GTK_WIDGET (tree_view));
12973 g_object_notify (G_OBJECT (tree_view), "special-cells");
12978 pspp_sheet_view_get_fixed_height (const PsppSheetView *tree_view)
12980 /* XXX (re)calculate fixed_height if necessary */
12981 return tree_view->priv->fixed_height;
12985 pspp_sheet_view_set_fixed_height (PsppSheetView *tree_view,
12988 g_return_if_fail (fixed_height > 0);
12990 if (tree_view->priv->fixed_height != fixed_height)
12992 tree_view->priv->fixed_height = fixed_height;
12993 g_object_notify (G_OBJECT (tree_view), "fixed-height");
12995 if (!tree_view->priv->fixed_height_set)
12997 tree_view->priv->fixed_height_set = TRUE;
12998 g_object_notify (G_OBJECT (tree_view), "fixed-height-set");
13003 * pspp_sheet_view_set_tooltip_row:
13004 * @tree_view: a #PsppSheetView
13005 * @tooltip: a #GtkTooltip
13006 * @path: a #GtkTreePath
13008 * Sets the tip area of @tooltip to be the area covered by the row at @path.
13009 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13010 * See also gtk_tooltip_set_tip_area().
13015 pspp_sheet_view_set_tooltip_row (PsppSheetView *tree_view,
13016 GtkTooltip *tooltip,
13019 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13020 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
13022 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, NULL, NULL);
13026 * pspp_sheet_view_set_tooltip_cell:
13027 * @tree_view: a #PsppSheetView
13028 * @tooltip: a #GtkTooltip
13029 * @path: (allow-none): a #GtkTreePath or %NULL
13030 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
13031 * @cell: (allow-none): a #GtkCellRenderer or %NULL
13033 * Sets the tip area of @tooltip to the area @path, @column and @cell have
13034 * in common. For example if @path is %NULL and @column is set, the tip
13035 * area will be set to the full area covered by @column. See also
13036 * gtk_tooltip_set_tip_area().
13038 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13043 pspp_sheet_view_set_tooltip_cell (PsppSheetView *tree_view,
13044 GtkTooltip *tooltip,
13046 PsppSheetViewColumn *column,
13047 GtkCellRenderer *cell)
13051 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13052 g_return_if_fail (GTK_IS_TOOLTIP (tooltip));
13053 g_return_if_fail (column == NULL || PSPP_IS_SHEET_VIEW_COLUMN (column));
13054 g_return_if_fail (cell == NULL || GTK_IS_CELL_RENDERER (cell));
13056 /* Determine x values. */
13057 if (column && cell)
13062 pspp_sheet_view_get_cell_area (tree_view, path, column, &tmp);
13063 pspp_sheet_view_column_cell_get_position (column, cell, &start, &width);
13065 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13068 rect.width = width;
13074 pspp_sheet_view_get_background_area (tree_view, NULL, column, &tmp);
13075 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13078 rect.width = tmp.width;
13082 GtkAllocation allocation;
13083 gtk_widget_get_allocation (GTK_WIDGET (tree_view), &allocation);
13085 rect.width = allocation.width;
13088 /* Determine y values. */
13093 pspp_sheet_view_get_background_area (tree_view, path, NULL, &tmp);
13094 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view,
13097 rect.height = tmp.height;
13102 rect.height = gtk_adjustment_get_page_size (tree_view->priv->vadjustment);
13105 gtk_tooltip_set_tip_area (tooltip, &rect);
13109 * pspp_sheet_view_get_tooltip_context:
13110 * @tree_view: a #PsppSheetView
13111 * @x: the x coordinate (relative to widget coordinates)
13112 * @y: the y coordinate (relative to widget coordinates)
13113 * @keyboard_tip: whether this is a keyboard tooltip or not
13114 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
13115 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
13116 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
13118 * This function is supposed to be used in a #GtkWidget::query-tooltip
13119 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
13120 * which are received in the signal handler, should be passed to this
13121 * function without modification.
13123 * The return value indicates whether there is a tree view row at the given
13124 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
13125 * tooltips the row returned will be the cursor row. When %TRUE, then any of
13126 * @model, @path and @iter which have been provided will be set to point to
13127 * that row and the corresponding model. @x and @y will always be converted
13128 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
13130 * Return value: whether or not the given tooltip context points to a row.
13135 pspp_sheet_view_get_tooltip_context (PsppSheetView *tree_view,
13138 gboolean keyboard_tip,
13139 GtkTreeModel **model,
13140 GtkTreePath **path,
13143 GtkTreePath *tmppath = NULL;
13145 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), FALSE);
13146 g_return_val_if_fail (x != NULL, FALSE);
13147 g_return_val_if_fail (y != NULL, FALSE);
13151 pspp_sheet_view_get_cursor (tree_view, &tmppath, NULL);
13158 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view, *x, *y,
13161 if (!pspp_sheet_view_get_path_at_pos (tree_view, *x, *y,
13162 &tmppath, NULL, NULL, NULL))
13167 *model = pspp_sheet_view_get_model (tree_view);
13170 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view),
13176 gtk_tree_path_free (tmppath);
13182 pspp_sheet_view_set_tooltip_query_cb (GtkWidget *widget,
13185 gboolean keyboard_tip,
13186 GtkTooltip *tooltip,
13189 GValue value = { 0, };
13190 GValue transformed = { 0, };
13193 GtkTreeModel *model;
13194 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
13196 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget),
13199 &model, &path, &iter))
13202 gtk_tree_model_get_value (model, &iter,
13203 tree_view->priv->tooltip_column, &value);
13205 g_value_init (&transformed, G_TYPE_STRING);
13207 if (!g_value_transform (&value, &transformed))
13209 g_value_unset (&value);
13210 gtk_tree_path_free (path);
13215 g_value_unset (&value);
13217 if (!g_value_get_string (&transformed))
13219 g_value_unset (&transformed);
13220 gtk_tree_path_free (path);
13225 gtk_tooltip_set_markup (tooltip, g_value_get_string (&transformed));
13226 pspp_sheet_view_set_tooltip_row (tree_view, tooltip, path);
13228 gtk_tree_path_free (path);
13229 g_value_unset (&transformed);
13235 * pspp_sheet_view_set_tooltip_column:
13236 * @tree_view: a #PsppSheetView
13237 * @column: an integer, which is a valid column number for @tree_view's model
13239 * If you only plan to have simple (text-only) tooltips on full rows, you
13240 * can use this function to have #PsppSheetView handle these automatically
13241 * for you. @column should be set to the column in @tree_view's model
13242 * containing the tooltip texts, or -1 to disable this feature.
13244 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
13245 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
13247 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
13248 * so &, <, etc have to be escaped in the text.
13253 pspp_sheet_view_set_tooltip_column (PsppSheetView *tree_view,
13256 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
13258 if (column == tree_view->priv->tooltip_column)
13263 g_signal_handlers_disconnect_by_func (tree_view,
13264 pspp_sheet_view_set_tooltip_query_cb,
13266 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), FALSE);
13270 if (tree_view->priv->tooltip_column == -1)
13272 g_signal_connect (tree_view, "query-tooltip",
13273 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb), NULL);
13274 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE);
13278 tree_view->priv->tooltip_column = column;
13279 g_object_notify (G_OBJECT (tree_view), "tooltip-column");
13283 * pspp_sheet_view_get_tooltip_column:
13284 * @tree_view: a #PsppSheetView
13286 * Returns the column of @tree_view's model which is being used for
13287 * displaying tooltips on @tree_view's rows.
13289 * Return value: the index of the tooltip column that is currently being
13290 * used, or -1 if this is disabled.
13295 pspp_sheet_view_get_tooltip_column (PsppSheetView *tree_view)
13297 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view), 0);
13299 return tree_view->priv->tooltip_column;
13303 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
13304 GValue *return_accu,
13305 const GValue *handler_return,
13308 gboolean continue_emission;
13309 gboolean signal_handled;
13311 signal_handled = g_value_get_boolean (handler_return);
13312 g_value_set_boolean (return_accu, signal_handled);
13313 continue_emission = !signal_handled;
13315 return continue_emission;
13320 pspp_sheet_view_grid_lines_get_type (void)
13322 static GType etype = 0;
13323 if (G_UNLIKELY(etype == 0)) {
13324 static const GEnumValue values[] = {
13325 { PSPP_SHEET_VIEW_GRID_LINES_NONE, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13326 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13327 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13328 { PSPP_SHEET_VIEW_GRID_LINES_BOTH, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13331 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values);
13337 pspp_sheet_view_special_cells_get_type (void)
13339 static GType etype = 0;
13340 if (G_UNLIKELY(etype == 0)) {
13341 static const GEnumValue values[] = {
13342 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13343 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13344 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13347 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values);