1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012, 2013 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Library General Public
22 * License as published by the Free Software Foundation; either
23 * version 2 of the License, or (at your option) any later version.
25 * This library is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Library General Public License for more details.
30 * You should have received a copy of the GNU Library General Public
31 * License along with this library; if not, write to the
32 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
33 * Boston, MA 02111-1307, USA.
38 #include "ui/gui/pspp-sheet-private.h"
42 #include <gdk/gdkkeysyms.h>
43 #include <gdk/gdkkeysyms-compat.h>
46 #include "ui/gui/psppire-marshal.h"
47 #include "ui/gui/pspp-sheet-selection.h"
49 #define P_(STRING) STRING
50 #define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
51 #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
53 /* Many keyboard shortcuts for Mac are the same as for X
54 * except they use Command key instead of Control (e.g. Cut,
55 * Copy, Paste). This symbol is for those simple cases. */
56 #ifndef GDK_WINDOWING_QUARTZ
57 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_CONTROL_MASK
59 #define GTK_DEFAULT_ACCEL_MOD_MASK GDK_META_MASK
62 #define PSPP_SHEET_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5)
63 #define PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC (PSPP_SHEET_VIEW_PRIORITY_VALIDATE + 2)
64 #define PSPP_SHEET_VIEW_TIME_MS_PER_IDLE 30
65 #define SCROLL_EDGE_SIZE 15
66 #define EXPANDER_EXTRA_PADDING 4
67 #define PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT 5000
69 /* The "background" areas of all rows/cells add up to cover the entire tree.
70 * The background includes all inter-row and inter-cell spacing.
71 * The "cell" areas are the cell_area passed in to gtk_cell_renderer_render(),
72 * i.e. just the cells, no spacing.
75 #define BACKGROUND_HEIGHT(tree_view) (tree_view->priv->fixed_height)
76 #define CELL_HEIGHT(tree_view, separator) ((BACKGROUND_HEIGHT (tree_view)) - (separator))
78 /* Translate from bin_window coordinates to rbtree (tree coordinates) and
81 #define TREE_WINDOW_Y_TO_RBTREE_Y(tree_view,y) ((y) + tree_view->priv->dy)
82 #define RBTREE_Y_TO_TREE_WINDOW_Y(tree_view,y) ((y) - tree_view->priv->dy)
84 /* This is in bin_window coordinates */
85 #define BACKGROUND_FIRST_PIXEL(tree_view,node) (RBTREE_Y_TO_TREE_WINDOW_Y (tree_view, pspp_sheet_view_node_find_offset (tree_view, (node))))
86 #define CELL_FIRST_PIXEL(tree_view,node,separator) (BACKGROUND_FIRST_PIXEL (tree_view,node) + separator/2)
88 #define ROW_HEIGHT(tree_view) \
89 ((tree_view->priv->fixed_height > 0) ? (tree_view->priv->fixed_height) : (tree_view)->priv->expander_size)
92 typedef struct _PsppSheetViewChild PsppSheetViewChild
;
93 struct _PsppSheetViewChild
103 typedef struct _TreeViewDragInfo TreeViewDragInfo
;
104 struct _TreeViewDragInfo
106 GdkModifierType start_button_mask
;
107 GtkTargetList
*_unused_source_target_list
;
108 GdkDragAction source_actions
;
110 GtkTargetList
*_unused_dest_target_list
;
112 guint source_set
: 1;
128 START_INTERACTIVE_SEARCH
,
140 PROP_HEADERS_VISIBLE
,
141 PROP_HEADERS_CLICKABLE
,
146 PROP_HOVER_SELECTION
,
148 PROP_ENABLE_GRID_LINES
,
152 PROP_FIXED_HEIGHT_SET
156 static void pspp_sheet_view_finalize (GObject
*object
);
157 static void pspp_sheet_view_set_property (GObject
*object
,
161 static void pspp_sheet_view_get_property (GObject
*object
,
166 static void pspp_sheet_view_dispose (GObject
*object
);
168 /* gtkwidget signals */
169 static void pspp_sheet_view_realize (GtkWidget
*widget
);
170 static void pspp_sheet_view_unrealize (GtkWidget
*widget
);
171 static void pspp_sheet_view_map (GtkWidget
*widget
);
172 static void pspp_sheet_view_size_request (GtkWidget
*widget
,
173 GtkRequisition
*requisition
);
174 static void pspp_sheet_view_size_allocate (GtkWidget
*widget
,
175 GtkAllocation
*allocation
);
176 static gboolean
pspp_sheet_view_draw (GtkWidget
*widget
,
178 static gboolean
pspp_sheet_view_key_press (GtkWidget
*widget
,
180 static gboolean
pspp_sheet_view_key_release (GtkWidget
*widget
,
182 static gboolean
pspp_sheet_view_motion (GtkWidget
*widget
,
183 GdkEventMotion
*event
);
184 static gboolean
pspp_sheet_view_enter_notify (GtkWidget
*widget
,
185 GdkEventCrossing
*event
);
186 static gboolean
pspp_sheet_view_leave_notify (GtkWidget
*widget
,
187 GdkEventCrossing
*event
);
188 static gboolean
pspp_sheet_view_button_press (GtkWidget
*widget
,
189 GdkEventButton
*event
);
190 static gboolean
pspp_sheet_view_button_release (GtkWidget
*widget
,
191 GdkEventButton
*event
);
192 static gboolean
pspp_sheet_view_grab_broken (GtkWidget
*widget
,
193 GdkEventGrabBroken
*event
);
195 static void pspp_sheet_view_set_focus_child (GtkContainer
*container
,
197 static gint
pspp_sheet_view_focus_out (GtkWidget
*widget
,
198 GdkEventFocus
*event
);
199 static gint
pspp_sheet_view_focus (GtkWidget
*widget
,
200 GtkDirectionType direction
);
201 static void pspp_sheet_view_grab_focus (GtkWidget
*widget
);
202 static void pspp_sheet_view_style_set (GtkWidget
*widget
,
203 GtkStyle
*previous_style
);
204 static void pspp_sheet_view_grab_notify (GtkWidget
*widget
,
205 gboolean was_grabbed
);
206 static void pspp_sheet_view_state_changed (GtkWidget
*widget
,
207 GtkStateType previous_state
);
209 /* container signals */
210 static void pspp_sheet_view_remove (GtkContainer
*container
,
212 static void pspp_sheet_view_forall (GtkContainer
*container
,
213 gboolean include_internals
,
214 GtkCallback callback
,
215 gpointer callback_data
);
217 /* Source side drag signals */
218 static void pspp_sheet_view_drag_begin (GtkWidget
*widget
,
219 GdkDragContext
*context
);
220 static void pspp_sheet_view_drag_end (GtkWidget
*widget
,
221 GdkDragContext
*context
);
222 static void pspp_sheet_view_drag_data_get (GtkWidget
*widget
,
223 GdkDragContext
*context
,
224 GtkSelectionData
*selection_data
,
227 static void pspp_sheet_view_drag_data_delete (GtkWidget
*widget
,
228 GdkDragContext
*context
);
230 /* Target side drag signals */
231 static void pspp_sheet_view_drag_leave (GtkWidget
*widget
,
232 GdkDragContext
*context
,
234 static gboolean
pspp_sheet_view_drag_motion (GtkWidget
*widget
,
235 GdkDragContext
*context
,
239 static gboolean
pspp_sheet_view_drag_drop (GtkWidget
*widget
,
240 GdkDragContext
*context
,
244 static void pspp_sheet_view_drag_data_received (GtkWidget
*widget
,
245 GdkDragContext
*context
,
248 GtkSelectionData
*selection_data
,
252 /* tree_model signals */
253 static void pspp_sheet_view_set_adjustments (PsppSheetView
*tree_view
,
255 GtkAdjustment
*vadj
);
256 static gboolean
pspp_sheet_view_real_move_cursor (PsppSheetView
*tree_view
,
257 GtkMovementStep step
,
259 static gboolean
pspp_sheet_view_real_select_all (PsppSheetView
*tree_view
);
260 static gboolean
pspp_sheet_view_real_unselect_all (PsppSheetView
*tree_view
);
261 static gboolean
pspp_sheet_view_real_select_cursor_row (PsppSheetView
*tree_view
,
262 gboolean start_editing
,
263 PsppSheetSelectMode mode
);
264 static gboolean
pspp_sheet_view_real_toggle_cursor_row (PsppSheetView
*tree_view
);
265 static void pspp_sheet_view_row_changed (GtkTreeModel
*model
,
269 static void pspp_sheet_view_row_inserted (GtkTreeModel
*model
,
273 static void pspp_sheet_view_row_deleted (GtkTreeModel
*model
,
276 static void pspp_sheet_view_rows_reordered (GtkTreeModel
*model
,
282 /* Incremental reflow */
283 static gint
validate_row (PsppSheetView
*tree_view
,
287 static void validate_visible_area (PsppSheetView
*tree_view
);
288 static gboolean
validate_rows_handler (PsppSheetView
*tree_view
);
289 static gboolean
presize_handler_callback (gpointer data
);
290 static void install_presize_handler (PsppSheetView
*tree_view
);
291 static void install_scroll_sync_handler (PsppSheetView
*tree_view
);
292 static void pspp_sheet_view_set_top_row (PsppSheetView
*tree_view
,
295 static void pspp_sheet_view_dy_to_top_row (PsppSheetView
*tree_view
);
296 static void pspp_sheet_view_top_row_to_dy (PsppSheetView
*tree_view
);
297 static void invalidate_empty_focus (PsppSheetView
*tree_view
);
299 /* Internal functions */
300 static GtkAdjustment
*pspp_sheet_view_do_get_hadjustment (PsppSheetView
*);
301 static GtkAdjustment
*pspp_sheet_view_do_get_vadjustment (PsppSheetView
*);
302 static void pspp_sheet_view_do_set_hadjustment (PsppSheetView
*tree_view
,
303 GtkAdjustment
*adjustment
);
304 static void pspp_sheet_view_do_set_vadjustment (PsppSheetView
*tree_view
,
305 GtkAdjustment
*adjustment
);
306 static void pspp_sheet_view_add_move_binding (GtkBindingSet
*binding_set
,
309 gboolean add_shifted_binding
,
310 GtkMovementStep step
,
312 static void pspp_sheet_view_queue_draw_path (PsppSheetView
*tree_view
,
314 const GdkRectangle
*clip_rect
);
315 static gint
pspp_sheet_view_new_column_width (PsppSheetView
*tree_view
,
318 static void pspp_sheet_view_adjustment_changed (GtkAdjustment
*adjustment
,
319 PsppSheetView
*tree_view
);
320 static void pspp_sheet_view_clamp_node_visible (PsppSheetView
*tree_view
,
322 static void pspp_sheet_view_clamp_column_visible (PsppSheetView
*tree_view
,
323 PsppSheetViewColumn
*column
,
324 gboolean focus_to_cell
);
325 static gboolean
pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView
*tree_view
,
326 GdkEventMotion
*event
);
327 static void pspp_sheet_view_focus_to_cursor (PsppSheetView
*tree_view
);
328 static gboolean
pspp_sheet_view_move_cursor_up_down (PsppSheetView
*tree_view
,
330 PsppSheetSelectMode mode
);
331 static void pspp_sheet_view_move_cursor_page_up_down (PsppSheetView
*tree_view
,
333 PsppSheetSelectMode mode
);
334 static void pspp_sheet_view_move_cursor_left_right (PsppSheetView
*tree_view
,
336 PsppSheetSelectMode mode
);
337 static void pspp_sheet_view_move_cursor_line_start_end (PsppSheetView
*tree_view
,
339 PsppSheetSelectMode mode
);
340 static void pspp_sheet_view_move_cursor_tab (PsppSheetView
*tree_view
,
342 static void pspp_sheet_view_move_cursor_start_end (PsppSheetView
*tree_view
,
344 PsppSheetSelectMode mode
);
345 static void pspp_sheet_view_real_set_cursor (PsppSheetView
*tree_view
,
347 gboolean clear_and_select
,
349 PsppSheetSelectMode mode
);
350 static gboolean
pspp_sheet_view_has_special_cell (PsppSheetView
*tree_view
);
351 static void pspp_sheet_view_stop_rubber_band (PsppSheetView
*tree_view
);
352 static void update_prelight (PsppSheetView
*tree_view
,
355 static void initialize_fixed_height_mode (PsppSheetView
*tree_view
);
357 /* interactive search */
358 static void pspp_sheet_view_ensure_interactive_directory (PsppSheetView
*tree_view
);
359 static void pspp_sheet_view_search_dialog_hide (GtkWidget
*search_dialog
,
360 PsppSheetView
*tree_view
);
361 static void pspp_sheet_view_search_position_func (PsppSheetView
*tree_view
,
362 GtkWidget
*search_dialog
,
364 static void pspp_sheet_view_search_disable_popdown (GtkEntry
*entry
,
368 static void pspp_sheet_view_search_preedit_changed (GtkIMContext
*im_context
,
369 PsppSheetView
*tree_view
);
371 static void pspp_sheet_view_search_activate (GtkEntry
*entry
,
372 PsppSheetView
*tree_view
);
373 static gboolean
pspp_sheet_view_real_search_enable_popdown(gpointer data
);
374 static void pspp_sheet_view_search_enable_popdown (GtkWidget
*widget
,
376 static gboolean
pspp_sheet_view_search_delete_event (GtkWidget
*widget
,
378 PsppSheetView
*tree_view
);
379 static gboolean
pspp_sheet_view_search_button_press_event (GtkWidget
*widget
,
380 GdkEventButton
*event
,
381 PsppSheetView
*tree_view
);
382 static gboolean
pspp_sheet_view_search_scroll_event (GtkWidget
*entry
,
383 GdkEventScroll
*event
,
384 PsppSheetView
*tree_view
);
385 static gboolean
pspp_sheet_view_search_key_press_event (GtkWidget
*entry
,
387 PsppSheetView
*tree_view
);
388 static gboolean
pspp_sheet_view_search_move (GtkWidget
*window
,
389 PsppSheetView
*tree_view
,
391 static gboolean
pspp_sheet_view_search_equal_func (GtkTreeModel
*model
,
395 gpointer search_data
);
396 static gboolean
pspp_sheet_view_search_iter (GtkTreeModel
*model
,
397 PsppSheetSelection
*selection
,
402 static void pspp_sheet_view_search_init (GtkWidget
*entry
,
403 PsppSheetView
*tree_view
);
404 static void pspp_sheet_view_put (PsppSheetView
*tree_view
,
405 GtkWidget
*child_widget
,
410 static gboolean
pspp_sheet_view_start_editing (PsppSheetView
*tree_view
,
411 GtkTreePath
*cursor_path
);
412 static gboolean
pspp_sheet_view_editable_button_press_event (GtkWidget
*,
415 static void pspp_sheet_view_editable_clicked (GtkButton
*, PsppSheetView
*);
416 static void pspp_sheet_view_real_start_editing (PsppSheetView
*tree_view
,
417 PsppSheetViewColumn
*column
,
419 GtkCellEditable
*cell_editable
,
420 GdkRectangle
*cell_area
,
423 static gboolean
pspp_sheet_view_real_start_interactive_search (PsppSheetView
*tree_view
,
424 gboolean keybinding
);
425 static gboolean
pspp_sheet_view_start_interactive_search (PsppSheetView
*tree_view
);
426 static PsppSheetViewColumn
*pspp_sheet_view_get_drop_column (PsppSheetView
*tree_view
,
427 PsppSheetViewColumn
*column
,
430 pspp_sheet_view_adjust_cell_area (PsppSheetView
*tree_view
,
431 PsppSheetViewColumn
*column
,
432 const GdkRectangle
*background_area
,
433 gboolean subtract_focus_rect
,
434 GdkRectangle
*cell_area
);
435 static gint
pspp_sheet_view_find_offset (PsppSheetView
*tree_view
,
440 static void pspp_sheet_view_buildable_add_child (GtkBuildable
*tree_view
,
444 static void pspp_sheet_view_buildable_init (GtkBuildableIface
*iface
);
447 static gboolean
scroll_row_timeout (gpointer data
);
448 static void add_scroll_timeout (PsppSheetView
*tree_view
);
449 static void remove_scroll_timeout (PsppSheetView
*tree_view
);
451 static guint tree_view_signals
[LAST_SIGNAL
] = { 0 };
453 static GtkBindingSet
*edit_bindings
;
460 G_DEFINE_TYPE_WITH_CODE (PsppSheetView
, pspp_sheet_view
, GTK_TYPE_CONTAINER
,
461 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE
,
462 pspp_sheet_view_buildable_init
)
463 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE
, NULL
))
466 pspp_sheet_view_get_preferred_width (GtkWidget
*widget
,
470 GtkRequisition requisition
;
472 pspp_sheet_view_size_request (widget
, &requisition
);
474 *minimal_width
= *natural_width
= requisition
.width
;
478 pspp_sheet_view_get_preferred_height (GtkWidget
*widget
,
479 gint
*minimal_height
,
480 gint
*natural_height
)
482 GtkRequisition requisition
;
484 pspp_sheet_view_size_request (widget
, &requisition
);
486 *minimal_height
= *natural_height
= requisition
.height
;
490 pspp_sheet_view_class_init (PsppSheetViewClass
*class)
492 GObjectClass
*o_class
;
493 GtkWidgetClass
*widget_class
;
494 GtkContainerClass
*container_class
;
495 GtkBindingSet
*binding_set
[2];
498 binding_set
[0] = gtk_binding_set_by_class (class);
500 binding_set
[1] = gtk_binding_set_new ("PsppSheetViewEditing");
501 edit_bindings
= binding_set
[1];
503 o_class
= (GObjectClass
*) class;
504 widget_class
= (GtkWidgetClass
*) class;
505 container_class
= (GtkContainerClass
*) class;
507 /* GObject signals */
508 o_class
->set_property
= pspp_sheet_view_set_property
;
509 o_class
->get_property
= pspp_sheet_view_get_property
;
510 o_class
->finalize
= pspp_sheet_view_finalize
;
511 o_class
->dispose
= pspp_sheet_view_dispose
;
513 /* GtkWidget signals */
514 widget_class
->map
= pspp_sheet_view_map
;
515 widget_class
->realize
= pspp_sheet_view_realize
;
516 widget_class
->unrealize
= pspp_sheet_view_unrealize
;
517 widget_class
->get_preferred_width
= pspp_sheet_view_get_preferred_width
;
518 widget_class
->get_preferred_height
= pspp_sheet_view_get_preferred_height
;
519 widget_class
->size_allocate
= pspp_sheet_view_size_allocate
;
520 widget_class
->button_press_event
= pspp_sheet_view_button_press
;
521 widget_class
->button_release_event
= pspp_sheet_view_button_release
;
522 widget_class
->grab_broken_event
= pspp_sheet_view_grab_broken
;
523 /*widget_class->configure_event = pspp_sheet_view_configure;*/
524 widget_class
->motion_notify_event
= pspp_sheet_view_motion
;
525 widget_class
->draw
= pspp_sheet_view_draw
;
526 widget_class
->key_press_event
= pspp_sheet_view_key_press
;
527 widget_class
->key_release_event
= pspp_sheet_view_key_release
;
528 widget_class
->enter_notify_event
= pspp_sheet_view_enter_notify
;
529 widget_class
->leave_notify_event
= pspp_sheet_view_leave_notify
;
530 widget_class
->focus_out_event
= pspp_sheet_view_focus_out
;
531 widget_class
->drag_begin
= pspp_sheet_view_drag_begin
;
532 widget_class
->drag_end
= pspp_sheet_view_drag_end
;
533 widget_class
->drag_data_get
= pspp_sheet_view_drag_data_get
;
534 widget_class
->drag_data_delete
= pspp_sheet_view_drag_data_delete
;
535 widget_class
->drag_leave
= pspp_sheet_view_drag_leave
;
536 widget_class
->drag_motion
= pspp_sheet_view_drag_motion
;
537 widget_class
->drag_drop
= pspp_sheet_view_drag_drop
;
538 widget_class
->drag_data_received
= pspp_sheet_view_drag_data_received
;
539 widget_class
->focus
= pspp_sheet_view_focus
;
540 widget_class
->grab_focus
= pspp_sheet_view_grab_focus
;
541 widget_class
->style_set
= pspp_sheet_view_style_set
;
542 widget_class
->grab_notify
= pspp_sheet_view_grab_notify
;
543 widget_class
->state_changed
= pspp_sheet_view_state_changed
;
545 /* GtkContainer signals */
546 container_class
->remove
= pspp_sheet_view_remove
;
547 container_class
->forall
= pspp_sheet_view_forall
;
548 container_class
->set_focus_child
= pspp_sheet_view_set_focus_child
;
550 class->set_scroll_adjustments
= pspp_sheet_view_set_adjustments
;
551 class->move_cursor
= pspp_sheet_view_real_move_cursor
;
552 class->select_all
= pspp_sheet_view_real_select_all
;
553 class->unselect_all
= pspp_sheet_view_real_unselect_all
;
554 class->select_cursor_row
= pspp_sheet_view_real_select_cursor_row
;
555 class->toggle_cursor_row
= pspp_sheet_view_real_toggle_cursor_row
;
556 class->start_interactive_search
= pspp_sheet_view_start_interactive_search
;
560 g_object_class_install_property (o_class
,
562 g_param_spec_object ("model",
563 P_("TreeView Model"),
564 P_("The model for the tree view"),
566 GTK_PARAM_READWRITE
));
568 g_object_class_override_property (o_class
, PROP_HADJUSTMENT
, "hadjustment");
569 g_object_class_override_property (o_class
, PROP_VADJUSTMENT
, "vadjustment");
570 g_object_class_override_property (o_class
, PROP_HSCROLL_POLICY
, "hscroll-policy");
571 g_object_class_override_property (o_class
, PROP_VSCROLL_POLICY
, "vscroll-policy");
573 g_object_class_install_property (o_class
,
574 PROP_HEADERS_VISIBLE
,
575 g_param_spec_boolean ("headers-visible",
576 P_("Headers Visible"),
577 P_("Show the column header buttons"),
579 GTK_PARAM_READWRITE
));
581 g_object_class_install_property (o_class
,
582 PROP_HEADERS_CLICKABLE
,
583 g_param_spec_boolean ("headers-clickable",
584 P_("Headers Clickable"),
585 P_("Column headers respond to click events"),
587 GTK_PARAM_READWRITE
));
589 g_object_class_install_property (o_class
,
591 g_param_spec_boolean ("reorderable",
593 P_("View is reorderable"),
595 GTK_PARAM_READWRITE
));
597 g_object_class_install_property (o_class
,
599 g_param_spec_boolean ("rules-hint",
601 P_("Set a hint to the theme engine to draw rows in alternating colors"),
603 GTK_PARAM_READWRITE
));
605 g_object_class_install_property (o_class
,
607 g_param_spec_boolean ("enable-search",
609 P_("View allows user to search through columns interactively"),
611 GTK_PARAM_READWRITE
));
613 g_object_class_install_property (o_class
,
615 g_param_spec_int ("search-column",
617 P_("Model column to search through during interactive search"),
621 GTK_PARAM_READWRITE
));
624 * PsppSheetView:hover-selection:
626 * Enables of disables the hover selection mode of @tree_view.
627 * Hover selection makes the selected row follow the pointer.
628 * Currently, this works only for the selection modes
629 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
631 * This mode is primarily intended for treeviews in popups, e.g.
632 * in #GtkComboBox or #GtkEntryCompletion.
636 g_object_class_install_property (o_class
,
637 PROP_HOVER_SELECTION
,
638 g_param_spec_boolean ("hover-selection",
639 P_("Hover Selection"),
640 P_("Whether the selection should follow the pointer"),
642 GTK_PARAM_READWRITE
));
644 g_object_class_install_property (o_class
,
646 g_param_spec_boolean ("rubber-banding",
647 P_("Rubber Banding"),
648 P_("Whether to enable selection of multiple items by dragging the mouse pointer"),
650 GTK_PARAM_READWRITE
));
652 g_object_class_install_property (o_class
,
653 PROP_ENABLE_GRID_LINES
,
654 g_param_spec_enum ("enable-grid-lines",
655 P_("Enable Grid Lines"),
656 P_("Whether grid lines should be drawn in the tree view"),
657 PSPP_TYPE_SHEET_VIEW_GRID_LINES
,
658 PSPP_SHEET_VIEW_GRID_LINES_NONE
,
659 GTK_PARAM_READWRITE
));
661 g_object_class_install_property (o_class
,
663 g_param_spec_int ("tooltip-column",
664 P_("Tooltip Column"),
665 P_("The column in the model containing the tooltip texts for the rows"),
669 GTK_PARAM_READWRITE
));
671 g_object_class_install_property (o_class
,
673 g_param_spec_enum ("special-cells",
675 P_("Whether rows have special cells."),
676 PSPP_TYPE_SHEET_VIEW_SPECIAL_CELLS
,
677 PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT
,
678 GTK_PARAM_READWRITE
));
680 g_object_class_install_property (o_class
,
682 g_param_spec_int ("fixed-height",
684 P_("Height of a single row. Normally the height of a row is determined automatically. Writing this property sets fixed-height-set to true, preventing this property's value from changing."),
688 GTK_PARAM_READWRITE
));
690 g_object_class_install_property (o_class
,
691 PROP_FIXED_HEIGHT_SET
,
692 g_param_spec_boolean ("fixed-height-set",
693 P_("Fixed Height Set"),
694 P_("Whether fixed-height was set externally."),
696 GTK_PARAM_READWRITE
));
698 /* Style properties */
699 #define _TREE_VIEW_EXPANDER_SIZE 12
700 #define _TREE_VIEW_VERTICAL_SEPARATOR 2
701 #define _TREE_VIEW_HORIZONTAL_SEPARATOR 2
703 gtk_widget_class_install_style_property (widget_class
,
704 g_param_spec_int ("expander-size",
706 P_("Size of the expander arrow"),
709 _TREE_VIEW_EXPANDER_SIZE
,
710 GTK_PARAM_READABLE
));
712 gtk_widget_class_install_style_property (widget_class
,
713 g_param_spec_int ("vertical-separator",
714 P_("Vertical Separator Width"),
715 P_("Vertical space between cells. Must be an even number"),
718 _TREE_VIEW_VERTICAL_SEPARATOR
,
719 GTK_PARAM_READABLE
));
721 gtk_widget_class_install_style_property (widget_class
,
722 g_param_spec_int ("horizontal-separator",
723 P_("Horizontal Separator Width"),
724 P_("Horizontal space between cells. Must be an even number"),
727 _TREE_VIEW_HORIZONTAL_SEPARATOR
,
728 GTK_PARAM_READABLE
));
730 gtk_widget_class_install_style_property (widget_class
,
731 g_param_spec_boolean ("allow-rules",
733 P_("Allow drawing of alternating color rows"),
735 GTK_PARAM_READABLE
));
737 gtk_widget_class_install_style_property (widget_class
,
738 g_param_spec_boxed ("even-row-color",
739 P_("Even Row Color"),
740 P_("Color to use for even rows"),
742 GTK_PARAM_READABLE
));
744 gtk_widget_class_install_style_property (widget_class
,
745 g_param_spec_boxed ("odd-row-color",
747 P_("Color to use for odd rows"),
749 GTK_PARAM_READABLE
));
751 gtk_widget_class_install_style_property (widget_class
,
752 g_param_spec_boolean ("row-ending-details",
753 P_("Row Ending details"),
754 P_("Enable extended row background theming"),
756 GTK_PARAM_READABLE
));
758 gtk_widget_class_install_style_property (widget_class
,
759 g_param_spec_int ("grid-line-width",
760 P_("Grid line width"),
761 P_("Width, in pixels, of the tree view grid lines"),
763 GTK_PARAM_READABLE
));
765 gtk_widget_class_install_style_property (widget_class
,
766 g_param_spec_int ("tree-line-width",
767 P_("Tree line width"),
768 P_("Width, in pixels, of the tree view lines"),
770 GTK_PARAM_READABLE
));
772 gtk_widget_class_install_style_property (widget_class
,
773 g_param_spec_string ("tree-line-pattern",
774 P_("Tree line pattern"),
775 P_("Dash pattern used to draw the tree view lines"),
777 GTK_PARAM_READABLE
));
782 * PsppSheetView::set-scroll-adjustments
783 * @horizontal: the horizontal #GtkAdjustment
784 * @vertical: the vertical #GtkAdjustment
786 * Set the scroll adjustments for the tree view. Usually scrolled containers
787 * like #GtkScrolledWindow will emit this signal to connect two instances
788 * of #GtkScrollbar to the scroll directions of the #PsppSheetView.
790 widget_class
->set_scroll_adjustments_signal
=
791 g_signal_new ("set-scroll-adjustments",
792 G_TYPE_FROM_CLASS (o_class
),
793 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
794 G_STRUCT_OFFSET (PsppSheetViewClass
, set_scroll_adjustments
),
796 psppire_marshal_VOID__OBJECT_OBJECT
,
799 GTK_TYPE_ADJUSTMENT
);
803 * PsppSheetView::row-activated:
804 * @tree_view: the object on which the signal is emitted
805 * @path: the #GtkTreePath for the activated row
806 * @column: the #PsppSheetViewColumn in which the activation occurred
808 * The "row-activated" signal is emitted when the method
809 * pspp_sheet_view_row_activated() is called or the user double clicks
810 * a treeview row. It is also emitted when a non-editable row is
811 * selected and one of the keys: Space, Shift+Space, Return or
814 * For selection handling refer to the <link linkend="TreeWidget">tree
815 * widget conceptual overview</link> as well as #PsppSheetSelection.
817 tree_view_signals
[ROW_ACTIVATED
] =
818 g_signal_new ("row-activated",
819 G_TYPE_FROM_CLASS (o_class
),
820 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
821 G_STRUCT_OFFSET (PsppSheetViewClass
, row_activated
),
823 psppire_marshal_VOID__BOXED_OBJECT
,
826 PSPP_TYPE_SHEET_VIEW_COLUMN
);
829 * PsppSheetView::columns-changed:
830 * @tree_view: the object on which the signal is emitted
832 * The number of columns of the treeview has changed.
834 tree_view_signals
[COLUMNS_CHANGED
] =
835 g_signal_new ("columns-changed",
836 G_TYPE_FROM_CLASS (o_class
),
838 G_STRUCT_OFFSET (PsppSheetViewClass
, columns_changed
),
840 g_cclosure_marshal_VOID__VOID
,
844 * PsppSheetView::cursor-changed:
845 * @tree_view: the object on which the signal is emitted
847 * The position of the cursor (focused cell) has changed.
849 tree_view_signals
[CURSOR_CHANGED
] =
850 g_signal_new ("cursor-changed",
851 G_TYPE_FROM_CLASS (o_class
),
853 G_STRUCT_OFFSET (PsppSheetViewClass
, cursor_changed
),
855 g_cclosure_marshal_VOID__VOID
,
858 tree_view_signals
[MOVE_CURSOR
] =
859 g_signal_new ("move-cursor",
860 G_TYPE_FROM_CLASS (o_class
),
861 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
862 G_STRUCT_OFFSET (PsppSheetViewClass
, move_cursor
),
864 psppire_marshal_BOOLEAN__ENUM_INT
,
866 GTK_TYPE_MOVEMENT_STEP
,
869 tree_view_signals
[SELECT_ALL
] =
870 g_signal_new ("select-all",
871 G_TYPE_FROM_CLASS (o_class
),
872 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
873 G_STRUCT_OFFSET (PsppSheetViewClass
, select_all
),
875 psppire_marshal_BOOLEAN__VOID
,
878 tree_view_signals
[UNSELECT_ALL
] =
879 g_signal_new ("unselect-all",
880 G_TYPE_FROM_CLASS (o_class
),
881 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
882 G_STRUCT_OFFSET (PsppSheetViewClass
, unselect_all
),
884 psppire_marshal_BOOLEAN__VOID
,
887 tree_view_signals
[SELECT_CURSOR_ROW
] =
888 g_signal_new ("select-cursor-row",
889 G_TYPE_FROM_CLASS (o_class
),
890 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
891 G_STRUCT_OFFSET (PsppSheetViewClass
, select_cursor_row
),
893 psppire_marshal_BOOLEAN__BOOLEAN
,
895 G_TYPE_BOOLEAN
, G_TYPE_INT
);
897 tree_view_signals
[TOGGLE_CURSOR_ROW
] =
898 g_signal_new ("toggle-cursor-row",
899 G_TYPE_FROM_CLASS (o_class
),
900 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
901 G_STRUCT_OFFSET (PsppSheetViewClass
, toggle_cursor_row
),
903 psppire_marshal_BOOLEAN__VOID
,
906 tree_view_signals
[START_INTERACTIVE_SEARCH
] =
907 g_signal_new ("start-interactive-search",
908 G_TYPE_FROM_CLASS (o_class
),
909 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
910 G_STRUCT_OFFSET (PsppSheetViewClass
, start_interactive_search
),
912 psppire_marshal_BOOLEAN__VOID
,
916 for (i
= 0; i
< 2; i
++)
918 pspp_sheet_view_add_move_binding (binding_set
[i
], GDK_Up
, 0, TRUE
,
919 GTK_MOVEMENT_DISPLAY_LINES
, -1);
920 pspp_sheet_view_add_move_binding (binding_set
[i
], GDK_KP_Up
, 0, TRUE
,
921 GTK_MOVEMENT_DISPLAY_LINES
, -1);
923 pspp_sheet_view_add_move_binding (binding_set
[i
], GDK_Down
, 0, TRUE
,
924 GTK_MOVEMENT_DISPLAY_LINES
, 1);
925 pspp_sheet_view_add_move_binding (binding_set
[i
], GDK_KP_Down
, 0, TRUE
,
926 GTK_MOVEMENT_DISPLAY_LINES
, 1);
928 pspp_sheet_view_add_move_binding (binding_set
[i
], GDK_p
, GDK_CONTROL_MASK
, FALSE
,
929 GTK_MOVEMENT_DISPLAY_LINES
, -1);
931 pspp_sheet_view_add_move_binding (binding_set
[i
], GDK_n
, GDK_CONTROL_MASK
, FALSE
,
932 GTK_MOVEMENT_DISPLAY_LINES
, 1);
934 pspp_sheet_view_add_move_binding (binding_set
[i
], GDK_Home
, 0, TRUE
,
935 GTK_MOVEMENT_DISPLAY_LINE_ENDS
, -1);
936 pspp_sheet_view_add_move_binding (binding_set
[i
], GDK_KP_Home
, 0, TRUE
,
937 GTK_MOVEMENT_DISPLAY_LINE_ENDS
, -1);
939 pspp_sheet_view_add_move_binding (binding_set
[i
], GDK_End
, 0, TRUE
,
940 GTK_MOVEMENT_DISPLAY_LINE_ENDS
, 1);
941 pspp_sheet_view_add_move_binding (binding_set
[i
], GDK_KP_End
, 0, TRUE
,
942 GTK_MOVEMENT_DISPLAY_LINE_ENDS
, 1);
944 pspp_sheet_view_add_move_binding (binding_set
[i
], GDK_Page_Up
, 0, TRUE
,
945 GTK_MOVEMENT_PAGES
, -1);
946 pspp_sheet_view_add_move_binding (binding_set
[i
], GDK_KP_Page_Up
, 0, TRUE
,
947 GTK_MOVEMENT_PAGES
, -1);
949 pspp_sheet_view_add_move_binding (binding_set
[i
], GDK_Page_Down
, 0, TRUE
,
950 GTK_MOVEMENT_PAGES
, 1);
951 pspp_sheet_view_add_move_binding (binding_set
[i
], GDK_KP_Page_Down
, 0, TRUE
,
952 GTK_MOVEMENT_PAGES
, 1);
955 gtk_binding_entry_add_signal (binding_set
[i
], GDK_Up
, GDK_CONTROL_MASK
, "move-cursor", 2,
956 G_TYPE_ENUM
, GTK_MOVEMENT_BUFFER_ENDS
,
959 gtk_binding_entry_add_signal (binding_set
[i
], GDK_Down
, GDK_CONTROL_MASK
, "move-cursor", 2,
960 G_TYPE_ENUM
, GTK_MOVEMENT_BUFFER_ENDS
,
963 gtk_binding_entry_add_signal (binding_set
[i
], GDK_Right
, 0, "move-cursor", 2,
964 G_TYPE_ENUM
, GTK_MOVEMENT_VISUAL_POSITIONS
,
967 gtk_binding_entry_add_signal (binding_set
[i
], GDK_Left
, 0, "move-cursor", 2,
968 G_TYPE_ENUM
, GTK_MOVEMENT_VISUAL_POSITIONS
,
971 gtk_binding_entry_add_signal (binding_set
[i
], GDK_Tab
, 0, "move-cursor", 2,
972 G_TYPE_ENUM
, GTK_MOVEMENT_LOGICAL_POSITIONS
,
975 gtk_binding_entry_add_signal (binding_set
[i
], GDK_Tab
, GDK_SHIFT_MASK
, "move-cursor", 2,
976 G_TYPE_ENUM
, GTK_MOVEMENT_LOGICAL_POSITIONS
,
979 gtk_binding_entry_add_signal (binding_set
[i
], GDK_KP_Right
, 0, "move-cursor", 2,
980 G_TYPE_ENUM
, GTK_MOVEMENT_DISPLAY_LINE_ENDS
,
983 gtk_binding_entry_add_signal (binding_set
[i
], GDK_KP_Left
, 0, "move-cursor", 2,
984 G_TYPE_ENUM
, GTK_MOVEMENT_DISPLAY_LINE_ENDS
,
987 gtk_binding_entry_add_signal (binding_set
[i
], GDK_Right
, GDK_CONTROL_MASK
,
989 G_TYPE_ENUM
, GTK_MOVEMENT_DISPLAY_LINE_ENDS
,
992 gtk_binding_entry_add_signal (binding_set
[i
], GDK_Left
, GDK_CONTROL_MASK
,
994 G_TYPE_ENUM
, GTK_MOVEMENT_DISPLAY_LINE_ENDS
,
997 gtk_binding_entry_add_signal (binding_set
[i
], GDK_KP_Right
, GDK_CONTROL_MASK
,
999 G_TYPE_ENUM
, GTK_MOVEMENT_VISUAL_POSITIONS
,
1002 gtk_binding_entry_add_signal (binding_set
[i
], GDK_KP_Left
, GDK_CONTROL_MASK
,
1004 G_TYPE_ENUM
, GTK_MOVEMENT_VISUAL_POSITIONS
,
1007 gtk_binding_entry_add_signal (binding_set
[i
], GDK_f
, GDK_CONTROL_MASK
, "start-interactive-search", 0);
1009 gtk_binding_entry_add_signal (binding_set
[i
], GDK_F
, GDK_CONTROL_MASK
, "start-interactive-search", 0);
1012 gtk_binding_entry_add_signal (binding_set
[0], GDK_space
, GDK_CONTROL_MASK
, "toggle-cursor-row", 0);
1013 gtk_binding_entry_add_signal (binding_set
[0], GDK_KP_Space
, GDK_CONTROL_MASK
, "toggle-cursor-row", 0);
1015 gtk_binding_entry_add_signal (binding_set
[0], GDK_a
, GDK_CONTROL_MASK
, "select-all", 0);
1016 gtk_binding_entry_add_signal (binding_set
[0], GDK_slash
, GDK_CONTROL_MASK
, "select-all", 0);
1018 gtk_binding_entry_add_signal (binding_set
[0], GDK_A
, GDK_CONTROL_MASK
| GDK_SHIFT_MASK
, "unselect-all", 0);
1019 gtk_binding_entry_add_signal (binding_set
[0], GDK_backslash
, GDK_CONTROL_MASK
, "unselect-all", 0);
1021 gtk_binding_entry_add_signal (binding_set
[0], GDK_space
, GDK_SHIFT_MASK
, "select-cursor-row", 1,
1022 G_TYPE_BOOLEAN
, TRUE
,
1023 G_TYPE_INT
, PSPP_SHEET_SELECT_MODE_EXTEND
);
1024 gtk_binding_entry_add_signal (binding_set
[0], GDK_KP_Space
, GDK_SHIFT_MASK
, "select-cursor-row", 1,
1025 G_TYPE_BOOLEAN
, TRUE
,
1026 G_TYPE_INT
, PSPP_SHEET_SELECT_MODE_EXTEND
);
1028 gtk_binding_entry_add_signal (binding_set
[0], GDK_space
, 0, "select-cursor-row", 1,
1029 G_TYPE_BOOLEAN
, TRUE
,
1031 gtk_binding_entry_add_signal (binding_set
[0], GDK_KP_Space
, 0, "select-cursor-row", 1,
1032 G_TYPE_BOOLEAN
, TRUE
,
1034 gtk_binding_entry_add_signal (binding_set
[0], GDK_Return
, 0, "select-cursor-row", 1,
1035 G_TYPE_BOOLEAN
, TRUE
,
1037 gtk_binding_entry_add_signal (binding_set
[0], GDK_ISO_Enter
, 0, "select-cursor-row", 1,
1038 G_TYPE_BOOLEAN
, TRUE
,
1040 gtk_binding_entry_add_signal (binding_set
[0], GDK_KP_Enter
, 0, "select-cursor-row", 1,
1041 G_TYPE_BOOLEAN
, TRUE
,
1044 gtk_binding_entry_add_signal (binding_set
[0], GDK_BackSpace
, 0, "select-cursor-parent", 0);
1045 gtk_binding_entry_add_signal (binding_set
[0], GDK_BackSpace
, GDK_CONTROL_MASK
, "select-cursor-parent", 0);
1047 g_type_class_add_private (o_class
, sizeof (PsppSheetViewPrivate
));
1051 pspp_sheet_view_buildable_init (GtkBuildableIface
*iface
)
1053 iface
->add_child
= pspp_sheet_view_buildable_add_child
;
1057 pspp_sheet_view_init (PsppSheetView
*tree_view
)
1059 tree_view
->priv
= G_TYPE_INSTANCE_GET_PRIVATE (tree_view
, PSPP_TYPE_SHEET_VIEW
, PsppSheetViewPrivate
);
1061 gtk_widget_set_can_focus (GTK_WIDGET (tree_view
), TRUE
);
1062 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (tree_view
), FALSE
);
1064 tree_view
->priv
->flags
= PSPP_SHEET_VIEW_DRAW_KEYFOCUS
1065 | PSPP_SHEET_VIEW_HEADERS_VISIBLE
;
1067 /* We need some padding */
1068 tree_view
->priv
->selected
= range_tower_create ();
1069 tree_view
->priv
->dy
= 0;
1070 tree_view
->priv
->cursor_offset
= 0;
1071 tree_view
->priv
->n_columns
= 0;
1072 tree_view
->priv
->header_height
= 1;
1073 tree_view
->priv
->x_drag
= 0;
1074 tree_view
->priv
->drag_pos
= -1;
1075 tree_view
->priv
->header_has_focus
= FALSE
;
1076 tree_view
->priv
->pressed_button
= -1;
1077 tree_view
->priv
->press_start_x
= -1;
1078 tree_view
->priv
->press_start_y
= -1;
1079 tree_view
->priv
->reorderable
= FALSE
;
1080 tree_view
->priv
->presize_handler_timer
= 0;
1081 tree_view
->priv
->scroll_sync_timer
= 0;
1082 tree_view
->priv
->fixed_height
= -1;
1083 tree_view
->priv
->fixed_height_set
= FALSE
;
1084 pspp_sheet_view_set_adjustments (tree_view
, NULL
, NULL
);
1085 tree_view
->priv
->selection
= _pspp_sheet_selection_new_with_tree_view (tree_view
);
1086 tree_view
->priv
->enable_search
= TRUE
;
1087 tree_view
->priv
->search_column
= -1;
1088 tree_view
->priv
->search_position_func
= pspp_sheet_view_search_position_func
;
1089 tree_view
->priv
->search_equal_func
= pspp_sheet_view_search_equal_func
;
1090 tree_view
->priv
->search_custom_entry_set
= FALSE
;
1091 tree_view
->priv
->typeselect_flush_timeout
= 0;
1092 tree_view
->priv
->init_hadjust_value
= TRUE
;
1093 tree_view
->priv
->width
= 0;
1095 tree_view
->priv
->hover_selection
= FALSE
;
1097 tree_view
->priv
->rubber_banding_enable
= FALSE
;
1099 tree_view
->priv
->grid_lines
= PSPP_SHEET_VIEW_GRID_LINES_NONE
;
1101 tree_view
->priv
->tooltip_column
= -1;
1103 tree_view
->priv
->special_cells
= PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT
;
1105 tree_view
->priv
->post_validation_flag
= FALSE
;
1107 tree_view
->priv
->last_button_x
= -1;
1108 tree_view
->priv
->last_button_y
= -1;
1110 tree_view
->priv
->event_last_x
= -10000;
1111 tree_view
->priv
->event_last_y
= -10000;
1113 tree_view
->priv
->prelight_node
= -1;
1114 tree_view
->priv
->rubber_band_start_node
= -1;
1115 tree_view
->priv
->rubber_band_end_node
= -1;
1117 tree_view
->priv
->anchor_column
= NULL
;
1119 tree_view
->priv
->button_style
= NULL
;
1121 tree_view
->dispose_has_run
= FALSE
;
1123 pspp_sheet_view_do_set_vadjustment (tree_view
, NULL
);
1124 pspp_sheet_view_do_set_hadjustment (tree_view
, NULL
);
1133 pspp_sheet_view_set_property (GObject
*object
,
1135 const GValue
*value
,
1138 PsppSheetView
*tree_view
;
1140 tree_view
= PSPP_SHEET_VIEW (object
);
1145 pspp_sheet_view_set_model (tree_view
, g_value_get_object (value
));
1147 case PROP_HADJUSTMENT
:
1148 pspp_sheet_view_do_set_hadjustment (tree_view
, g_value_get_object (value
));
1150 case PROP_VADJUSTMENT
:
1151 pspp_sheet_view_do_set_vadjustment (tree_view
, g_value_get_object (value
));
1153 case PROP_HSCROLL_POLICY
:
1154 tree_view
->priv
->hscroll_policy
= g_value_get_enum (value
);
1155 gtk_widget_queue_resize (GTK_WIDGET (tree_view
));
1157 case PROP_VSCROLL_POLICY
:
1158 tree_view
->priv
->vscroll_policy
= g_value_get_enum (value
);
1159 gtk_widget_queue_resize (GTK_WIDGET (tree_view
));
1161 case PROP_HEADERS_VISIBLE
:
1162 pspp_sheet_view_set_headers_visible (tree_view
, g_value_get_boolean (value
));
1164 case PROP_HEADERS_CLICKABLE
:
1165 pspp_sheet_view_set_headers_clickable (tree_view
, g_value_get_boolean (value
));
1167 case PROP_REORDERABLE
:
1168 pspp_sheet_view_set_reorderable (tree_view
, g_value_get_boolean (value
));
1170 case PROP_RULES_HINT
:
1171 pspp_sheet_view_set_rules_hint (tree_view
, g_value_get_boolean (value
));
1173 case PROP_ENABLE_SEARCH
:
1174 pspp_sheet_view_set_enable_search (tree_view
, g_value_get_boolean (value
));
1176 case PROP_SEARCH_COLUMN
:
1177 pspp_sheet_view_set_search_column (tree_view
, g_value_get_int (value
));
1179 case PROP_HOVER_SELECTION
:
1180 tree_view
->priv
->hover_selection
= g_value_get_boolean (value
);
1182 case PROP_RUBBER_BANDING
:
1183 tree_view
->priv
->rubber_banding_enable
= g_value_get_boolean (value
);
1185 case PROP_ENABLE_GRID_LINES
:
1186 pspp_sheet_view_set_grid_lines (tree_view
, g_value_get_enum (value
));
1188 case PROP_TOOLTIP_COLUMN
:
1189 pspp_sheet_view_set_tooltip_column (tree_view
, g_value_get_int (value
));
1191 case PROP_SPECIAL_CELLS
:
1192 pspp_sheet_view_set_special_cells (tree_view
, g_value_get_enum (value
));
1194 case PROP_FIXED_HEIGHT
:
1195 pspp_sheet_view_set_fixed_height (tree_view
, g_value_get_int (value
));
1197 case PROP_FIXED_HEIGHT_SET
:
1198 if (g_value_get_boolean (value
))
1200 if (!tree_view
->priv
->fixed_height_set
1201 && tree_view
->priv
->fixed_height
>= 0)
1203 tree_view
->priv
->fixed_height_set
= true;
1204 g_object_notify (G_OBJECT (tree_view
), "fixed-height-set");
1209 if (tree_view
->priv
->fixed_height_set
)
1211 tree_view
->priv
->fixed_height_set
= false;
1212 g_object_notify (G_OBJECT (tree_view
), "fixed-height-set");
1213 install_presize_handler (tree_view
);
1218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1224 pspp_sheet_view_get_property (GObject
*object
,
1229 PsppSheetView
*tree_view
;
1231 tree_view
= PSPP_SHEET_VIEW (object
);
1236 g_value_set_object (value
, tree_view
->priv
->model
);
1238 case PROP_HADJUSTMENT
:
1239 g_value_set_object (value
, tree_view
->priv
->hadjustment
);
1241 case PROP_VADJUSTMENT
:
1242 g_value_set_object (value
, tree_view
->priv
->vadjustment
);
1244 case PROP_HSCROLL_POLICY
:
1245 g_value_set_enum (value
, tree_view
->priv
->hscroll_policy
);
1247 case PROP_VSCROLL_POLICY
:
1248 g_value_set_enum (value
, tree_view
->priv
->vscroll_policy
);
1250 case PROP_HEADERS_VISIBLE
:
1251 g_value_set_boolean (value
, pspp_sheet_view_get_headers_visible (tree_view
));
1253 case PROP_HEADERS_CLICKABLE
:
1254 g_value_set_boolean (value
, pspp_sheet_view_get_headers_clickable (tree_view
));
1256 case PROP_REORDERABLE
:
1257 g_value_set_boolean (value
, tree_view
->priv
->reorderable
);
1259 case PROP_RULES_HINT
:
1260 g_value_set_boolean (value
, tree_view
->priv
->has_rules
);
1262 case PROP_ENABLE_SEARCH
:
1263 g_value_set_boolean (value
, tree_view
->priv
->enable_search
);
1265 case PROP_SEARCH_COLUMN
:
1266 g_value_set_int (value
, tree_view
->priv
->search_column
);
1268 case PROP_HOVER_SELECTION
:
1269 g_value_set_boolean (value
, tree_view
->priv
->hover_selection
);
1271 case PROP_RUBBER_BANDING
:
1272 g_value_set_boolean (value
, tree_view
->priv
->rubber_banding_enable
);
1274 case PROP_ENABLE_GRID_LINES
:
1275 g_value_set_enum (value
, tree_view
->priv
->grid_lines
);
1277 case PROP_TOOLTIP_COLUMN
:
1278 g_value_set_int (value
, tree_view
->priv
->tooltip_column
);
1280 case PROP_SPECIAL_CELLS
:
1281 g_value_set_enum (value
, tree_view
->priv
->special_cells
);
1283 case PROP_FIXED_HEIGHT
:
1284 g_value_set_int (value
, pspp_sheet_view_get_fixed_height (tree_view
));
1286 case PROP_FIXED_HEIGHT_SET
:
1287 g_value_set_boolean (value
, tree_view
->priv
->fixed_height_set
);
1290 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1296 pspp_sheet_view_dispose (GObject
*object
)
1298 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (object
);
1300 if (tree_view
->dispose_has_run
)
1303 tree_view
->dispose_has_run
= TRUE
;
1305 if (tree_view
->priv
->selection
!= NULL
)
1307 _pspp_sheet_selection_set_tree_view (tree_view
->priv
->selection
, NULL
);
1308 g_object_unref (tree_view
->priv
->selection
);
1309 tree_view
->priv
->selection
= NULL
;
1312 if (tree_view
->priv
->hadjustment
)
1314 g_object_unref (tree_view
->priv
->hadjustment
);
1315 tree_view
->priv
->hadjustment
= NULL
;
1317 if (tree_view
->priv
->vadjustment
)
1319 g_object_unref (tree_view
->priv
->vadjustment
);
1320 tree_view
->priv
->vadjustment
= NULL
;
1323 if (tree_view
->priv
->button_style
)
1325 g_object_unref (tree_view
->priv
->button_style
);
1326 tree_view
->priv
->button_style
= NULL
;
1330 G_OBJECT_CLASS (pspp_sheet_view_parent_class
)->dispose (object
);
1336 pspp_sheet_view_buildable_add_child (GtkBuildable
*tree_view
,
1337 GtkBuilder
*builder
,
1341 pspp_sheet_view_append_column (PSPP_SHEET_VIEW (tree_view
), PSPP_SHEET_VIEW_COLUMN (child
));
1345 pspp_sheet_view_finalize (GObject
*object
)
1347 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (object
);
1349 pspp_sheet_view_stop_editing (tree_view
, TRUE
);
1351 if (tree_view
->priv
->selected
!= NULL
)
1353 range_tower_destroy (tree_view
->priv
->selected
);
1354 tree_view
->priv
->selected
= NULL
;
1358 tree_view
->priv
->prelight_node
= -1;
1361 if (tree_view
->priv
->scroll_to_path
!= NULL
)
1363 gtk_tree_row_reference_free (tree_view
->priv
->scroll_to_path
);
1364 tree_view
->priv
->scroll_to_path
= NULL
;
1367 if (tree_view
->priv
->drag_dest_row
!= NULL
)
1369 gtk_tree_row_reference_free (tree_view
->priv
->drag_dest_row
);
1370 tree_view
->priv
->drag_dest_row
= NULL
;
1373 if (tree_view
->priv
->top_row
!= NULL
)
1375 gtk_tree_row_reference_free (tree_view
->priv
->top_row
);
1376 tree_view
->priv
->top_row
= NULL
;
1379 if (tree_view
->priv
->column_drop_func_data
&&
1380 tree_view
->priv
->column_drop_func_data_destroy
)
1382 tree_view
->priv
->column_drop_func_data_destroy (tree_view
->priv
->column_drop_func_data
);
1383 tree_view
->priv
->column_drop_func_data
= NULL
;
1386 if (tree_view
->priv
->destroy_count_destroy
&&
1387 tree_view
->priv
->destroy_count_data
)
1389 tree_view
->priv
->destroy_count_destroy (tree_view
->priv
->destroy_count_data
);
1390 tree_view
->priv
->destroy_count_data
= NULL
;
1393 gtk_tree_row_reference_free (tree_view
->priv
->cursor
);
1394 tree_view
->priv
->cursor
= NULL
;
1396 gtk_tree_row_reference_free (tree_view
->priv
->anchor
);
1397 tree_view
->priv
->anchor
= NULL
;
1399 /* destroy interactive search dialog */
1400 if (tree_view
->priv
->search_window
)
1402 gtk_widget_destroy (tree_view
->priv
->search_window
);
1403 tree_view
->priv
->search_window
= NULL
;
1404 tree_view
->priv
->search_entry
= NULL
;
1405 if (tree_view
->priv
->typeselect_flush_timeout
)
1407 g_source_remove (tree_view
->priv
->typeselect_flush_timeout
);
1408 tree_view
->priv
->typeselect_flush_timeout
= 0;
1412 if (tree_view
->priv
->search_destroy
&& tree_view
->priv
->search_user_data
)
1414 tree_view
->priv
->search_destroy (tree_view
->priv
->search_user_data
);
1415 tree_view
->priv
->search_user_data
= NULL
;
1418 if (tree_view
->priv
->search_position_destroy
&& tree_view
->priv
->search_position_user_data
)
1420 tree_view
->priv
->search_position_destroy (tree_view
->priv
->search_position_user_data
);
1421 tree_view
->priv
->search_position_user_data
= NULL
;
1424 pspp_sheet_view_set_model (tree_view
, NULL
);
1427 G_OBJECT_CLASS (pspp_sheet_view_parent_class
)->finalize (object
);
1432 /* GtkWidget Methods
1435 /* GtkWidget::map helper */
1437 pspp_sheet_view_map_buttons (PsppSheetView
*tree_view
)
1441 g_return_if_fail (gtk_widget_get_mapped (GTK_WIDGET (tree_view
)));
1443 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view
, PSPP_SHEET_VIEW_HEADERS_VISIBLE
))
1445 PsppSheetViewColumn
*column
;
1447 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
1449 column
= list
->data
;
1450 if (column
->button
!= NULL
&&
1451 gtk_widget_get_visible (column
->button
) &&
1452 !gtk_widget_get_mapped (column
->button
))
1453 gtk_widget_map (column
->button
);
1455 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
1457 column
= list
->data
;
1458 if (column
->visible
== FALSE
|| column
->window
== NULL
)
1460 if (column
->resizable
)
1462 gdk_window_raise (column
->window
);
1463 gdk_window_show (column
->window
);
1466 gdk_window_hide (column
->window
);
1468 gdk_window_show (tree_view
->priv
->header_window
);
1473 pspp_sheet_view_map (GtkWidget
*widget
)
1475 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
1478 gtk_widget_set_mapped (widget
, TRUE
);
1480 tmp_list
= tree_view
->priv
->children
;
1483 PsppSheetViewChild
*child
= tmp_list
->data
;
1484 tmp_list
= tmp_list
->next
;
1486 if (gtk_widget_get_visible (child
->widget
))
1488 if (!gtk_widget_get_mapped (child
->widget
))
1489 gtk_widget_map (child
->widget
);
1492 gdk_window_show (tree_view
->priv
->bin_window
);
1494 pspp_sheet_view_map_buttons (tree_view
);
1496 gdk_window_show (gtk_widget_get_window (widget
));
1500 pspp_sheet_view_realize (GtkWidget
*widget
)
1502 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
1504 GdkWindowAttr attributes
;
1505 gint attributes_mask
;
1506 GtkAllocation allocation
;
1507 GtkAllocation old_allocation
;
1509 gtk_widget_set_realized (widget
, TRUE
);
1511 gtk_widget_get_allocation (widget
, &allocation
);
1512 gtk_widget_get_allocation (widget
, &old_allocation
);
1514 /* Make the main, clipping window */
1515 attributes
.window_type
= GDK_WINDOW_CHILD
;
1516 attributes
.x
= allocation
.x
;
1517 attributes
.y
= allocation
.y
;
1518 attributes
.width
= allocation
.width
;
1519 attributes
.height
= allocation
.height
;
1520 attributes
.wclass
= GDK_INPUT_OUTPUT
;
1521 attributes
.visual
= gtk_widget_get_visual (widget
);
1522 attributes
.event_mask
= GDK_VISIBILITY_NOTIFY_MASK
;
1524 attributes_mask
= GDK_WA_X
| GDK_WA_Y
| GDK_WA_VISUAL
;
1526 gtk_widget_set_window (widget
,
1527 gdk_window_new (gtk_widget_get_parent_window (widget
),
1528 &attributes
, attributes_mask
));
1529 gdk_window_set_user_data (gtk_widget_get_window (widget
), widget
);
1531 /* Make the window for the tree */
1533 attributes
.y
= TREE_VIEW_HEADER_HEIGHT (tree_view
);
1534 attributes
.width
= MAX (tree_view
->priv
->width
, old_allocation
.width
);
1535 attributes
.height
= old_allocation
.height
;
1536 attributes
.event_mask
= (GDK_EXPOSURE_MASK
|
1538 GDK_POINTER_MOTION_MASK
|
1539 GDK_ENTER_NOTIFY_MASK
|
1540 GDK_LEAVE_NOTIFY_MASK
|
1541 GDK_BUTTON_PRESS_MASK
|
1542 GDK_BUTTON_RELEASE_MASK
|
1543 gtk_widget_get_events (widget
));
1545 tree_view
->priv
->bin_window
= gdk_window_new (gtk_widget_get_window (widget
),
1546 &attributes
, attributes_mask
);
1547 gdk_window_set_user_data (tree_view
->priv
->bin_window
, widget
);
1549 /* Make the column header window */
1552 attributes
.width
= MAX (tree_view
->priv
->width
, old_allocation
.width
);
1553 attributes
.height
= tree_view
->priv
->header_height
;
1554 attributes
.event_mask
= (GDK_EXPOSURE_MASK
|
1556 GDK_BUTTON_PRESS_MASK
|
1557 GDK_BUTTON_RELEASE_MASK
|
1558 GDK_KEY_PRESS_MASK
|
1559 GDK_KEY_RELEASE_MASK
|
1560 gtk_widget_get_events (widget
));
1562 tree_view
->priv
->header_window
= gdk_window_new (gtk_widget_get_window (widget
),
1563 &attributes
, attributes_mask
);
1564 gdk_window_set_user_data (tree_view
->priv
->header_window
, widget
);
1566 /* Add them all up. */
1567 gtk_widget_set_style (widget
,
1568 gtk_style_attach (gtk_widget_get_style (widget
), gtk_widget_get_window (widget
)));
1569 gdk_window_set_background (tree_view
->priv
->bin_window
, >k_widget_get_style (widget
)->base
[gtk_widget_get_state (widget
)]);
1570 gtk_style_set_background (gtk_widget_get_style (widget
), tree_view
->priv
->header_window
, GTK_STATE_NORMAL
);
1572 tmp_list
= tree_view
->priv
->children
;
1575 PsppSheetViewChild
*child
= tmp_list
->data
;
1576 tmp_list
= tmp_list
->next
;
1578 gtk_widget_set_parent_window (child
->widget
, tree_view
->priv
->bin_window
);
1581 for (tmp_list
= tree_view
->priv
->columns
; tmp_list
; tmp_list
= tmp_list
->next
)
1582 _pspp_sheet_view_column_realize_button (PSPP_SHEET_VIEW_COLUMN (tmp_list
->data
));
1584 /* Need to call those here, since they create GCs */
1585 pspp_sheet_view_set_grid_lines (tree_view
, tree_view
->priv
->grid_lines
);
1587 install_presize_handler (tree_view
);
1591 pspp_sheet_view_unrealize (GtkWidget
*widget
)
1593 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
1594 PsppSheetViewPrivate
*priv
= tree_view
->priv
;
1597 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class
)->unrealize (widget
);
1599 if (priv
->scroll_timeout
!= 0)
1601 g_source_remove (priv
->scroll_timeout
);
1602 priv
->scroll_timeout
= 0;
1605 if (priv
->open_dest_timeout
!= 0)
1607 g_source_remove (priv
->open_dest_timeout
);
1608 priv
->open_dest_timeout
= 0;
1611 if (priv
->presize_handler_timer
!= 0)
1613 g_source_remove (priv
->presize_handler_timer
);
1614 priv
->presize_handler_timer
= 0;
1617 if (priv
->validate_rows_timer
!= 0)
1619 g_source_remove (priv
->validate_rows_timer
);
1620 priv
->validate_rows_timer
= 0;
1623 if (priv
->scroll_sync_timer
!= 0)
1625 g_source_remove (priv
->scroll_sync_timer
);
1626 priv
->scroll_sync_timer
= 0;
1629 if (priv
->typeselect_flush_timeout
)
1631 g_source_remove (priv
->typeselect_flush_timeout
);
1632 priv
->typeselect_flush_timeout
= 0;
1635 for (list
= priv
->columns
; list
; list
= list
->next
)
1636 _pspp_sheet_view_column_unrealize_button (PSPP_SHEET_VIEW_COLUMN (list
->data
));
1638 gdk_window_set_user_data (priv
->bin_window
, NULL
);
1639 gdk_window_destroy (priv
->bin_window
);
1640 priv
->bin_window
= NULL
;
1642 gdk_window_set_user_data (priv
->header_window
, NULL
);
1643 gdk_window_destroy (priv
->header_window
);
1644 priv
->header_window
= NULL
;
1646 if (priv
->drag_window
)
1648 gdk_window_set_user_data (priv
->drag_window
, NULL
);
1649 gdk_window_destroy (priv
->drag_window
);
1650 priv
->drag_window
= NULL
;
1653 if (priv
->drag_highlight_window
)
1655 gdk_window_set_user_data (priv
->drag_highlight_window
, NULL
);
1656 gdk_window_destroy (priv
->drag_highlight_window
);
1657 priv
->drag_highlight_window
= NULL
;
1660 if (tree_view
->priv
->columns
!= NULL
)
1662 list
= tree_view
->priv
->columns
;
1665 PsppSheetViewColumn
*column
;
1666 column
= PSPP_SHEET_VIEW_COLUMN (list
->data
);
1668 pspp_sheet_view_remove_column (tree_view
, column
);
1670 tree_view
->priv
->columns
= NULL
;
1674 /* GtkWidget::size_request helper */
1676 pspp_sheet_view_size_request_columns (PsppSheetView
*tree_view
)
1680 tree_view
->priv
->header_height
= 0;
1682 if (tree_view
->priv
->model
)
1684 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
1686 GtkRequisition requisition
;
1687 PsppSheetViewColumn
*column
= list
->data
;
1689 pspp_sheet_view_column_size_request (column
, &requisition
);
1690 column
->button_request
= requisition
.width
;
1691 tree_view
->priv
->header_height
= MAX (tree_view
->priv
->header_height
, requisition
.height
);
1697 /* Called only by ::size_request */
1699 pspp_sheet_view_update_size (PsppSheetView
*tree_view
)
1702 PsppSheetViewColumn
*column
;
1705 if (tree_view
->priv
->model
== NULL
)
1707 tree_view
->priv
->width
= 0;
1708 tree_view
->priv
->prev_width
= 0;
1709 tree_view
->priv
->height
= 0;
1713 tree_view
->priv
->prev_width
= tree_view
->priv
->width
;
1714 tree_view
->priv
->width
= 0;
1716 /* keep this in sync with size_allocate below */
1717 for (list
= tree_view
->priv
->columns
, i
= 0; list
; list
= list
->next
, i
++)
1719 gint real_requested_width
= 0;
1720 column
= list
->data
;
1721 if (!column
->visible
)
1724 if (column
->use_resized_width
)
1726 real_requested_width
= column
->resized_width
;
1730 real_requested_width
= column
->fixed_width
;
1733 if (column
->min_width
!= -1)
1734 real_requested_width
= MAX (real_requested_width
, column
->min_width
);
1735 if (column
->max_width
!= -1)
1736 real_requested_width
= MIN (real_requested_width
, column
->max_width
);
1738 tree_view
->priv
->width
+= real_requested_width
;
1741 tree_view
->priv
->height
= tree_view
->priv
->fixed_height
* tree_view
->priv
->row_count
;
1745 pspp_sheet_view_size_request (GtkWidget
*widget
,
1746 GtkRequisition
*requisition
)
1748 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
1751 /* we validate some rows initially just to make sure we have some size.
1752 * In practice, with a lot of static lists, this should get a good width.
1754 initialize_fixed_height_mode (tree_view
);
1755 pspp_sheet_view_size_request_columns (tree_view
);
1756 pspp_sheet_view_update_size (PSPP_SHEET_VIEW (widget
));
1758 requisition
->width
= tree_view
->priv
->width
;
1759 requisition
->height
= tree_view
->priv
->height
+ TREE_VIEW_HEADER_HEIGHT (tree_view
);
1761 tmp_list
= tree_view
->priv
->children
;
1765 PsppSheetViewChild
*child
= tmp_list
->data
;
1766 GtkRequisition child_requisition
;
1768 tmp_list
= tmp_list
->next
;
1770 if (gtk_widget_get_visible (child
->widget
))
1771 gtk_widget_size_request (child
->widget
, &child_requisition
);
1776 invalidate_column (PsppSheetView
*tree_view
,
1777 PsppSheetViewColumn
*column
)
1779 gint column_offset
= 0;
1781 GtkWidget
*widget
= GTK_WIDGET (tree_view
);
1784 if (!gtk_widget_get_realized (widget
))
1787 rtl
= (gtk_widget_get_direction (GTK_WIDGET (tree_view
)) == GTK_TEXT_DIR_RTL
);
1788 for (list
= (rtl
? g_list_last (tree_view
->priv
->columns
) : g_list_first (tree_view
->priv
->columns
));
1790 list
= (rtl
? list
->prev
: list
->next
))
1792 PsppSheetViewColumn
*tmpcolumn
= list
->data
;
1793 if (tmpcolumn
== column
)
1795 GdkRectangle invalid_rect
;
1796 GtkAllocation allocation
;
1798 gtk_widget_get_allocation (widget
, &allocation
);
1799 invalid_rect
.x
= column_offset
;
1801 invalid_rect
.width
= column
->width
;
1802 invalid_rect
.height
= allocation
.height
;
1804 gdk_window_invalidate_rect (gtk_widget_get_window (widget
), &invalid_rect
, TRUE
);
1808 column_offset
+= tmpcolumn
->width
;
1813 invalidate_last_column (PsppSheetView
*tree_view
)
1818 rtl
= (gtk_widget_get_direction (GTK_WIDGET (tree_view
)) == GTK_TEXT_DIR_RTL
);
1820 for (last_column
= (rtl
? g_list_first (tree_view
->priv
->columns
) : g_list_last (tree_view
->priv
->columns
));
1822 last_column
= (rtl
? last_column
->next
: last_column
->prev
))
1824 if (PSPP_SHEET_VIEW_COLUMN (last_column
->data
)->visible
)
1826 invalidate_column (tree_view
, last_column
->data
);
1833 pspp_sheet_view_get_real_requested_width_from_column (PsppSheetView
*tree_view
,
1834 PsppSheetViewColumn
*column
)
1836 gint real_requested_width
;
1838 if (column
->use_resized_width
)
1840 real_requested_width
= column
->resized_width
;
1844 real_requested_width
= column
->fixed_width
;
1847 if (column
->min_width
!= -1)
1848 real_requested_width
= MAX (real_requested_width
, column
->min_width
);
1849 if (column
->max_width
!= -1)
1850 real_requested_width
= MIN (real_requested_width
, column
->max_width
);
1852 return real_requested_width
;
1856 span_intersects (int a0
, int a_width
,
1857 int b0
, int b_width
)
1859 int a1
= a0
+ a_width
;
1860 int b1
= b0
+ b_width
;
1861 return (a0
>= b0
&& a0
< b1
) || (b0
>= a0
&& b0
< a1
);
1864 /* GtkWidget::size_allocate helper */
1866 pspp_sheet_view_size_allocate_columns (GtkWidget
*widget
,
1867 gboolean
*width_changed
)
1869 PsppSheetView
*tree_view
;
1870 GList
*list
, *first_column
, *last_column
;
1871 PsppSheetViewColumn
*column
;
1872 GtkAllocation col_allocation
;
1873 GtkAllocation allocation
;
1875 gint extra
, extra_per_column
;
1876 gint full_requested_width
= 0;
1877 gint number_of_expand_columns
= 0;
1878 gboolean column_changed
= FALSE
;
1881 tree_view
= PSPP_SHEET_VIEW (widget
);
1883 for (last_column
= g_list_last (tree_view
->priv
->columns
);
1884 last_column
&& !(PSPP_SHEET_VIEW_COLUMN (last_column
->data
)->visible
);
1885 last_column
= last_column
->prev
)
1888 if (last_column
== NULL
)
1891 for (first_column
= g_list_first (tree_view
->priv
->columns
);
1892 first_column
&& !(PSPP_SHEET_VIEW_COLUMN (first_column
->data
)->visible
);
1893 first_column
= first_column
->next
)
1896 col_allocation
.y
= 0;
1897 col_allocation
.height
= tree_view
->priv
->header_height
;
1899 rtl
= (gtk_widget_get_direction (widget
) == GTK_TEXT_DIR_RTL
);
1901 /* find out how many extra space and expandable columns we have */
1902 for (list
= tree_view
->priv
->columns
; list
!= last_column
->next
; list
= list
->next
)
1904 column
= (PsppSheetViewColumn
*)list
->data
;
1906 if (!column
->visible
)
1909 full_requested_width
+= pspp_sheet_view_get_real_requested_width_from_column (tree_view
, column
);
1912 number_of_expand_columns
++;
1915 gtk_widget_get_allocation (widget
, &allocation
);
1916 extra
= MAX (allocation
.width
- full_requested_width
, 0);
1917 if (number_of_expand_columns
> 0)
1918 extra_per_column
= extra
/number_of_expand_columns
;
1920 extra_per_column
= 0;
1922 for (list
= (rtl
? last_column
: first_column
);
1923 list
!= (rtl
? first_column
->prev
: last_column
->next
);
1924 list
= (rtl
? list
->prev
: list
->next
))
1926 gint real_requested_width
= 0;
1929 column
= list
->data
;
1930 old_width
= column
->width
;
1932 if (!column
->visible
)
1935 /* We need to handle the dragged button specially.
1937 if (column
== tree_view
->priv
->drag_column
)
1939 GtkAllocation drag_allocation
;
1940 drag_allocation
.width
= gdk_window_get_width (tree_view
->priv
->drag_window
);
1941 drag_allocation
.height
= gdk_window_get_height (tree_view
->priv
->drag_window
);
1942 drag_allocation
.x
= 0;
1943 drag_allocation
.y
= 0;
1944 pspp_sheet_view_column_size_allocate (tree_view
->priv
->drag_column
,
1946 width
+= drag_allocation
.width
;
1950 real_requested_width
= pspp_sheet_view_get_real_requested_width_from_column (tree_view
, column
);
1952 col_allocation
.x
= width
;
1953 column
->width
= real_requested_width
;
1957 if (number_of_expand_columns
== 1)
1959 /* We add the remander to the last column as
1961 column
->width
+= extra
;
1965 column
->width
+= extra_per_column
;
1966 extra
-= extra_per_column
;
1967 number_of_expand_columns
--;
1971 if (column
->width
!= old_width
)
1972 g_object_notify (G_OBJECT (column
), "width");
1974 col_allocation
.width
= column
->width
;
1975 width
+= column
->width
;
1977 if (column
->width
> old_width
)
1978 column_changed
= TRUE
;
1980 pspp_sheet_view_column_size_allocate (column
, &col_allocation
);
1983 gdk_window_move_resize (column
->window
,
1984 col_allocation
.x
+ (rtl
? 0 : col_allocation
.width
) - TREE_VIEW_DRAG_WIDTH
/2,
1986 TREE_VIEW_DRAG_WIDTH
, col_allocation
.height
);
1989 /* We change the width here. The user might have been resizing columns,
1990 * so the total width of the tree view changes.
1992 tree_view
->priv
->width
= width
;
1994 *width_changed
= TRUE
;
1997 gtk_widget_queue_draw (GTK_WIDGET (tree_view
));
2001 pspp_sheet_view_size_allocate (GtkWidget
*widget
,
2002 GtkAllocation
*allocation
)
2004 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
2006 gboolean width_changed
= FALSE
;
2007 GtkAllocation old_allocation
;
2008 gtk_widget_get_allocation (widget
, &old_allocation
);
2010 if (allocation
->width
!= old_allocation
.width
)
2011 width_changed
= TRUE
;
2014 gtk_widget_set_allocation (widget
, allocation
);
2016 tmp_list
= tree_view
->priv
->children
;
2020 GtkAllocation allocation
;
2022 PsppSheetViewChild
*child
= tmp_list
->data
;
2023 tmp_list
= tmp_list
->next
;
2025 /* totally ignore our child's requisition */
2026 allocation
.x
= child
->x
;
2027 allocation
.y
= child
->y
;
2028 allocation
.width
= child
->width
;
2029 allocation
.height
= child
->height
;
2030 gtk_widget_size_allocate (child
->widget
, &allocation
);
2033 /* We size-allocate the columns first because the width of the
2034 * tree view (used in updating the adjustments below) might change.
2036 pspp_sheet_view_size_allocate_columns (widget
, &width_changed
);
2038 gtk_adjustment_set_page_size (tree_view
->priv
->hadjustment
, allocation
->width
);
2039 gtk_adjustment_set_page_increment (tree_view
->priv
->hadjustment
, allocation
->width
* 0.9);
2040 gtk_adjustment_set_step_increment (tree_view
->priv
->hadjustment
, allocation
->width
* 0.1);
2041 gtk_adjustment_set_lower (tree_view
->priv
->hadjustment
, 0);
2042 gtk_adjustment_set_upper (tree_view
->priv
->hadjustment
, MAX (gtk_adjustment_get_page_size (tree_view
->priv
->hadjustment
), tree_view
->priv
->width
));
2044 if (gtk_widget_get_direction(widget
) == GTK_TEXT_DIR_RTL
)
2046 if (allocation
->width
< tree_view
->priv
->width
)
2048 if (tree_view
->priv
->init_hadjust_value
)
2050 gtk_adjustment_set_value (tree_view
->priv
->hadjustment
, MAX (tree_view
->priv
->width
- allocation
->width
, 0));
2051 tree_view
->priv
->init_hadjust_value
= FALSE
;
2053 else if (allocation
->width
!= old_allocation
.width
)
2055 gtk_adjustment_set_value (tree_view
->priv
->hadjustment
, CLAMP (gtk_adjustment_get_value (tree_view
->priv
->hadjustment
) - allocation
->width
+ old_allocation
.width
, 0, tree_view
->priv
->width
- allocation
->width
));
2058 gtk_adjustment_set_value (tree_view
->priv
->hadjustment
, CLAMP (tree_view
->priv
->width
- (tree_view
->priv
->prev_width
- gtk_adjustment_get_value (tree_view
->priv
->hadjustment
)), 0, tree_view
->priv
->width
- allocation
->width
));
2062 gtk_adjustment_set_value (tree_view
->priv
->hadjustment
, 0);
2063 tree_view
->priv
->init_hadjust_value
= TRUE
;
2067 if (gtk_adjustment_get_value (tree_view
->priv
->hadjustment
) + allocation
->width
> tree_view
->priv
->width
)
2068 gtk_adjustment_set_value (tree_view
->priv
->hadjustment
, MAX (tree_view
->priv
->width
- allocation
->width
, 0));
2070 gtk_adjustment_changed (tree_view
->priv
->hadjustment
);
2072 gtk_adjustment_set_page_size (tree_view
->priv
->vadjustment
, allocation
->height
- TREE_VIEW_HEADER_HEIGHT (tree_view
));
2073 gtk_adjustment_set_step_increment (tree_view
->priv
->vadjustment
, gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
) * 0.1);
2074 gtk_adjustment_set_page_increment (tree_view
->priv
->vadjustment
, gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
) * 0.9);
2075 gtk_adjustment_set_lower (tree_view
->priv
->vadjustment
, 0);
2076 gtk_adjustment_set_upper (tree_view
->priv
->vadjustment
, MAX (gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
), tree_view
->priv
->height
));
2078 gtk_adjustment_changed (tree_view
->priv
->vadjustment
);
2080 /* now the adjustments and window sizes are in sync, we can sync toprow/dy again */
2081 if (tree_view
->priv
->height
<= gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
))
2082 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view
->priv
->vadjustment
), 0);
2083 else if (gtk_adjustment_get_value (tree_view
->priv
->vadjustment
) + gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
) > tree_view
->priv
->height
)
2084 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view
->priv
->vadjustment
),
2085 tree_view
->priv
->height
- gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
));
2086 else if (gtk_tree_row_reference_valid (tree_view
->priv
->top_row
))
2087 pspp_sheet_view_top_row_to_dy (tree_view
);
2089 pspp_sheet_view_dy_to_top_row (tree_view
);
2091 if (gtk_widget_get_realized (widget
))
2093 gdk_window_move_resize (gtk_widget_get_window (widget
),
2094 allocation
->x
, allocation
->y
,
2095 allocation
->width
, allocation
->height
);
2096 gdk_window_move_resize (tree_view
->priv
->header_window
,
2097 - (gint
) gtk_adjustment_get_value (tree_view
->priv
->hadjustment
),
2099 MAX (tree_view
->priv
->width
, allocation
->width
),
2100 tree_view
->priv
->header_height
);
2101 gdk_window_move_resize (tree_view
->priv
->bin_window
,
2102 - (gint
) gtk_adjustment_get_value (tree_view
->priv
->hadjustment
),
2103 TREE_VIEW_HEADER_HEIGHT (tree_view
),
2104 MAX (tree_view
->priv
->width
, allocation
->width
),
2105 allocation
->height
- TREE_VIEW_HEADER_HEIGHT (tree_view
));
2108 if (tree_view
->priv
->row_count
== 0)
2109 invalidate_empty_focus (tree_view
);
2111 if (gtk_widget_get_realized (widget
))
2113 gboolean has_expand_column
= FALSE
;
2114 for (tmp_list
= tree_view
->priv
->columns
; tmp_list
; tmp_list
= tmp_list
->next
)
2116 if (pspp_sheet_view_column_get_expand (PSPP_SHEET_VIEW_COLUMN (tmp_list
->data
)))
2118 has_expand_column
= TRUE
;
2123 /* This little hack only works if we have an LTR locale, and no column has the */
2126 if (gtk_widget_get_direction (GTK_WIDGET (tree_view
)) == GTK_TEXT_DIR_LTR
&&
2127 ! has_expand_column
)
2128 invalidate_last_column (tree_view
);
2130 gtk_widget_queue_draw (widget
);
2135 /* Grabs the focus and unsets the PSPP_SHEET_VIEW_DRAW_KEYFOCUS flag */
2137 grab_focus_and_unset_draw_keyfocus (PsppSheetView
*tree_view
)
2139 GtkWidget
*widget
= GTK_WIDGET (tree_view
);
2141 if (gtk_widget_get_can_focus (widget
) && !gtk_widget_has_focus (widget
))
2142 gtk_widget_grab_focus (widget
);
2143 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view
, PSPP_SHEET_VIEW_DRAW_KEYFOCUS
);
2147 pspp_sheet_view_node_is_selected (PsppSheetView
*tree_view
,
2150 return node
>= 0 && range_tower_contains (tree_view
->priv
->selected
, node
);
2154 pspp_sheet_view_node_select (PsppSheetView
*tree_view
,
2157 range_tower_set1 (tree_view
->priv
->selected
, node
, 1);
2161 pspp_sheet_view_node_unselect (PsppSheetView
*tree_view
,
2164 range_tower_set0 (tree_view
->priv
->selected
, node
, 1);
2168 pspp_sheet_view_node_next (PsppSheetView
*tree_view
,
2171 return node
+ 1 < tree_view
->priv
->row_count
? node
+ 1 : -1;
2175 pspp_sheet_view_node_prev (PsppSheetView
*tree_view
,
2178 return node
> 0 ? node
- 1 : -1;
2182 all_columns_selected (PsppSheetView
*tree_view
)
2186 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
2188 PsppSheetViewColumn
*column
= list
->data
;
2189 if (column
->selectable
&& !column
->selected
)
2197 pspp_sheet_view_row_head_clicked (PsppSheetView
*tree_view
,
2199 PsppSheetViewColumn
*column
,
2200 GdkEventButton
*event
)
2202 PsppSheetSelection
*selection
;
2203 PsppSheetSelectionMode mode
;
2205 gboolean update_anchor
;
2209 g_return_val_if_fail (tree_view
!= NULL
, FALSE
);
2210 g_return_val_if_fail (column
!= NULL
, FALSE
);
2212 selection
= tree_view
->priv
->selection
;
2213 mode
= pspp_sheet_selection_get_mode (selection
);
2214 if (mode
!= PSPP_SHEET_SELECTION_RECTANGLE
)
2217 if (!column
->row_head
)
2222 modifiers
= event
->state
& gtk_accelerator_get_default_mod_mask ();
2223 if (event
->type
!= GDK_BUTTON_PRESS
2224 || (modifiers
!= GDK_CONTROL_MASK
&& modifiers
!= GDK_SHIFT_MASK
))
2230 path
= gtk_tree_path_new_from_indices (node
, -1);
2233 pspp_sheet_selection_unselect_all (selection
);
2234 pspp_sheet_selection_select_path (selection
, path
);
2235 pspp_sheet_selection_select_all_columns (selection
);
2236 update_anchor
= TRUE
;
2239 else if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 3)
2241 if (pspp_sheet_selection_count_selected_rows (selection
) <= 1
2242 || !all_columns_selected (tree_view
))
2244 pspp_sheet_selection_unselect_all (selection
);
2245 pspp_sheet_selection_select_path (selection
, path
);
2246 pspp_sheet_selection_select_all_columns (selection
);
2247 update_anchor
= TRUE
;
2251 update_anchor
= handled
= FALSE
;
2253 else if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 1
2254 && modifiers
== GDK_CONTROL_MASK
)
2256 if (!all_columns_selected (tree_view
))
2258 pspp_sheet_selection_unselect_all (selection
);
2259 pspp_sheet_selection_select_all_columns (selection
);
2262 if (pspp_sheet_selection_path_is_selected (selection
, path
))
2263 pspp_sheet_selection_unselect_path (selection
, path
);
2265 pspp_sheet_selection_select_path (selection
, path
);
2266 update_anchor
= TRUE
;
2269 else if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 1
2270 && modifiers
== GDK_SHIFT_MASK
)
2272 GtkTreeRowReference
*anchor
= tree_view
->priv
->anchor
;
2273 GtkTreePath
*anchor_path
;
2275 if (all_columns_selected (tree_view
)
2276 && gtk_tree_row_reference_valid (anchor
))
2278 update_anchor
= FALSE
;
2279 anchor_path
= gtk_tree_row_reference_get_path (anchor
);
2283 update_anchor
= TRUE
;
2284 anchor_path
= gtk_tree_path_copy (path
);
2287 pspp_sheet_selection_unselect_all (selection
);
2288 pspp_sheet_selection_select_range (selection
, anchor_path
, path
);
2289 pspp_sheet_selection_select_all_columns (selection
);
2291 gtk_tree_path_free (anchor_path
);
2296 update_anchor
= handled
= FALSE
;
2300 if (tree_view
->priv
->anchor
)
2301 gtk_tree_row_reference_free (tree_view
->priv
->anchor
);
2302 tree_view
->priv
->anchor
=
2303 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view
),
2304 tree_view
->priv
->model
,
2308 gtk_tree_path_free (path
);
2313 find_click (PsppSheetView
*tree_view
,
2316 PsppSheetViewColumn
**column
,
2317 GdkRectangle
*background_area
,
2318 GdkRectangle
*cell_area
)
2325 /* find the node that was clicked */
2326 new_y
= TREE_WINDOW_Y_TO_RBTREE_Y(tree_view
, y
);
2329 y_offset
= -pspp_sheet_view_find_offset (tree_view
, new_y
, node
);
2334 background_area
->y
= y_offset
+ y
;
2335 background_area
->height
= ROW_HEIGHT (tree_view
);
2336 background_area
->x
= 0;
2338 /* Let the column have a chance at selecting it. */
2339 rtl
= (gtk_widget_get_direction (GTK_WIDGET (tree_view
)) == GTK_TEXT_DIR_RTL
);
2340 for (list
= (rtl
? g_list_last (tree_view
->priv
->columns
) : g_list_first (tree_view
->priv
->columns
));
2341 list
; list
= (rtl
? list
->prev
: list
->next
))
2343 PsppSheetViewColumn
*candidate
= list
->data
;
2345 if (!candidate
->visible
)
2348 background_area
->width
= candidate
->width
;
2349 if ((background_area
->x
> x
) ||
2350 (background_area
->x
+ background_area
->width
<= x
))
2352 background_area
->x
+= background_area
->width
;
2356 /* we found the focus column */
2358 pspp_sheet_view_adjust_cell_area (tree_view
, candidate
, background_area
,
2360 *column
= candidate
;
2368 pspp_sheet_view_button_press (GtkWidget
*widget
,
2369 GdkEventButton
*event
)
2371 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
2373 PsppSheetViewColumn
*column
= NULL
;
2375 GdkRectangle background_area
;
2376 GdkRectangle cell_area
;
2379 rtl
= (gtk_widget_get_direction (widget
) == GTK_TEXT_DIR_RTL
);
2380 pspp_sheet_view_stop_editing (tree_view
, FALSE
);
2383 /* Because grab_focus can cause reentrancy, we delay grab_focus until after
2384 * we're done handling the button press.
2387 if (event
->window
== tree_view
->priv
->bin_window
)
2392 gint pre_val
, aft_val
;
2393 PsppSheetViewColumn
*column
= NULL
;
2394 GtkCellRenderer
*focus_cell
= NULL
;
2395 gboolean row_double_click
= FALSE
;
2398 if (tree_view
->priv
->row_count
== 0)
2400 grab_focus_and_unset_draw_keyfocus (tree_view
);
2404 if (!find_click (tree_view
, event
->x
, event
->y
, &node
, &column
,
2405 &background_area
, &cell_area
))
2407 grab_focus_and_unset_draw_keyfocus (tree_view
);
2411 tree_view
->priv
->focus_column
= column
;
2413 if (pspp_sheet_view_row_head_clicked (tree_view
, node
, column
, event
))
2417 pre_val
= gtk_adjustment_get_value (tree_view
->priv
->vadjustment
);
2419 path
= _pspp_sheet_view_find_path (tree_view
, node
);
2421 /* we only handle selection modifications on the first button press
2423 if (event
->type
== GDK_BUTTON_PRESS
)
2425 PsppSheetSelectionMode mode
= 0;
2427 if ((event
->state
& GDK_CONTROL_MASK
) == GDK_CONTROL_MASK
)
2428 mode
|= PSPP_SHEET_SELECT_MODE_TOGGLE
;
2429 if ((event
->state
& GDK_SHIFT_MASK
) == GDK_SHIFT_MASK
)
2430 mode
|= PSPP_SHEET_SELECT_MODE_EXTEND
;
2432 focus_cell
= _pspp_sheet_view_column_get_cell_at_pos (column
, event
->x
- background_area
.x
);
2434 pspp_sheet_view_column_focus_cell (column
, focus_cell
);
2436 if (event
->state
& GDK_CONTROL_MASK
)
2438 pspp_sheet_view_real_set_cursor (tree_view
, path
, FALSE
, TRUE
, mode
);
2439 pspp_sheet_view_real_toggle_cursor_row (tree_view
);
2441 else if (event
->state
& GDK_SHIFT_MASK
)
2443 pspp_sheet_view_real_set_cursor (tree_view
, path
, TRUE
, TRUE
, mode
);
2444 pspp_sheet_view_real_select_cursor_row (tree_view
, FALSE
, mode
);
2448 pspp_sheet_view_real_set_cursor (tree_view
, path
, TRUE
, TRUE
, 0);
2451 if (tree_view
->priv
->anchor_column
== NULL
||
2452 !(event
->state
& GDK_SHIFT_MASK
))
2453 tree_view
->priv
->anchor_column
= column
;
2454 pspp_sheet_selection_unselect_all_columns (tree_view
->priv
->selection
);
2455 pspp_sheet_selection_select_column_range (tree_view
->priv
->selection
,
2456 tree_view
->priv
->anchor_column
,
2460 /* the treeview may have been scrolled because of _set_cursor,
2464 aft_val
= gtk_adjustment_get_value (tree_view
->priv
->vadjustment
);
2465 dval
= pre_val
- aft_val
;
2467 cell_area
.y
+= dval
;
2468 background_area
.y
+= dval
;
2470 /* Save press to possibly begin a drag
2472 if (!tree_view
->priv
->in_grab
&&
2473 tree_view
->priv
->pressed_button
< 0)
2475 tree_view
->priv
->pressed_button
= event
->button
;
2476 tree_view
->priv
->press_start_x
= event
->x
;
2477 tree_view
->priv
->press_start_y
= event
->y
;
2478 tree_view
->priv
->press_start_node
= node
;
2480 if (tree_view
->priv
->rubber_banding_enable
2481 && (tree_view
->priv
->selection
->type
== PSPP_SHEET_SELECTION_MULTIPLE
||
2482 tree_view
->priv
->selection
->type
== PSPP_SHEET_SELECTION_RECTANGLE
))
2484 tree_view
->priv
->press_start_y
+= tree_view
->priv
->dy
;
2485 tree_view
->priv
->rubber_band_x
= event
->x
;
2486 tree_view
->priv
->rubber_band_y
= event
->y
+ tree_view
->priv
->dy
;
2487 tree_view
->priv
->rubber_band_status
= RUBBER_BAND_MAYBE_START
;
2489 if ((event
->state
& GDK_CONTROL_MASK
) == GDK_CONTROL_MASK
)
2490 tree_view
->priv
->rubber_band_ctrl
= TRUE
;
2491 if ((event
->state
& GDK_SHIFT_MASK
) == GDK_SHIFT_MASK
)
2492 tree_view
->priv
->rubber_band_shift
= TRUE
;
2497 /* Test if a double click happened on the same row. */
2498 if (event
->button
== 1 && event
->type
== GDK_BUTTON_PRESS
)
2500 int double_click_time
, double_click_distance
;
2502 g_object_get (gtk_settings_get_for_screen (
2503 gtk_widget_get_screen (widget
)),
2504 "gtk-double-click-time", &double_click_time
,
2505 "gtk-double-click-distance", &double_click_distance
,
2508 /* Same conditions as _gdk_event_button_generate */
2509 if (tree_view
->priv
->last_button_x
!= -1 &&
2510 (event
->time
< tree_view
->priv
->last_button_time
+ double_click_time
) &&
2511 (ABS (event
->x
- tree_view
->priv
->last_button_x
) <= double_click_distance
) &&
2512 (ABS (event
->y
- tree_view
->priv
->last_button_y
) <= double_click_distance
))
2514 /* We do no longer compare paths of this row and the
2515 * row clicked previously. We use the double click
2516 * distance to decide whether this is a valid click,
2517 * allowing the mouse to slightly move over another row.
2519 row_double_click
= TRUE
;
2521 tree_view
->priv
->last_button_time
= 0;
2522 tree_view
->priv
->last_button_x
= -1;
2523 tree_view
->priv
->last_button_y
= -1;
2527 tree_view
->priv
->last_button_time
= event
->time
;
2528 tree_view
->priv
->last_button_x
= event
->x
;
2529 tree_view
->priv
->last_button_y
= event
->y
;
2533 if (row_double_click
)
2535 gtk_grab_remove (widget
);
2536 pspp_sheet_view_row_activated (tree_view
, path
, column
);
2538 if (tree_view
->priv
->pressed_button
== event
->button
)
2539 tree_view
->priv
->pressed_button
= -1;
2542 gtk_tree_path_free (path
);
2544 /* If we activated the row through a double click we don't want to grab
2545 * focus back, as moving focus to another widget is pretty common.
2547 if (!row_double_click
)
2548 grab_focus_and_unset_draw_keyfocus (tree_view
);
2553 /* We didn't click in the window. Let's check to see if we clicked on a column resize window.
2555 for (i
= 0, list
= tree_view
->priv
->columns
; list
; list
= list
->next
, i
++)
2557 column
= list
->data
;
2558 if (event
->window
== column
->window
&&
2559 column
->resizable
&&
2564 if (gdk_pointer_grab (column
->window
, FALSE
,
2565 GDK_POINTER_MOTION_HINT_MASK
|
2566 GDK_BUTTON1_MOTION_MASK
|
2567 GDK_BUTTON_RELEASE_MASK
,
2568 NULL
, NULL
, event
->time
))
2571 gtk_grab_add (widget
);
2572 PSPP_SHEET_VIEW_SET_FLAG (tree_view
, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE
);
2573 column
->resized_width
= column
->width
;
2575 /* block attached dnd signal handler */
2576 drag_data
= g_object_get_data (G_OBJECT (widget
), "gtk-site-data");
2578 g_signal_handlers_block_matched (widget
,
2579 G_SIGNAL_MATCH_DATA
,
2583 tree_view
->priv
->drag_pos
= i
;
2584 tree_view
->priv
->x_drag
= column
->allocation
.x
+ (rtl
? 0 : column
->allocation
.width
);
2586 if (!gtk_widget_has_focus (widget
))
2587 gtk_widget_grab_focus (widget
);
2595 /* GtkWidget::button_release_event helper */
2597 pspp_sheet_view_button_release_drag_column (GtkWidget
*widget
,
2598 GdkEventButton
*event
)
2600 PsppSheetView
*tree_view
;
2604 tree_view
= PSPP_SHEET_VIEW (widget
);
2606 rtl
= (gtk_widget_get_direction (widget
) == GTK_TEXT_DIR_RTL
);
2607 gdk_display_pointer_ungrab (gtk_widget_get_display (widget
), GDK_CURRENT_TIME
);
2608 gdk_display_keyboard_ungrab (gtk_widget_get_display (widget
), GDK_CURRENT_TIME
);
2610 /* Move the button back */
2611 g_return_val_if_fail (tree_view
->priv
->drag_column
->button
, FALSE
);
2613 g_object_ref (tree_view
->priv
->drag_column
->button
);
2614 gtk_container_remove (GTK_CONTAINER (tree_view
), tree_view
->priv
->drag_column
->button
);
2615 gtk_widget_set_parent_window (tree_view
->priv
->drag_column
->button
, tree_view
->priv
->header_window
);
2616 gtk_widget_set_parent (tree_view
->priv
->drag_column
->button
, GTK_WIDGET (tree_view
));
2617 g_object_unref (tree_view
->priv
->drag_column
->button
);
2618 gtk_widget_queue_resize (widget
);
2619 if (tree_view
->priv
->drag_column
->resizable
)
2621 gdk_window_raise (tree_view
->priv
->drag_column
->window
);
2622 gdk_window_show (tree_view
->priv
->drag_column
->window
);
2625 gdk_window_hide (tree_view
->priv
->drag_column
->window
);
2627 gtk_widget_grab_focus (tree_view
->priv
->drag_column
->button
);
2631 if (tree_view
->priv
->cur_reorder
&&
2632 tree_view
->priv
->cur_reorder
->right_column
!= tree_view
->priv
->drag_column
)
2633 pspp_sheet_view_move_column_after (tree_view
, tree_view
->priv
->drag_column
,
2634 tree_view
->priv
->cur_reorder
->right_column
);
2638 if (tree_view
->priv
->cur_reorder
&&
2639 tree_view
->priv
->cur_reorder
->left_column
!= tree_view
->priv
->drag_column
)
2640 pspp_sheet_view_move_column_after (tree_view
, tree_view
->priv
->drag_column
,
2641 tree_view
->priv
->cur_reorder
->left_column
);
2643 tree_view
->priv
->drag_column
= NULL
;
2644 gdk_window_hide (tree_view
->priv
->drag_window
);
2646 for (l
= tree_view
->priv
->column_drag_info
; l
!= NULL
; l
= l
->next
)
2647 g_slice_free (PsppSheetViewColumnReorder
, l
->data
);
2648 g_list_free (tree_view
->priv
->column_drag_info
);
2649 tree_view
->priv
->column_drag_info
= NULL
;
2650 tree_view
->priv
->cur_reorder
= NULL
;
2652 if (tree_view
->priv
->drag_highlight_window
)
2653 gdk_window_hide (tree_view
->priv
->drag_highlight_window
);
2655 /* Reset our flags */
2656 tree_view
->priv
->drag_column_window_state
= DRAG_COLUMN_WINDOW_STATE_UNSET
;
2657 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view
, PSPP_SHEET_VIEW_IN_COLUMN_DRAG
);
2662 /* GtkWidget::button_release_event helper */
2664 pspp_sheet_view_button_release_column_resize (GtkWidget
*widget
,
2665 GdkEventButton
*event
)
2667 PsppSheetView
*tree_view
;
2670 tree_view
= PSPP_SHEET_VIEW (widget
);
2672 tree_view
->priv
->drag_pos
= -1;
2674 /* unblock attached dnd signal handler */
2675 drag_data
= g_object_get_data (G_OBJECT (widget
), "gtk-site-data");
2677 g_signal_handlers_unblock_matched (widget
,
2678 G_SIGNAL_MATCH_DATA
,
2682 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view
, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE
);
2683 gtk_grab_remove (widget
);
2684 gdk_display_pointer_ungrab (gdk_window_get_display (event
->window
),
2690 pspp_sheet_view_button_release_edit (PsppSheetView
*tree_view
,
2691 GdkEventButton
*event
)
2693 GtkCellEditable
*cell_editable
;
2698 PsppSheetViewColumn
*column
;
2699 GdkRectangle background_area
;
2700 GdkRectangle cell_area
;
2706 if (event
->window
!= tree_view
->priv
->bin_window
)
2709 /* Ignore a released button, if that button wasn't depressed */
2710 if (tree_view
->priv
->pressed_button
!= event
->button
)
2713 if (!find_click (tree_view
, event
->x
, event
->y
, &node
, &column
, &background_area
,
2717 /* decide if we edit */
2718 path
= _pspp_sheet_view_find_path (tree_view
, node
);
2719 modifiers
= event
->state
& gtk_accelerator_get_default_mod_mask ();
2720 if (event
->button
!= 1 || modifiers
)
2723 gtk_tree_model_get_iter (tree_view
->priv
->model
, &iter
, path
);
2724 pspp_sheet_view_column_cell_set_cell_data (column
,
2725 tree_view
->priv
->model
,
2728 if (!pspp_sheet_view_column_get_quick_edit (column
)
2729 && _pspp_sheet_view_column_has_editable_cell (column
))
2732 flags
= 0; /* FIXME: get the right flags */
2733 path_string
= gtk_tree_path_to_string (path
);
2735 if (!_pspp_sheet_view_column_cell_event (column
,
2743 if (cell_editable
== NULL
)
2746 pspp_sheet_view_real_set_cursor (tree_view
, path
,
2747 TRUE
, TRUE
, 0); /* XXX mode? */
2748 gtk_widget_queue_draw (GTK_WIDGET (tree_view
));
2751 _pspp_sheet_view_column_get_neighbor_sizes (
2752 column
, _pspp_sheet_view_column_get_edited_cell (column
), &left
, &right
);
2755 area
.width
-= right
+ left
;
2757 pspp_sheet_view_real_start_editing (tree_view
,
2764 g_free (path_string
);
2765 gtk_tree_path_free (path
);
2770 pspp_sheet_view_button_release (GtkWidget
*widget
,
2771 GdkEventButton
*event
)
2773 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
2775 pspp_sheet_view_stop_editing (tree_view
, FALSE
);
2776 if (tree_view
->priv
->rubber_band_status
!= RUBBER_BAND_ACTIVE
2777 && pspp_sheet_view_button_release_edit (tree_view
, event
))
2779 if (tree_view
->priv
->pressed_button
== event
->button
)
2780 tree_view
->priv
->pressed_button
= -1;
2782 tree_view
->priv
->rubber_band_status
= RUBBER_BAND_OFF
;
2786 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view
, PSPP_SHEET_VIEW_IN_COLUMN_DRAG
))
2787 return pspp_sheet_view_button_release_drag_column (widget
, event
);
2789 if (tree_view
->priv
->rubber_band_status
)
2790 pspp_sheet_view_stop_rubber_band (tree_view
);
2792 if (tree_view
->priv
->pressed_button
== event
->button
)
2793 tree_view
->priv
->pressed_button
= -1;
2795 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view
, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE
))
2796 return pspp_sheet_view_button_release_column_resize (widget
, event
);
2802 pspp_sheet_view_grab_broken (GtkWidget
*widget
,
2803 GdkEventGrabBroken
*event
)
2805 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
2807 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view
, PSPP_SHEET_VIEW_IN_COLUMN_DRAG
))
2808 pspp_sheet_view_button_release_drag_column (widget
, (GdkEventButton
*)event
);
2810 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view
, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE
))
2811 pspp_sheet_view_button_release_column_resize (widget
, (GdkEventButton
*)event
);
2816 /* GtkWidget::motion_event function set.
2820 do_prelight (PsppSheetView
*tree_view
,
2822 /* these are in bin_window coords */
2826 int prev_node
= tree_view
->priv
->prelight_node
;
2828 if (prev_node
!= node
)
2830 tree_view
->priv
->prelight_node
= node
;
2833 _pspp_sheet_view_queue_draw_node (tree_view
, prev_node
, NULL
);
2836 _pspp_sheet_view_queue_draw_node (tree_view
, node
, NULL
);
2842 prelight_or_select (PsppSheetView
*tree_view
,
2844 /* these are in bin_window coords */
2848 PsppSheetSelectionMode mode
= pspp_sheet_selection_get_mode (tree_view
->priv
->selection
);
2850 if (tree_view
->priv
->hover_selection
&&
2851 (mode
== PSPP_SHEET_SELECTION_SINGLE
|| mode
== PSPP_SHEET_SELECTION_BROWSE
) &&
2852 !(tree_view
->priv
->edited_column
&&
2853 tree_view
->priv
->edited_column
->editable_widget
))
2857 if (!pspp_sheet_view_node_is_selected (tree_view
, node
))
2861 path
= _pspp_sheet_view_find_path (tree_view
, node
);
2862 pspp_sheet_selection_select_path (tree_view
->priv
->selection
, path
);
2863 if (pspp_sheet_view_node_is_selected (tree_view
, node
))
2865 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view
, PSPP_SHEET_VIEW_DRAW_KEYFOCUS
);
2866 pspp_sheet_view_real_set_cursor (tree_view
, path
, FALSE
, FALSE
, 0); /* XXX mode? */
2868 gtk_tree_path_free (path
);
2872 else if (mode
== PSPP_SHEET_SELECTION_SINGLE
)
2873 pspp_sheet_selection_unselect_all (tree_view
->priv
->selection
);
2876 do_prelight (tree_view
, node
, x
, y
);
2880 ensure_unprelighted (PsppSheetView
*tree_view
)
2882 do_prelight (tree_view
,
2884 -1000, -1000); /* coords not possibly over an arrow */
2886 g_assert (tree_view
->priv
->prelight_node
< 0);
2890 update_prelight (PsppSheetView
*tree_view
,
2897 if (tree_view
->priv
->row_count
== 0)
2902 ensure_unprelighted (tree_view
);
2906 new_y
= TREE_WINDOW_Y_TO_RBTREE_Y (tree_view
, y
);
2910 pspp_sheet_view_find_offset (tree_view
, new_y
, &node
);
2913 prelight_or_select (tree_view
, node
, x
, y
);
2919 /* Our motion arrow is either a box (in the case of the original spot)
2920 * or an arrow. It is expander_size wide.
2943 pspp_sheet_view_motion_draw_column_motion_arrow (PsppSheetView
*tree_view
)
2946 PsppSheetViewColumnReorder
*reorder
= tree_view
->priv
->cur_reorder
;
2947 GtkWidget
*widget
= GTK_WIDGET (tree_view
);
2948 GdkBitmap
*mask
= NULL
;
2953 gint arrow_type
= DRAG_COLUMN_WINDOW_STATE_UNSET
;
2954 GdkWindowAttr attributes
;
2955 guint attributes_mask
;
2958 reorder
->left_column
== tree_view
->priv
->drag_column
||
2959 reorder
->right_column
== tree_view
->priv
->drag_column
)
2960 arrow_type
= DRAG_COLUMN_WINDOW_STATE_ORIGINAL
;
2961 else if (reorder
->left_column
|| reorder
->right_column
)
2963 GdkRectangle visible_rect
;
2964 pspp_sheet_view_get_visible_rect (tree_view
, &visible_rect
);
2965 if (reorder
->left_column
)
2966 x
= reorder
->left_column
->allocation
.x
+ reorder
->left_column
->allocation
.width
;
2968 x
= reorder
->right_column
->allocation
.x
;
2970 if (x
< visible_rect
.x
)
2971 arrow_type
= DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT
;
2972 else if (x
> visible_rect
.x
+ visible_rect
.width
)
2973 arrow_type
= DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT
;
2975 arrow_type
= DRAG_COLUMN_WINDOW_STATE_ARROW
;
2978 /* We want to draw the rectangle over the initial location. */
2979 if (arrow_type
== DRAG_COLUMN_WINDOW_STATE_ORIGINAL
)
2984 if (tree_view
->priv
->drag_column_window_state
!= DRAG_COLUMN_WINDOW_STATE_ORIGINAL
)
2986 if (tree_view
->priv
->drag_highlight_window
)
2988 gdk_window_set_user_data (tree_view
->priv
->drag_highlight_window
,
2990 gdk_window_destroy (tree_view
->priv
->drag_highlight_window
);
2993 attributes
.window_type
= GDK_WINDOW_CHILD
;
2994 attributes
.wclass
= GDK_INPUT_OUTPUT
;
2995 attributes
.x
= tree_view
->priv
->drag_column_x
;
2997 width
= attributes
.width
= tree_view
->priv
->drag_column
->allocation
.width
;
2998 height
= attributes
.height
= tree_view
->priv
->drag_column
->allocation
.height
;
2999 attributes
.visual
= gtk_widget_get_visual (GTK_WIDGET (tree_view
));
3000 attributes
.colormap
= gtk_widget_get_colormap (GTK_WIDGET (tree_view
));
3001 attributes
.event_mask
= GDK_VISIBILITY_NOTIFY_MASK
| GDK_EXPOSURE_MASK
| GDK_POINTER_MOTION_MASK
;
3002 attributes_mask
= GDK_WA_X
| GDK_WA_Y
| GDK_WA_VISUAL
| GDK_WA_COLORMAP
;
3003 tree_view
->priv
->drag_highlight_window
= gdk_window_new (tree_view
->priv
->header_window
, &attributes
, attributes_mask
);
3004 gdk_window_set_user_data (tree_view
->priv
->drag_highlight_window
, GTK_WIDGET (tree_view
));
3006 mask
= gdk_pixmap_new (tree_view
->priv
->drag_highlight_window
, width
, height
, 1);
3007 gc
= gdk_gc_new (mask
);
3009 gdk_gc_set_foreground (gc
, &col
);
3010 gdk_draw_rectangle (mask
, gc
, TRUE
, 0, 0, width
, height
);
3012 gdk_gc_set_foreground(gc
, &col
);
3013 gdk_draw_rectangle (mask
, gc
, TRUE
, 2, 2, width
- 4, height
- 4);
3014 g_object_unref (gc
);
3016 gdk_window_shape_combine_mask (tree_view
->priv
->drag_highlight_window
,
3018 if (mask
) g_object_unref (mask
);
3019 tree_view
->priv
->drag_column_window_state
= DRAG_COLUMN_WINDOW_STATE_ORIGINAL
;
3022 else if (arrow_type
== DRAG_COLUMN_WINDOW_STATE_ARROW
)
3028 width
= tree_view
->priv
->expander_size
;
3030 /* Get x, y, width, height of arrow */
3031 gdk_window_get_origin (tree_view
->priv
->header_window
, &x
, &y
);
3032 if (reorder
->left_column
)
3034 x
+= reorder
->left_column
->allocation
.x
+ reorder
->left_column
->allocation
.width
- width
/2;
3035 height
= reorder
->left_column
->allocation
.height
;
3039 x
+= reorder
->right_column
->allocation
.x
- width
/2;
3040 height
= reorder
->right_column
->allocation
.height
;
3042 y
-= tree_view
->priv
->expander_size
/2; /* The arrow takes up only half the space */
3043 height
+= tree_view
->priv
->expander_size
;
3045 /* Create the new window */
3046 if (tree_view
->priv
->drag_column_window_state
!= DRAG_COLUMN_WINDOW_STATE_ARROW
)
3048 if (tree_view
->priv
->drag_highlight_window
)
3050 gdk_window_set_user_data (tree_view
->priv
->drag_highlight_window
,
3052 gdk_window_destroy (tree_view
->priv
->drag_highlight_window
);
3055 attributes
.window_type
= GDK_WINDOW_TEMP
;
3056 attributes
.wclass
= GDK_INPUT_OUTPUT
;
3057 attributes
.visual
= gtk_widget_get_visual (GTK_WIDGET (tree_view
));
3058 attributes
.colormap
= gtk_widget_get_colormap (GTK_WIDGET (tree_view
));
3059 attributes
.event_mask
= GDK_VISIBILITY_NOTIFY_MASK
| GDK_EXPOSURE_MASK
| GDK_POINTER_MOTION_MASK
;
3060 attributes_mask
= GDK_WA_X
| GDK_WA_Y
| GDK_WA_VISUAL
| GDK_WA_COLORMAP
;
3063 attributes
.width
= width
;
3064 attributes
.height
= height
;
3065 tree_view
->priv
->drag_highlight_window
= gdk_window_new (gtk_widget_get_root_window (widget
),
3066 &attributes
, attributes_mask
);
3067 gdk_window_set_user_data (tree_view
->priv
->drag_highlight_window
, GTK_WIDGET (tree_view
));
3069 mask
= gdk_pixmap_new (tree_view
->priv
->drag_highlight_window
, width
, height
, 1);
3070 gc
= gdk_gc_new (mask
);
3072 gdk_gc_set_foreground (gc
, &col
);
3073 gdk_draw_rectangle (mask
, gc
, TRUE
, 0, 0, width
, height
);
3075 /* Draw the 2 arrows as per above */
3077 gdk_gc_set_foreground (gc
, &col
);
3078 for (i
= 0; i
< width
; i
++)
3080 if (i
== (width
/2 - 1))
3082 gdk_draw_line (mask
, gc
, i
, j
, i
, height
- j
);
3083 if (i
< (width
/2 - 1))
3088 g_object_unref (gc
);
3089 gdk_window_shape_combine_mask (tree_view
->priv
->drag_highlight_window
,
3091 if (mask
) g_object_unref (mask
);
3094 tree_view
->priv
->drag_column_window_state
= DRAG_COLUMN_WINDOW_STATE_ARROW
;
3095 gdk_window_move (tree_view
->priv
->drag_highlight_window
, x
, y
);
3097 else if (arrow_type
== DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT
||
3098 arrow_type
== DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT
)
3104 width
= tree_view
->priv
->expander_size
;
3106 /* Get x, y, width, height of arrow */
3107 width
= width
/2; /* remember, the arrow only takes half the available width */
3108 gdk_window_get_origin (gtk_widget_get_window (widget
), &x
, &y
);
3109 if (arrow_type
== DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT
)
3110 x
+= widget
->allocation
.width
- width
;
3112 if (reorder
->left_column
)
3113 height
= reorder
->left_column
->allocation
.height
;
3115 height
= reorder
->right_column
->allocation
.height
;
3117 y
-= tree_view
->priv
->expander_size
;
3118 height
+= 2*tree_view
->priv
->expander_size
;
3120 /* Create the new window */
3121 if (tree_view
->priv
->drag_column_window_state
!= DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT
&&
3122 tree_view
->priv
->drag_column_window_state
!= DRAG_COLUMN_WINDOW_STATE_ARROW_RIGHT
)
3124 if (tree_view
->priv
->drag_highlight_window
)
3126 gdk_window_set_user_data (tree_view
->priv
->drag_highlight_window
,
3128 gdk_window_destroy (tree_view
->priv
->drag_highlight_window
);
3131 attributes
.window_type
= GDK_WINDOW_TEMP
;
3132 attributes
.wclass
= GDK_INPUT_OUTPUT
;
3133 attributes
.visual
= gtk_widget_get_visual (GTK_WIDGET (tree_view
));
3134 attributes
.colormap
= gtk_widget_get_colormap (GTK_WIDGET (tree_view
));
3135 attributes
.event_mask
= GDK_VISIBILITY_NOTIFY_MASK
| GDK_EXPOSURE_MASK
| GDK_POINTER_MOTION_MASK
;
3136 attributes_mask
= GDK_WA_X
| GDK_WA_Y
| GDK_WA_VISUAL
| GDK_WA_COLORMAP
;
3139 attributes
.width
= width
;
3140 attributes
.height
= height
;
3141 tree_view
->priv
->drag_highlight_window
= gdk_window_new (NULL
, &attributes
, attributes_mask
);
3142 gdk_window_set_user_data (tree_view
->priv
->drag_highlight_window
, GTK_WIDGET (tree_view
));
3144 mask
= gdk_pixmap_new (tree_view
->priv
->drag_highlight_window
, width
, height
, 1);
3145 gc
= gdk_gc_new (mask
);
3147 gdk_gc_set_foreground (gc
, &col
);
3148 gdk_draw_rectangle (mask
, gc
, TRUE
, 0, 0, width
, height
);
3150 /* Draw the 2 arrows as per above */
3152 gdk_gc_set_foreground (gc
, &col
);
3153 j
= tree_view
->priv
->expander_size
;
3154 for (i
= 0; i
< width
; i
++)
3157 if (arrow_type
== DRAG_COLUMN_WINDOW_STATE_ARROW_LEFT
)
3161 gdk_draw_line (mask
, gc
, k
, j
, k
, height
- j
);
3162 gdk_draw_line (mask
, gc
, k
, 0, k
, tree_view
->priv
->expander_size
- j
);
3163 gdk_draw_line (mask
, gc
, k
, height
, k
, height
- tree_view
->priv
->expander_size
+ j
);
3166 g_object_unref (gc
);
3167 gdk_window_shape_combine_mask (tree_view
->priv
->drag_highlight_window
,
3169 if (mask
) g_object_unref (mask
);
3172 tree_view
->priv
->drag_column_window_state
= arrow_type
;
3173 gdk_window_move (tree_view
->priv
->drag_highlight_window
, x
, y
);
3177 g_warning (G_STRLOC
"Invalid PsppSheetViewColumnReorder struct");
3178 gdk_window_hide (tree_view
->priv
->drag_highlight_window
);
3182 gdk_window_show (tree_view
->priv
->drag_highlight_window
);
3183 gdk_window_raise (tree_view
->priv
->drag_highlight_window
);
3188 pspp_sheet_view_motion_resize_column (GtkWidget
*widget
,
3189 GdkEventMotion
*event
)
3193 PsppSheetViewColumn
*column
;
3194 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
3196 column
= pspp_sheet_view_get_column (tree_view
, tree_view
->priv
->drag_pos
);
3198 if (event
->is_hint
|| event
->window
!= gtk_widget_get_window (widget
))
3199 gtk_widget_get_pointer (widget
, &x
, NULL
);
3203 if (tree_view
->priv
->hadjustment
)
3204 x
+= gtk_adjustment_get_value (tree_view
->priv
->hadjustment
);
3206 new_width
= pspp_sheet_view_new_column_width (tree_view
,
3207 tree_view
->priv
->drag_pos
, &x
);
3208 if (x
!= tree_view
->priv
->x_drag
&&
3209 (new_width
!= column
->fixed_width
))
3211 column
->use_resized_width
= TRUE
;
3212 column
->resized_width
= new_width
;
3215 column
->resized_width
-= tree_view
->priv
->last_extra_space_per_column
;
3217 gtk_widget_queue_resize (widget
);
3225 pspp_sheet_view_update_current_reorder (PsppSheetView
*tree_view
)
3227 PsppSheetViewColumnReorder
*reorder
= NULL
;
3231 gdk_window_get_pointer (tree_view
->priv
->header_window
, &mouse_x
, NULL
, NULL
);
3232 for (list
= tree_view
->priv
->column_drag_info
; list
; list
= list
->next
)
3234 reorder
= (PsppSheetViewColumnReorder
*) list
->data
;
3235 if (mouse_x
>= reorder
->left_align
&& mouse_x
< reorder
->right_align
)
3240 /* if (reorder && reorder == tree_view->priv->cur_reorder)
3243 tree_view
->priv
->cur_reorder
= reorder
;
3244 pspp_sheet_view_motion_draw_column_motion_arrow (tree_view
);
3248 pspp_sheet_view_vertical_autoscroll (PsppSheetView
*tree_view
)
3250 GdkRectangle visible_rect
;
3255 gdk_window_get_pointer (tree_view
->priv
->bin_window
, NULL
, &y
, NULL
);
3256 y
+= tree_view
->priv
->dy
;
3258 pspp_sheet_view_get_visible_rect (tree_view
, &visible_rect
);
3260 /* see if we are near the edge. */
3261 offset
= y
- (visible_rect
.y
+ 2 * SCROLL_EDGE_SIZE
);
3264 offset
= y
- (visible_rect
.y
+ visible_rect
.height
- 2 * SCROLL_EDGE_SIZE
);
3269 value
= CLAMP (gtk_adjustment_get_value (tree_view
->priv
->vadjustment
) + offset
, 0.0,
3270 gtk_adjustment_get_upper (tree_view
->priv
->vadjustment
) - gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
));
3271 gtk_adjustment_set_value (tree_view
->priv
->vadjustment
, value
);
3275 pspp_sheet_view_horizontal_autoscroll (PsppSheetView
*tree_view
)
3277 GdkRectangle visible_rect
;
3282 gdk_window_get_pointer (tree_view
->priv
->bin_window
, &x
, NULL
, NULL
);
3284 pspp_sheet_view_get_visible_rect (tree_view
, &visible_rect
);
3286 /* See if we are near the edge. */
3287 offset
= x
- (visible_rect
.x
+ SCROLL_EDGE_SIZE
);
3290 offset
= x
- (visible_rect
.x
+ visible_rect
.width
- SCROLL_EDGE_SIZE
);
3296 value
= CLAMP (gtk_adjustment_get_value (tree_view
->priv
->hadjustment
) + offset
,
3297 0.0, gtk_adjustment_get_upper (tree_view
->priv
->hadjustment
) - gtk_adjustment_get_page_size (tree_view
->priv
->hadjustment
));
3298 gtk_adjustment_set_value (tree_view
->priv
->hadjustment
, value
);
3305 pspp_sheet_view_motion_drag_column (GtkWidget
*widget
,
3306 GdkEventMotion
*event
)
3308 PsppSheetView
*tree_view
= (PsppSheetView
*) widget
;
3309 PsppSheetViewColumn
*column
= tree_view
->priv
->drag_column
;
3311 GtkAllocation allocation
;
3314 if ((column
== NULL
) ||
3315 (event
->window
!= tree_view
->priv
->drag_window
))
3318 /* Handle moving the header */
3319 gdk_window_get_position (tree_view
->priv
->drag_window
, &x
, &y
);
3320 gtk_widget_get_allocation (GTK_WIDGET (tree_view
), &allocation
);
3321 x
= CLAMP (x
+ (gint
)event
->x
- column
->drag_x
, 0,
3322 MAX (tree_view
->priv
->width
, allocation
.width
) - column
->allocation
.width
);
3323 gdk_window_move (tree_view
->priv
->drag_window
, x
, y
);
3325 /* autoscroll, if needed */
3326 pspp_sheet_view_horizontal_autoscroll (tree_view
);
3327 /* Update the current reorder position and arrow; */
3328 pspp_sheet_view_update_current_reorder (tree_view
);
3334 pspp_sheet_view_stop_rubber_band (PsppSheetView
*tree_view
)
3336 remove_scroll_timeout (tree_view
);
3337 gtk_grab_remove (GTK_WIDGET (tree_view
));
3339 if (tree_view
->priv
->rubber_band_status
== RUBBER_BAND_ACTIVE
)
3341 GtkTreePath
*tmp_path
;
3343 gtk_widget_queue_draw (GTK_WIDGET (tree_view
));
3345 /* The anchor path should be set to the start path */
3346 tmp_path
= _pspp_sheet_view_find_path (tree_view
,
3347 tree_view
->priv
->rubber_band_start_node
);
3349 if (tree_view
->priv
->anchor
)
3350 gtk_tree_row_reference_free (tree_view
->priv
->anchor
);
3352 tree_view
->priv
->anchor
=
3353 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view
),
3354 tree_view
->priv
->model
,
3357 gtk_tree_path_free (tmp_path
);
3359 /* ... and the cursor to the end path */
3360 tmp_path
= _pspp_sheet_view_find_path (tree_view
,
3361 tree_view
->priv
->rubber_band_end_node
);
3362 pspp_sheet_view_real_set_cursor (PSPP_SHEET_VIEW (tree_view
), tmp_path
, FALSE
, FALSE
, 0); /* XXX mode? */
3363 gtk_tree_path_free (tmp_path
);
3365 _pspp_sheet_selection_emit_changed (tree_view
->priv
->selection
);
3368 /* Clear status variables */
3369 tree_view
->priv
->rubber_band_status
= RUBBER_BAND_OFF
;
3370 tree_view
->priv
->rubber_band_shift
= 0;
3371 tree_view
->priv
->rubber_band_ctrl
= 0;
3373 tree_view
->priv
->rubber_band_start_node
= -1;
3374 tree_view
->priv
->rubber_band_end_node
= -1;
3378 pspp_sheet_view_update_rubber_band_selection_range (PsppSheetView
*tree_view
,
3382 gboolean skip_start
,
3385 if (start_node
== end_node
)
3388 /* We skip the first node and jump inside the loop */
3394 /* Small optimization by assuming insensitive nodes are never
3399 if (tree_view
->priv
->rubber_band_shift
)
3400 pspp_sheet_view_node_select (tree_view
, start_node
);
3401 else if (tree_view
->priv
->rubber_band_ctrl
)
3403 /* Toggle the selection state */
3404 if (pspp_sheet_view_node_is_selected (tree_view
, start_node
))
3405 pspp_sheet_view_node_unselect (tree_view
, start_node
);
3407 pspp_sheet_view_node_select (tree_view
, start_node
);
3410 pspp_sheet_view_node_select (tree_view
, start_node
);
3414 /* Mirror the above */
3415 if (tree_view
->priv
->rubber_band_shift
)
3416 pspp_sheet_view_node_unselect (tree_view
, start_node
);
3417 else if (tree_view
->priv
->rubber_band_ctrl
)
3419 /* Toggle the selection state */
3420 if (pspp_sheet_view_node_is_selected (tree_view
, start_node
))
3421 pspp_sheet_view_node_unselect (tree_view
, start_node
);
3423 pspp_sheet_view_node_select (tree_view
, start_node
);
3426 pspp_sheet_view_node_unselect (tree_view
, start_node
);
3429 _pspp_sheet_view_queue_draw_node (tree_view
, start_node
, NULL
);
3431 if (start_node
== end_node
)
3436 start_node
= pspp_sheet_view_node_next (tree_view
, start_node
);
3439 /* Ran out of tree */
3442 if (skip_end
&& start_node
== end_node
)
3449 pspp_sheet_view_node_find_offset (PsppSheetView
*tree_view
,
3452 return node
* tree_view
->priv
->fixed_height
;
3456 pspp_sheet_view_find_offset (PsppSheetView
*tree_view
,
3460 int fixed_height
= tree_view
->priv
->fixed_height
;
3461 if (fixed_height
<= 0
3463 || height
>= tree_view
->priv
->row_count
* fixed_height
)
3470 *new_node
= height
/ fixed_height
;
3471 return height
% fixed_height
;
3476 pspp_sheet_view_update_rubber_band_selection (PsppSheetView
*tree_view
)
3481 pspp_sheet_view_find_offset (tree_view
, MIN (tree_view
->priv
->press_start_y
, tree_view
->priv
->rubber_band_y
), &start_node
);
3482 pspp_sheet_view_find_offset (tree_view
, MAX (tree_view
->priv
->press_start_y
, tree_view
->priv
->rubber_band_y
), &end_node
);
3484 /* Handle the start area first */
3485 if (tree_view
->priv
->rubber_band_start_node
< 0)
3487 pspp_sheet_view_update_rubber_band_selection_range (tree_view
,
3494 else if (start_node
< tree_view
->priv
->rubber_band_start_node
)
3496 /* New node is above the old one; selection became bigger */
3497 pspp_sheet_view_update_rubber_band_selection_range (tree_view
,
3499 tree_view
->priv
->rubber_band_start_node
,
3504 else if (start_node
> tree_view
->priv
->rubber_band_start_node
)
3506 /* New node is below the old one; selection became smaller */
3507 pspp_sheet_view_update_rubber_band_selection_range (tree_view
,
3508 tree_view
->priv
->rubber_band_start_node
,
3515 tree_view
->priv
->rubber_band_start_node
= start_node
;
3517 /* Next, handle the end area */
3518 if (tree_view
->priv
->rubber_band_end_node
< 0)
3520 /* In the event this happens, start_node was also -1; this case is
3524 else if (end_node
< 0)
3526 /* Find the last node in the tree */
3527 pspp_sheet_view_find_offset (tree_view
, tree_view
->priv
->height
- 1,
3530 /* Selection reached end of the tree */
3531 pspp_sheet_view_update_rubber_band_selection_range (tree_view
,
3532 tree_view
->priv
->rubber_band_end_node
,
3538 else if (end_node
> tree_view
->priv
->rubber_band_end_node
)
3540 /* New node is below the old one; selection became bigger */
3541 pspp_sheet_view_update_rubber_band_selection_range (tree_view
,
3542 tree_view
->priv
->rubber_band_end_node
,
3548 else if (end_node
< tree_view
->priv
->rubber_band_end_node
)
3550 /* New node is above the old one; selection became smaller */
3551 pspp_sheet_view_update_rubber_band_selection_range (tree_view
,
3553 tree_view
->priv
->rubber_band_end_node
,
3559 tree_view
->priv
->rubber_band_end_node
= end_node
;
3562 #define GDK_RECTANGLE_PTR(X) ((GdkRectangle *)(X))
3565 pspp_sheet_view_update_rubber_band (PsppSheetView
*tree_view
)
3568 cairo_rectangle_int_t old_area
;
3569 cairo_rectangle_int_t new_area
;
3570 cairo_rectangle_int_t common
;
3571 cairo_region_t
*invalid_region
;
3572 PsppSheetViewColumn
*column
;
3574 old_area
.x
= MIN (tree_view
->priv
->press_start_x
, tree_view
->priv
->rubber_band_x
);
3575 old_area
.y
= MIN (tree_view
->priv
->press_start_y
, tree_view
->priv
->rubber_band_y
) - tree_view
->priv
->dy
;
3576 old_area
.width
= ABS (tree_view
->priv
->rubber_band_x
- tree_view
->priv
->press_start_x
) + 1;
3577 old_area
.height
= ABS (tree_view
->priv
->rubber_band_y
- tree_view
->priv
->press_start_y
) + 1;
3579 gdk_window_get_pointer (tree_view
->priv
->bin_window
, &x
, &y
, NULL
);
3582 y
= MAX (y
, 0) + tree_view
->priv
->dy
;
3584 new_area
.x
= MIN (tree_view
->priv
->press_start_x
, x
);
3585 new_area
.y
= MIN (tree_view
->priv
->press_start_y
, y
) - tree_view
->priv
->dy
;
3586 new_area
.width
= ABS (x
- tree_view
->priv
->press_start_x
) + 1;
3587 new_area
.height
= ABS (y
- tree_view
->priv
->press_start_y
) + 1;
3589 invalid_region
= cairo_region_create_rectangle (&old_area
);
3590 cairo_region_union_rectangle (invalid_region
, &new_area
);
3592 gdk_rectangle_intersect (GDK_RECTANGLE_PTR (&old_area
),
3593 GDK_RECTANGLE_PTR (&new_area
), GDK_RECTANGLE_PTR (&common
));
3594 if (common
.width
> 2 && common
.height
> 2)
3596 cairo_region_t
*common_region
;
3598 /* make sure the border is invalidated */
3604 common_region
= cairo_region_create_rectangle (&common
);
3606 cairo_region_subtract (invalid_region
, common_region
);
3607 cairo_region_destroy (common_region
);
3610 #if GTK_MAJOR_VERSION == 3
3611 gdk_window_invalidate_region (tree_view
->priv
->bin_window
, invalid_region
, TRUE
);
3614 cairo_rectangle_int_t extents
;
3616 cairo_region_get_extents (invalid_region
, &extents
);
3617 ereg
= gdk_region_rectangle (GDK_RECTANGLE_PTR (&extents
));
3618 gdk_window_invalidate_region (tree_view
->priv
->bin_window
, ereg
, TRUE
);
3619 gdk_region_destroy (ereg
);
3623 cairo_region_destroy (invalid_region
);
3625 tree_view
->priv
->rubber_band_x
= x
;
3626 tree_view
->priv
->rubber_band_y
= y
;
3627 pspp_sheet_view_get_path_at_pos (tree_view
, x
, y
, NULL
, &column
, NULL
, NULL
);
3629 pspp_sheet_selection_unselect_all_columns (tree_view
->priv
->selection
);
3630 pspp_sheet_selection_select_column_range (tree_view
->priv
->selection
,
3631 tree_view
->priv
->anchor_column
,
3634 gtk_widget_queue_draw (GTK_WIDGET (tree_view
));
3636 pspp_sheet_view_update_rubber_band_selection (tree_view
);
3641 pspp_sheet_view_paint_rubber_band (PsppSheetView
*tree_view
,
3646 GdkRectangle rubber_rect
;
3650 rubber_rect
.x
= MIN (tree_view
->priv
->press_start_x
, tree_view
->priv
->rubber_band_x
);
3651 rubber_rect
.y
= MIN (tree_view
->priv
->press_start_y
, tree_view
->priv
->rubber_band_y
) - tree_view
->priv
->dy
;
3652 rubber_rect
.width
= ABS (tree_view
->priv
->press_start_x
- tree_view
->priv
->rubber_band_x
) + 1;
3653 rubber_rect
.height
= ABS (tree_view
->priv
->press_start_y
- tree_view
->priv
->rubber_band_y
) + 1;
3655 if (!gdk_rectangle_intersect (&rubber_rect
, area
, &rect
))
3658 cr
= gdk_cairo_create (tree_view
->priv
->bin_window
);
3659 cairo_set_line_width (cr
, 1.0);
3661 style
= gtk_widget_get_style (GTK_WIDGET (tree_view
));
3662 cairo_set_source_rgba (cr
,
3663 style
->fg
[GTK_STATE_NORMAL
].red
/ 65535.,
3664 style
->fg
[GTK_STATE_NORMAL
].green
/ 65535.,
3665 style
->fg
[GTK_STATE_NORMAL
].blue
/ 65535.,
3668 gdk_cairo_rectangle (cr
, &rect
);
3672 cairo_set_source_rgb (cr
,
3673 style
->fg
[GTK_STATE_NORMAL
].red
/ 65535.,
3674 style
->fg
[GTK_STATE_NORMAL
].green
/ 65535.,
3675 style
->fg
[GTK_STATE_NORMAL
].blue
/ 65535.);
3677 cairo_rectangle (cr
,
3678 rubber_rect
.x
+ 0.5, rubber_rect
.y
+ 0.5,
3679 rubber_rect
.width
- 1, rubber_rect
.height
- 1);
3688 pspp_sheet_view_motion_bin_window (GtkWidget
*widget
,
3689 GdkEventMotion
*event
)
3691 PsppSheetView
*tree_view
;
3695 tree_view
= (PsppSheetView
*) widget
;
3697 if (tree_view
->priv
->row_count
== 0)
3700 if (tree_view
->priv
->rubber_band_status
== RUBBER_BAND_MAYBE_START
)
3702 GdkRectangle background_area
, cell_area
;
3703 PsppSheetViewColumn
*column
;
3705 if (find_click (tree_view
, event
->x
, event
->y
, &node
, &column
,
3706 &background_area
, &cell_area
)
3707 && tree_view
->priv
->focus_column
== column
3708 && tree_view
->priv
->press_start_node
== node
)
3711 gtk_grab_add (GTK_WIDGET (tree_view
));
3712 pspp_sheet_view_update_rubber_band (tree_view
);
3714 tree_view
->priv
->rubber_band_status
= RUBBER_BAND_ACTIVE
;
3716 else if (tree_view
->priv
->rubber_band_status
== RUBBER_BAND_ACTIVE
)
3718 pspp_sheet_view_update_rubber_band (tree_view
);
3720 add_scroll_timeout (tree_view
);
3723 /* only check for an initiated drag when a button is pressed */
3724 if (tree_view
->priv
->pressed_button
>= 0
3725 && !tree_view
->priv
->rubber_band_status
)
3726 pspp_sheet_view_maybe_begin_dragging_row (tree_view
, event
);
3728 new_y
= TREE_WINDOW_Y_TO_RBTREE_Y(tree_view
, event
->y
);
3732 pspp_sheet_view_find_offset (tree_view
, new_y
, &node
);
3734 tree_view
->priv
->event_last_x
= event
->x
;
3735 tree_view
->priv
->event_last_y
= event
->y
;
3737 prelight_or_select (tree_view
, node
, event
->x
, event
->y
);
3743 pspp_sheet_view_motion (GtkWidget
*widget
,
3744 GdkEventMotion
*event
)
3746 PsppSheetView
*tree_view
;
3748 tree_view
= (PsppSheetView
*) widget
;
3750 /* Resizing a column */
3751 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view
, PSPP_SHEET_VIEW_IN_COLUMN_RESIZE
))
3752 return pspp_sheet_view_motion_resize_column (widget
, event
);
3755 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view
, PSPP_SHEET_VIEW_IN_COLUMN_DRAG
))
3756 return pspp_sheet_view_motion_drag_column (widget
, event
);
3758 /* Sanity check it */
3759 if (event
->window
== tree_view
->priv
->bin_window
)
3760 return pspp_sheet_view_motion_bin_window (widget
, event
);
3765 /* Invalidate the focus rectangle near the edge of the bin_window; used when
3766 * the tree is empty.
3769 invalidate_empty_focus (PsppSheetView
*tree_view
)
3773 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view
)))
3778 area
.width
= gdk_window_get_width (tree_view
->priv
->bin_window
);
3779 area
.height
= gdk_window_get_height (tree_view
->priv
->bin_window
);
3780 gdk_window_invalidate_rect (tree_view
->priv
->bin_window
, &area
, FALSE
);
3783 /* Draws a focus rectangle near the edge of the bin_window; used when the tree
3787 draw_empty_focus (PsppSheetView
*tree_view
)
3789 GtkWidget
*widget
= GTK_WIDGET (tree_view
);
3791 cairo_t
*cr
= gdk_cairo_create (tree_view
->priv
->bin_window
);
3793 if (!gtk_widget_has_focus (widget
))
3796 w
= gdk_window_get_width (tree_view
->priv
->bin_window
);
3797 h
= gdk_window_get_height (tree_view
->priv
->bin_window
);
3803 gtk_paint_focus (gtk_widget_get_style (widget
),
3805 gtk_widget_get_state (widget
),
3813 pspp_sheet_view_draw_vertical_grid_lines (PsppSheetView
*tree_view
,
3815 gint n_visible_columns
,
3819 GList
*list
= tree_view
->priv
->columns
;
3823 if (tree_view
->priv
->grid_lines
!= PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3824 && tree_view
->priv
->grid_lines
!= PSPP_SHEET_VIEW_GRID_LINES_BOTH
)
3827 /* Only draw the lines for visible rows and columns */
3828 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
, i
++)
3830 PsppSheetViewColumn
*column
= list
->data
;
3833 if (! column
->visible
)
3836 current_x
+= column
->width
;
3838 /* Generally the grid lines should fit within the column, but for the
3839 last visible column we put it just past the end of the column.
3840 (Otherwise horizontal grid lines sometimes stick out by one pixel.) */
3842 if (i
!= n_visible_columns
- 1)
3845 cairo_set_line_width (cr
, 1.0);
3846 cairo_set_line_cap (cr
, CAIRO_LINE_CAP_SQUARE
);
3847 cairo_move_to (cr
, x
+ 0.5, min_y
);
3848 cairo_line_to (cr
, x
+ 0.5, max_y
- min_y
);
3853 /* Warning: Very scary function.
3854 * Modify at your own risk
3856 * KEEP IN SYNC WITH pspp_sheet_view_create_row_drag_icon()!
3857 * FIXME: It's not...
3860 pspp_sheet_view_bin_expose (GtkWidget
*widget
,
3863 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
3868 int drag_highlight
= -1;
3871 gint y_offset
, cell_offset
;
3873 GdkRectangle background_area
;
3874 GdkRectangle cell_area
;
3876 gint bin_window_width
;
3877 gint bin_window_height
;
3878 GtkTreePath
*cursor_path
;
3879 GtkTreePath
*drag_dest_path
;
3880 GList
*first_column
, *last_column
;
3881 gint vertical_separator
;
3882 gint horizontal_separator
;
3883 gint focus_line_width
;
3884 gboolean allow_rules
;
3885 gboolean has_special_cell
;
3887 gint n_visible_columns
;
3888 gint grid_line_width
;
3889 gboolean row_ending_details
;
3890 gboolean draw_vgrid_lines
, draw_hgrid_lines
;
3894 GtkAllocation allocation
;
3895 gtk_widget_get_allocation (widget
, &allocation
);
3899 Zarea
.height
= allocation
.height
;
3901 rtl
= (gtk_widget_get_direction (widget
) == GTK_TEXT_DIR_RTL
);
3903 gtk_widget_style_get (widget
,
3904 "horizontal-separator", &horizontal_separator
,
3905 "vertical-separator", &vertical_separator
,
3906 "allow-rules", &allow_rules
,
3907 "focus-line-width", &focus_line_width
,
3908 "row-ending-details", &row_ending_details
,
3911 if (tree_view
->priv
->row_count
== 0)
3913 draw_empty_focus (tree_view
);
3918 /* clip event->area to the visible area */
3919 if (Zarea
.height
< 0.5)
3923 validate_visible_area (tree_view
);
3925 new_y
= TREE_WINDOW_Y_TO_RBTREE_Y (tree_view
, Zarea
.y
);
3929 y_offset
= -pspp_sheet_view_find_offset (tree_view
, new_y
, &node
);
3931 gdk_window_get_width (tree_view
->priv
->bin_window
);
3934 gdk_window_get_height (tree_view
->priv
->bin_window
);
3937 if (tree_view
->priv
->height
< bin_window_height
)
3939 gtk_paint_flat_box (gtk_widget_get_style (widget
),
3941 gtk_widget_get_state (widget
),
3945 0, tree_view
->priv
->height
,
3947 bin_window_height
- tree_view
->priv
->height
);
3953 /* find the path for the node */
3954 path
= _pspp_sheet_view_find_path ((PsppSheetView
*)widget
, node
);
3955 gtk_tree_model_get_iter (tree_view
->priv
->model
,
3958 gtk_tree_path_free (path
);
3961 drag_dest_path
= NULL
;
3963 if (tree_view
->priv
->cursor
)
3964 cursor_path
= gtk_tree_row_reference_get_path (tree_view
->priv
->cursor
);
3967 _pspp_sheet_view_find_node (tree_view
, cursor_path
, &cursor
);
3969 if (tree_view
->priv
->drag_dest_row
)
3970 drag_dest_path
= gtk_tree_row_reference_get_path (tree_view
->priv
->drag_dest_row
);
3973 _pspp_sheet_view_find_node (tree_view
, drag_dest_path
,
3977 tree_view
->priv
->grid_lines
== PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
3978 || tree_view
->priv
->grid_lines
== PSPP_SHEET_VIEW_GRID_LINES_BOTH
;
3980 tree_view
->priv
->grid_lines
== PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
3981 || tree_view
->priv
->grid_lines
== PSPP_SHEET_VIEW_GRID_LINES_BOTH
;
3983 if (draw_vgrid_lines
|| draw_hgrid_lines
)
3984 gtk_widget_style_get (widget
, "grid-line-width", &grid_line_width
, NULL
);
3986 n_visible_columns
= 0;
3987 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
3989 if (! PSPP_SHEET_VIEW_COLUMN (list
->data
)->visible
)
3991 n_visible_columns
++;
3994 /* Find the last column */
3995 for (last_column
= g_list_last (tree_view
->priv
->columns
);
3996 last_column
&& !(PSPP_SHEET_VIEW_COLUMN (last_column
->data
)->visible
);
3997 last_column
= last_column
->prev
)
4001 for (first_column
= g_list_first (tree_view
->priv
->columns
);
4002 first_column
&& !(PSPP_SHEET_VIEW_COLUMN (first_column
->data
)->visible
);
4003 first_column
= first_column
->next
)
4006 /* Actually process the expose event. To do this, we want to
4007 * start at the first node of the event, and walk the tree in
4008 * order, drawing each successive node.
4015 gboolean is_first
= FALSE
;
4016 gboolean is_last
= FALSE
;
4017 gboolean done
= FALSE
;
4020 max_height
= ROW_HEIGHT (tree_view
);
4024 background_area
.y
= y_offset
+ Zarea
.y
;
4025 background_area
.height
= max_height
;
4026 max_y
= background_area
.y
+ max_height
;
4030 if (node
== tree_view
->priv
->prelight_node
)
4031 flags
|= GTK_CELL_RENDERER_PRELIT
;
4033 selected
= pspp_sheet_view_node_is_selected (tree_view
, node
);
4037 if (tree_view
->priv
->special_cells
== PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT
)
4039 /* we *need* to set cell data on all cells before the call
4040 * to _has_special_cell, else _has_special_cell() does not
4041 * return a correct value.
4043 for (list
= (rtl
? g_list_last (tree_view
->priv
->columns
) : g_list_first (tree_view
->priv
->columns
));
4045 list
= (rtl
? list
->prev
: list
->next
))
4047 PsppSheetViewColumn
*column
= list
->data
;
4048 pspp_sheet_view_column_cell_set_cell_data (column
,
4049 tree_view
->priv
->model
,
4053 has_special_cell
= pspp_sheet_view_has_special_cell (tree_view
);
4056 has_special_cell
= tree_view
->priv
->special_cells
== PSPP_SHEET_VIEW_SPECIAL_CELLS_YES
;
4058 for (list
= (rtl
? g_list_last (tree_view
->priv
->columns
) : g_list_first (tree_view
->priv
->columns
));
4060 list
= (rtl
? list
->prev
: list
->next
))
4062 PsppSheetViewColumn
*column
= list
->data
;
4063 const gchar
*detail
= NULL
;
4064 gboolean selected_column
;
4067 if (!column
->visible
)
4070 if (tree_view
->priv
->selection
->type
== PSPP_SHEET_SELECTION_RECTANGLE
)
4071 selected_column
= column
->selected
&& column
->selectable
;
4073 selected_column
= TRUE
;
4076 if (cell_offset
> Zarea
.x
+ Zarea
.width
||
4077 cell_offset
+ column
->width
< Zarea
.x
)
4079 cell_offset
+= column
->width
;
4084 if (selected
&& selected_column
)
4085 flags
|= GTK_CELL_RENDERER_SELECTED
;
4087 flags
&= ~GTK_CELL_RENDERER_SELECTED
;
4089 if (column
->show_sort_indicator
)
4090 flags
|= GTK_CELL_RENDERER_SORTED
;
4092 flags
&= ~GTK_CELL_RENDERER_SORTED
;
4095 flags
|= GTK_CELL_RENDERER_FOCUSED
;
4097 flags
&= ~GTK_CELL_RENDERER_FOCUSED
;
4099 background_area
.x
= cell_offset
;
4100 background_area
.width
= column
->width
;
4102 cell_area
= background_area
;
4103 cell_area
.y
+= vertical_separator
/ 2;
4104 cell_area
.x
+= horizontal_separator
/ 2;
4105 cell_area
.height
-= vertical_separator
;
4106 cell_area
.width
-= horizontal_separator
;
4108 if (draw_vgrid_lines
)
4110 if (list
== first_column
)
4112 cell_area
.width
-= grid_line_width
/ 2;
4114 else if (list
== last_column
)
4116 cell_area
.x
+= grid_line_width
/ 2;
4117 cell_area
.width
-= grid_line_width
/ 2;
4121 cell_area
.x
+= grid_line_width
/ 2;
4122 cell_area
.width
-= grid_line_width
;
4126 if (draw_hgrid_lines
)
4128 cell_area
.y
+= grid_line_width
/ 2;
4129 cell_area
.height
-= grid_line_width
;
4133 if (gdk_region_rect_in (event
->region
, &background_area
) == GDK_OVERLAP_RECTANGLE_OUT
)
4135 cell_offset
+= column
->width
;
4140 pspp_sheet_view_column_cell_set_cell_data (column
,
4141 tree_view
->priv
->model
,
4144 /* Select the detail for drawing the cell. relevant
4145 * factors are parity, sortedness, and whether to
4148 if (allow_rules
&& tree_view
->priv
->has_rules
)
4150 if ((flags
& GTK_CELL_RENDERER_SORTED
) &&
4151 n_visible_columns
>= 3)
4154 detail
= "cell_odd_ruled_sorted";
4156 detail
= "cell_even_ruled_sorted";
4161 detail
= "cell_odd_ruled";
4163 detail
= "cell_even_ruled";
4168 if ((flags
& GTK_CELL_RENDERER_SORTED
) &&
4169 n_visible_columns
>= 3)
4172 detail
= "cell_odd_sorted";
4174 detail
= "cell_even_sorted";
4179 detail
= "cell_odd";
4181 detail
= "cell_even";
4187 if (gtk_widget_get_state (widget
) == GTK_STATE_INSENSITIVE
)
4188 state
= GTK_STATE_INSENSITIVE
;
4189 else if (flags
& GTK_CELL_RENDERER_SELECTED
)
4190 state
= GTK_STATE_SELECTED
;
4192 state
= GTK_STATE_NORMAL
;
4194 /* Draw background */
4195 if (row_ending_details
)
4197 char new_detail
[128];
4199 is_first
= (rtl
? !list
->next
: !list
->prev
);
4200 is_last
= (rtl
? !list
->prev
: !list
->next
);
4202 /* (I don't like the snprintfs either, but couldn't find a
4205 if (is_first
&& is_last
)
4206 g_snprintf (new_detail
, 127, "%s", detail
);
4208 g_snprintf (new_detail
, 127, "%s_start", detail
);
4210 g_snprintf (new_detail
, 127, "%s_end", detail
);
4212 g_snprintf (new_detail
, 128, "%s_middle", detail
);
4214 gtk_paint_flat_box (gtk_widget_get_style (widget
),
4222 background_area
.width
,
4223 background_area
.height
);
4227 gtk_paint_flat_box (gtk_widget_get_style (widget
),
4235 background_area
.width
,
4236 background_area
.height
);
4239 if (draw_hgrid_lines
)
4241 cairo_set_line_width (cr
, 1.0);
4242 cairo_set_line_cap (cr
, CAIRO_LINE_CAP_SQUARE
);
4244 if (background_area
.y
>= 0)
4247 gdk_draw_line (event
->window
,
4248 tree_view
->priv
->grid_line_gc
[widget
->state
],
4249 background_area
.x
, background_area
.y
,
4250 background_area
.x
+ background_area
.width
,
4253 cairo_move_to (cr
, background_area
.x
, background_area
.y
- 0.5);
4254 cairo_line_to (cr
, background_area
.x
+ background_area
.width
,
4255 background_area
.y
- 0.5);
4259 if (y_offset
+ max_height
<= Zarea
.height
- 0.5)
4262 gdk_draw_line (event
->window
,
4263 tree_view
->priv
->grid_line_gc
[widget
->state
],
4264 background_area
.x
, background_area
.y
+ max_height
,
4265 background_area
.x
+ background_area
.width
,
4266 background_area
.y
+ max_height
);
4269 cairo_move_to (cr
, background_area
.x
, background_area
.y
+ max_height
- 0.5);
4270 cairo_line_to (cr
, background_area
.x
+ background_area
.width
,
4271 background_area
.y
+ max_height
- 0.5);
4277 _pspp_sheet_view_column_cell_render (column
,
4283 if (node
== cursor
&& has_special_cell
&&
4284 ((column
== tree_view
->priv
->focus_column
&&
4285 PSPP_SHEET_VIEW_FLAG_SET (tree_view
, PSPP_SHEET_VIEW_DRAW_KEYFOCUS
) &&
4286 gtk_widget_has_focus (widget
)) ||
4287 (column
== tree_view
->priv
->edited_column
)))
4289 _pspp_sheet_view_column_cell_draw_focus (column
,
4296 cell_offset
+= column
->width
;
4299 if (cell_offset
< Zarea
.x
)
4301 gtk_paint_flat_box (gtk_widget_get_style (widget
),
4309 Zarea
.x
- cell_offset
,
4310 background_area
.height
);
4313 if (node
== drag_highlight
)
4315 /* Draw indicator for the drop
4317 gint highlight_y
= -1;
4321 switch (tree_view
->priv
->drag_dest_pos
)
4323 case PSPP_SHEET_VIEW_DROP_BEFORE
:
4324 highlight_y
= background_area
.y
- 1;
4325 if (highlight_y
< 0)
4329 case PSPP_SHEET_VIEW_DROP_AFTER
:
4330 highlight_y
= background_area
.y
+ background_area
.height
- 1;
4333 case PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE
:
4334 case PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER
:
4335 _pspp_sheet_view_find_node (tree_view
, drag_dest_path
, &node
);
4339 width
= gdk_window_get_width (tree_view
->priv
->bin_window
);
4341 if (row_ending_details
)
4342 gtk_paint_focus (gtk_widget_get_style (widget
),
4344 gtk_widget_get_state (widget
),
4347 ? (is_last
? "treeview-drop-indicator" : "treeview-drop-indicator-left" )
4348 : (is_last
? "treeview-drop-indicator-right" : "tree-view-drop-indicator-middle" )),
4349 0, BACKGROUND_FIRST_PIXEL (tree_view
, node
)
4350 - focus_line_width
/ 2,
4351 width
, ROW_HEIGHT (tree_view
)
4352 - focus_line_width
+ 1);
4354 gtk_paint_focus (gtk_widget_get_style (widget
),
4356 gtk_widget_get_state (widget
),
4358 "treeview-drop-indicator",
4359 0, BACKGROUND_FIRST_PIXEL (tree_view
, node
)
4360 - focus_line_width
/ 2,
4361 width
, ROW_HEIGHT (tree_view
)
4362 - focus_line_width
+ 1);
4367 if (highlight_y
>= 0)
4369 gdk_draw_line (event
->window
,
4370 widget
->style
->fg_gc
[gtk_widget_get_state (widget
)],
4373 rtl
? 0 : bin_window_width
,
4379 /* draw the big row-spanning focus rectangle, if needed */
4380 if (!has_special_cell
&& node
== cursor
&&
4381 PSPP_SHEET_VIEW_FLAG_SET (tree_view
, PSPP_SHEET_VIEW_DRAW_KEYFOCUS
) &&
4382 gtk_widget_has_focus (widget
))
4384 gint tmp_y
, tmp_height
;
4386 GtkStateType focus_rect_state
;
4389 flags
& GTK_CELL_RENDERER_SELECTED
? GTK_STATE_SELECTED
:
4390 (flags
& GTK_CELL_RENDERER_PRELIT
? GTK_STATE_PRELIGHT
:
4391 (flags
& GTK_CELL_RENDERER_INSENSITIVE
? GTK_STATE_INSENSITIVE
:
4394 width
= gdk_window_get_width (tree_view
->priv
->bin_window
);
4396 if (draw_hgrid_lines
)
4398 tmp_y
= BACKGROUND_FIRST_PIXEL (tree_view
, node
) + grid_line_width
/ 2;
4399 tmp_height
= ROW_HEIGHT (tree_view
) - grid_line_width
;
4403 tmp_y
= BACKGROUND_FIRST_PIXEL (tree_view
, node
);
4404 tmp_height
= ROW_HEIGHT (tree_view
);
4407 if (row_ending_details
)
4408 gtk_paint_focus (gtk_widget_get_style (widget
),
4413 ? (is_last
? "treeview" : "treeview-left" )
4414 : (is_last
? "treeview-right" : "treeview-middle" )),
4418 gtk_paint_focus (gtk_widget_get_style (widget
),
4427 y_offset
+= max_height
;
4431 node
= pspp_sheet_view_node_next (tree_view
, node
);
4434 gboolean has_next
= gtk_tree_model_iter_next (tree_view
->priv
->model
, &iter
);
4438 TREE_VIEW_INTERNAL_ASSERT (has_next
, FALSE
);
4445 while (y_offset
< Zarea
.height
);
4448 pspp_sheet_view_draw_vertical_grid_lines (tree_view
, cr
, n_visible_columns
,
4452 if (tree_view
->priv
->rubber_band_status
== RUBBER_BAND_ACTIVE
)
4454 GdkRectangle
*rectangles
;
4457 gdk_region_get_rectangles (event
->region
,
4461 while (n_rectangles
--)
4462 pspp_sheet_view_paint_rubber_band (tree_view
, &rectangles
[n_rectangles
]);
4464 g_free (rectangles
);
4469 gtk_tree_path_free (cursor_path
);
4472 gtk_tree_path_free (drag_dest_path
);
4479 pspp_sheet_view_draw (GtkWidget
*widget
,
4482 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
4484 if (gtk_cairo_should_draw_window (cr
, tree_view
->priv
->bin_window
))
4490 gtk_cairo_transform_to_window(cr
,widget
,tree_view
->priv
->bin_window
);
4491 retval
= pspp_sheet_view_bin_expose (widget
, cr
);
4494 /* We can't just chain up to Container::expose as it will try to send the
4495 * event to the headers, so we handle propagating it to our children
4496 * (eg. widgets being edited) ourselves.
4498 tmp_list
= tree_view
->priv
->children
;
4501 PsppSheetViewChild
*child
= tmp_list
->data
;
4502 tmp_list
= tmp_list
->next
;
4504 gtk_container_propagate_draw (GTK_CONTAINER (tree_view
), child
->widget
, cr
);
4509 else if (gtk_cairo_should_draw_window (cr
, tree_view
->priv
->header_window
))
4511 gint n_visible_columns
;
4514 for (list
= tree_view
->priv
->columns
; list
!= NULL
; list
= list
->next
)
4516 PsppSheetViewColumn
*column
= list
->data
;
4518 if (column
== tree_view
->priv
->drag_column
|| !column
->visible
)
4521 if (span_intersects (column
->allocation
.x
, column
->allocation
.width
,
4522 (int) gtk_adjustment_get_value (tree_view
->priv
->hadjustment
),
4523 (int) gtk_widget_get_allocated_width (widget
))
4524 && column
->button
!= NULL
)
4525 gtk_container_propagate_draw (GTK_CONTAINER (tree_view
),
4526 column
->button
, cr
);
4529 n_visible_columns
= 0;
4530 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
4532 if (! PSPP_SHEET_VIEW_COLUMN (list
->data
)->visible
)
4534 n_visible_columns
++;
4537 gtk_cairo_transform_to_window(cr
,widget
,tree_view
->priv
->header_window
);
4538 pspp_sheet_view_draw_vertical_grid_lines (tree_view
,
4542 TREE_VIEW_HEADER_HEIGHT (tree_view
));
4547 else if (gtk_cairo_should_draw_window (cr
, tree_view
->priv
->drag_window
))
4549 gtk_container_propagate_draw (GTK_CONTAINER (tree_view
),
4550 tree_view
->priv
->drag_column
->button
,
4567 /* returns 0x1 when no column has been found -- yes it's hackish */
4568 static PsppSheetViewColumn
*
4569 pspp_sheet_view_get_drop_column (PsppSheetView
*tree_view
,
4570 PsppSheetViewColumn
*column
,
4573 PsppSheetViewColumn
*left_column
= NULL
;
4574 PsppSheetViewColumn
*cur_column
= NULL
;
4577 if (!column
->reorderable
)
4578 return (PsppSheetViewColumn
*)0x1;
4580 switch (drop_position
)
4583 /* find first column where we can drop */
4584 tmp_list
= tree_view
->priv
->columns
;
4585 if (column
== PSPP_SHEET_VIEW_COLUMN (tmp_list
->data
))
4586 return (PsppSheetViewColumn
*)0x1;
4590 g_assert (tmp_list
);
4592 cur_column
= PSPP_SHEET_VIEW_COLUMN (tmp_list
->data
);
4593 tmp_list
= tmp_list
->next
;
4595 if (left_column
&& left_column
->visible
== FALSE
)
4598 if (!tree_view
->priv
->column_drop_func
)
4601 if (!tree_view
->priv
->column_drop_func (tree_view
, column
, left_column
, cur_column
, tree_view
->priv
->column_drop_func_data
))
4603 left_column
= cur_column
;
4610 if (!tree_view
->priv
->column_drop_func
)
4613 if (tree_view
->priv
->column_drop_func (tree_view
, column
, left_column
, NULL
, tree_view
->priv
->column_drop_func_data
))
4616 return (PsppSheetViewColumn
*)0x1;
4620 /* find first column after column where we can drop */
4621 tmp_list
= tree_view
->priv
->columns
;
4623 for (; tmp_list
; tmp_list
= tmp_list
->next
)
4624 if (PSPP_SHEET_VIEW_COLUMN (tmp_list
->data
) == column
)
4627 if (!tmp_list
|| !tmp_list
->next
)
4628 return (PsppSheetViewColumn
*)0x1;
4630 tmp_list
= tmp_list
->next
;
4631 left_column
= PSPP_SHEET_VIEW_COLUMN (tmp_list
->data
);
4632 tmp_list
= tmp_list
->next
;
4636 g_assert (tmp_list
);
4638 cur_column
= PSPP_SHEET_VIEW_COLUMN (tmp_list
->data
);
4639 tmp_list
= tmp_list
->next
;
4641 if (left_column
&& left_column
->visible
== FALSE
)
4643 left_column
= cur_column
;
4645 tmp_list
= tmp_list
->next
;
4649 if (!tree_view
->priv
->column_drop_func
)
4652 if (!tree_view
->priv
->column_drop_func (tree_view
, column
, left_column
, cur_column
, tree_view
->priv
->column_drop_func_data
))
4654 left_column
= cur_column
;
4661 if (!tree_view
->priv
->column_drop_func
)
4664 if (tree_view
->priv
->column_drop_func (tree_view
, column
, left_column
, NULL
, tree_view
->priv
->column_drop_func_data
))
4667 return (PsppSheetViewColumn
*)0x1;
4671 /* find first column before column where we can drop */
4672 tmp_list
= tree_view
->priv
->columns
;
4674 for (; tmp_list
; tmp_list
= tmp_list
->next
)
4675 if (PSPP_SHEET_VIEW_COLUMN (tmp_list
->data
) == column
)
4678 if (!tmp_list
|| !tmp_list
->prev
)
4679 return (PsppSheetViewColumn
*)0x1;
4681 tmp_list
= tmp_list
->prev
;
4682 cur_column
= PSPP_SHEET_VIEW_COLUMN (tmp_list
->data
);
4683 tmp_list
= tmp_list
->prev
;
4687 g_assert (tmp_list
);
4689 left_column
= PSPP_SHEET_VIEW_COLUMN (tmp_list
->data
);
4691 if (left_column
&& !left_column
->visible
)
4693 /*if (!tmp_list->prev)
4694 return (PsppSheetViewColumn *)0x1;
4697 cur_column = PSPP_SHEET_VIEW_COLUMN (tmp_list->prev->data);
4698 tmp_list = tmp_list->prev->prev;
4701 cur_column
= left_column
;
4703 tmp_list
= tmp_list
->prev
;
4707 if (!tree_view
->priv
->column_drop_func
)
4710 if (tree_view
->priv
->column_drop_func (tree_view
, column
, left_column
, cur_column
, tree_view
->priv
->column_drop_func_data
))
4713 cur_column
= left_column
;
4714 tmp_list
= tmp_list
->prev
;
4717 if (!tree_view
->priv
->column_drop_func
)
4720 if (tree_view
->priv
->column_drop_func (tree_view
, column
, NULL
, cur_column
, tree_view
->priv
->column_drop_func_data
))
4723 return (PsppSheetViewColumn
*)0x1;
4727 /* same as DROP_HOME case, but doing it backwards */
4728 tmp_list
= g_list_last (tree_view
->priv
->columns
);
4731 if (column
== PSPP_SHEET_VIEW_COLUMN (tmp_list
->data
))
4732 return (PsppSheetViewColumn
*)0x1;
4736 g_assert (tmp_list
);
4738 left_column
= PSPP_SHEET_VIEW_COLUMN (tmp_list
->data
);
4740 if (left_column
&& !left_column
->visible
)
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
, left_column
, cur_column
, tree_view
->priv
->column_drop_func_data
))
4752 cur_column
= left_column
;
4753 tmp_list
= tmp_list
->prev
;
4756 if (!tree_view
->priv
->column_drop_func
)
4759 if (tree_view
->priv
->column_drop_func (tree_view
, column
, NULL
, cur_column
, tree_view
->priv
->column_drop_func_data
))
4762 return (PsppSheetViewColumn
*)0x1;
4766 return (PsppSheetViewColumn
*)0x1;
4770 pspp_sheet_view_key_press (GtkWidget
*widget
,
4773 PsppSheetView
*tree_view
= (PsppSheetView
*) widget
;
4775 if (tree_view
->priv
->rubber_band_status
)
4777 if (event
->keyval
== GDK_Escape
)
4778 pspp_sheet_view_stop_rubber_band (tree_view
);
4783 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view
, PSPP_SHEET_VIEW_IN_COLUMN_DRAG
))
4785 if (event
->keyval
== GDK_Escape
)
4787 tree_view
->priv
->cur_reorder
= NULL
;
4788 pspp_sheet_view_button_release_drag_column (widget
, NULL
);
4793 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view
, PSPP_SHEET_VIEW_HEADERS_VISIBLE
))
4795 GList
*focus_column
;
4798 rtl
= (gtk_widget_get_direction (GTK_WIDGET (tree_view
)) == GTK_TEXT_DIR_RTL
);
4800 for (focus_column
= tree_view
->priv
->columns
;
4802 focus_column
= focus_column
->next
)
4804 PsppSheetViewColumn
*column
= PSPP_SHEET_VIEW_COLUMN (focus_column
->data
);
4806 if (column
->button
&& gtk_widget_has_focus (column
->button
))
4811 (event
->state
& GDK_SHIFT_MASK
) && (event
->state
& GDK_MOD1_MASK
) &&
4812 (event
->keyval
== GDK_Left
|| event
->keyval
== GDK_KP_Left
4813 || event
->keyval
== GDK_Right
|| event
->keyval
== GDK_KP_Right
))
4815 PsppSheetViewColumn
*column
= PSPP_SHEET_VIEW_COLUMN (focus_column
->data
);
4817 if (!column
->resizable
)
4819 gtk_widget_error_bell (widget
);
4823 if (event
->keyval
== (rtl
? GDK_Right
: GDK_Left
)
4824 || event
->keyval
== (rtl
? GDK_KP_Right
: GDK_KP_Left
))
4826 gint old_width
= column
->resized_width
;
4828 column
->resized_width
= MAX (column
->resized_width
,
4830 column
->resized_width
-= 2;
4831 if (column
->resized_width
< 0)
4832 column
->resized_width
= 0;
4834 if (column
->min_width
== -1)
4835 column
->resized_width
= MAX (column
->button_request
,
4836 column
->resized_width
);
4838 column
->resized_width
= MAX (column
->min_width
,
4839 column
->resized_width
);
4841 if (column
->max_width
!= -1)
4842 column
->resized_width
= MIN (column
->resized_width
,
4845 column
->use_resized_width
= TRUE
;
4847 if (column
->resized_width
!= old_width
)
4848 gtk_widget_queue_resize (widget
);
4850 gtk_widget_error_bell (widget
);
4852 else if (event
->keyval
== (rtl
? GDK_Left
: GDK_Right
)
4853 || event
->keyval
== (rtl
? GDK_KP_Left
: GDK_KP_Right
))
4855 gint old_width
= column
->resized_width
;
4857 column
->resized_width
= MAX (column
->resized_width
,
4859 column
->resized_width
+= 2;
4861 if (column
->max_width
!= -1)
4862 column
->resized_width
= MIN (column
->resized_width
,
4865 column
->use_resized_width
= TRUE
;
4867 if (column
->resized_width
!= old_width
)
4868 gtk_widget_queue_resize (widget
);
4870 gtk_widget_error_bell (widget
);
4877 (event
->state
& GDK_MOD1_MASK
) &&
4878 (event
->keyval
== GDK_Left
|| event
->keyval
== GDK_KP_Left
4879 || event
->keyval
== GDK_Right
|| event
->keyval
== GDK_KP_Right
4880 || event
->keyval
== GDK_Home
|| event
->keyval
== GDK_KP_Home
4881 || event
->keyval
== GDK_End
|| event
->keyval
== GDK_KP_End
))
4883 PsppSheetViewColumn
*column
= PSPP_SHEET_VIEW_COLUMN (focus_column
->data
);
4885 if (event
->keyval
== (rtl
? GDK_Right
: GDK_Left
)
4886 || event
->keyval
== (rtl
? GDK_KP_Right
: GDK_KP_Left
))
4888 PsppSheetViewColumn
*col
;
4889 col
= pspp_sheet_view_get_drop_column (tree_view
, column
, DROP_LEFT
);
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
== (rtl
? GDK_Left
: GDK_Right
)
4896 || event
->keyval
== (rtl
? GDK_KP_Left
: GDK_KP_Right
))
4898 PsppSheetViewColumn
*col
;
4899 col
= pspp_sheet_view_get_drop_column (tree_view
, column
, DROP_RIGHT
);
4900 if (col
!= (PsppSheetViewColumn
*)0x1)
4901 pspp_sheet_view_move_column_after (tree_view
, column
, col
);
4903 gtk_widget_error_bell (widget
);
4905 else if (event
->keyval
== GDK_Home
|| event
->keyval
== GDK_KP_Home
)
4907 PsppSheetViewColumn
*col
;
4908 col
= pspp_sheet_view_get_drop_column (tree_view
, column
, DROP_HOME
);
4909 if (col
!= (PsppSheetViewColumn
*)0x1)
4910 pspp_sheet_view_move_column_after (tree_view
, column
, col
);
4912 gtk_widget_error_bell (widget
);
4914 else if (event
->keyval
== GDK_End
|| event
->keyval
== GDK_KP_End
)
4916 PsppSheetViewColumn
*col
;
4917 col
= pspp_sheet_view_get_drop_column (tree_view
, column
, DROP_END
);
4918 if (col
!= (PsppSheetViewColumn
*)0x1)
4919 pspp_sheet_view_move_column_after (tree_view
, column
, col
);
4921 gtk_widget_error_bell (widget
);
4928 /* Chain up to the parent class. It handles the keybindings. */
4929 if (GTK_WIDGET_CLASS (pspp_sheet_view_parent_class
)->key_press_event (widget
, event
))
4932 if (tree_view
->priv
->search_entry_avoid_unhandled_binding
)
4934 tree_view
->priv
->search_entry_avoid_unhandled_binding
= FALSE
;
4938 /* We pass the event to the search_entry. If its text changes, then we start
4939 * the typeahead find capabilities. */
4940 if (gtk_widget_has_focus (GTK_WIDGET (tree_view
))
4941 && tree_view
->priv
->enable_search
4942 && !tree_view
->priv
->search_custom_entry_set
)
4944 GdkEvent
*new_event
;
4946 const char *new_text
;
4949 gboolean text_modified
;
4950 gulong popup_menu_id
;
4952 pspp_sheet_view_ensure_interactive_directory (tree_view
);
4954 /* Make a copy of the current text */
4955 old_text
= g_strdup (gtk_entry_get_text (GTK_ENTRY (tree_view
->priv
->search_entry
)));
4956 new_event
= gdk_event_copy ((GdkEvent
*) event
);
4957 g_object_unref (((GdkEventKey
*) new_event
)->window
);
4958 ((GdkEventKey
*) new_event
)->window
= g_object_ref (gtk_widget_get_window (tree_view
->priv
->search_window
));
4959 gtk_widget_realize (tree_view
->priv
->search_window
);
4961 popup_menu_id
= g_signal_connect (tree_view
->priv
->search_entry
,
4962 "popup-menu", G_CALLBACK (gtk_true
),
4965 /* Move the entry off screen */
4966 screen
= gtk_widget_get_screen (GTK_WIDGET (tree_view
));
4967 gtk_window_move (GTK_WINDOW (tree_view
->priv
->search_window
),
4968 gdk_screen_get_width (screen
) + 1,
4969 gdk_screen_get_height (screen
) + 1);
4970 gtk_widget_show (tree_view
->priv
->search_window
);
4972 /* Send the event to the window. If the preedit_changed signal is emitted
4973 * during this event, we will set priv->imcontext_changed */
4974 tree_view
->priv
->imcontext_changed
= FALSE
;
4975 retval
= gtk_widget_event (tree_view
->priv
->search_window
, new_event
);
4976 gdk_event_free (new_event
);
4977 gtk_widget_hide (tree_view
->priv
->search_window
);
4979 g_signal_handler_disconnect (tree_view
->priv
->search_entry
,
4982 /* We check to make sure that the entry tried to handle the text, and that
4983 * the text has changed.
4985 new_text
= gtk_entry_get_text (GTK_ENTRY (tree_view
->priv
->search_entry
));
4986 text_modified
= strcmp (old_text
, new_text
) != 0;
4988 if (tree_view
->priv
->imcontext_changed
|| /* we're in a preedit */
4989 (retval
&& text_modified
)) /* ...or the text was modified */
4991 if (pspp_sheet_view_real_start_interactive_search (tree_view
, FALSE
))
4993 gtk_widget_grab_focus (GTK_WIDGET (tree_view
));
4998 gtk_entry_set_text (GTK_ENTRY (tree_view
->priv
->search_entry
), "");
5008 pspp_sheet_view_key_release (GtkWidget
*widget
,
5011 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
5013 if (tree_view
->priv
->rubber_band_status
)
5016 return GTK_WIDGET_CLASS (pspp_sheet_view_parent_class
)->key_release_event (widget
, event
);
5019 /* FIXME Is this function necessary? Can I get an enter_notify event
5020 * w/o either an expose event or a mouse motion event?
5023 pspp_sheet_view_enter_notify (GtkWidget
*widget
,
5024 GdkEventCrossing
*event
)
5026 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
5030 /* Sanity check it */
5031 if (event
->window
!= tree_view
->priv
->bin_window
)
5034 if (tree_view
->priv
->row_count
== 0)
5037 if (event
->mode
== GDK_CROSSING_GRAB
||
5038 event
->mode
== GDK_CROSSING_GTK_GRAB
||
5039 event
->mode
== GDK_CROSSING_GTK_UNGRAB
||
5040 event
->mode
== GDK_CROSSING_STATE_CHANGED
)
5043 /* find the node internally */
5044 new_y
= TREE_WINDOW_Y_TO_RBTREE_Y(tree_view
, event
->y
);
5047 pspp_sheet_view_find_offset (tree_view
, new_y
, &node
);
5049 tree_view
->priv
->event_last_x
= event
->x
;
5050 tree_view
->priv
->event_last_y
= event
->y
;
5052 prelight_or_select (tree_view
, node
, event
->x
, event
->y
);
5058 pspp_sheet_view_leave_notify (GtkWidget
*widget
,
5059 GdkEventCrossing
*event
)
5061 PsppSheetView
*tree_view
;
5063 if (event
->mode
== GDK_CROSSING_GRAB
)
5066 tree_view
= PSPP_SHEET_VIEW (widget
);
5068 if (tree_view
->priv
->prelight_node
>= 0)
5069 _pspp_sheet_view_queue_draw_node (tree_view
,
5070 tree_view
->priv
->prelight_node
,
5073 tree_view
->priv
->event_last_x
= -10000;
5074 tree_view
->priv
->event_last_y
= -10000;
5076 prelight_or_select (tree_view
,
5078 -1000, -1000); /* coords not possibly over an arrow */
5085 pspp_sheet_view_focus_out (GtkWidget
*widget
,
5086 GdkEventFocus
*event
)
5088 PsppSheetView
*tree_view
;
5090 tree_view
= PSPP_SHEET_VIEW (widget
);
5092 gtk_widget_queue_draw (widget
);
5094 /* destroy interactive search dialog */
5095 if (tree_view
->priv
->search_window
)
5096 pspp_sheet_view_search_dialog_hide (tree_view
->priv
->search_window
, tree_view
);
5102 /* Incremental Reflow
5106 pspp_sheet_view_node_queue_redraw (PsppSheetView
*tree_view
,
5109 GtkAllocation allocation
;
5110 gint y
= pspp_sheet_view_node_find_offset (tree_view
, node
)
5111 - gtk_adjustment_get_value (tree_view
->priv
->vadjustment
)
5112 + TREE_VIEW_HEADER_HEIGHT (tree_view
);
5114 gtk_widget_get_allocation (GTK_WIDGET (tree_view
), &allocation
);
5116 gtk_widget_queue_draw_area (GTK_WIDGET (tree_view
),
5119 tree_view
->priv
->fixed_height
);
5123 node_is_visible (PsppSheetView
*tree_view
,
5129 y
= pspp_sheet_view_node_find_offset (tree_view
, node
);
5130 height
= ROW_HEIGHT (tree_view
);
5132 if (y
>= gtk_adjustment_get_value (tree_view
->priv
->vadjustment
) &&
5133 y
+ height
<= (gtk_adjustment_get_value (tree_view
->priv
->vadjustment
)
5134 + gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
)))
5140 /* Returns the row height. */
5142 validate_row (PsppSheetView
*tree_view
,
5147 PsppSheetViewColumn
*column
;
5148 GList
*list
, *first_column
, *last_column
;
5150 gint horizontal_separator
;
5151 gint vertical_separator
;
5152 gint focus_line_width
;
5153 gboolean draw_vgrid_lines
, draw_hgrid_lines
;
5155 gint grid_line_width
;
5156 gboolean wide_separators
;
5157 gint separator_height
;
5159 gtk_widget_style_get (GTK_WIDGET (tree_view
),
5160 "focus-padding", &focus_pad
,
5161 "focus-line-width", &focus_line_width
,
5162 "horizontal-separator", &horizontal_separator
,
5163 "vertical-separator", &vertical_separator
,
5164 "grid-line-width", &grid_line_width
,
5165 "wide-separators", &wide_separators
,
5166 "separator-height", &separator_height
,
5170 tree_view
->priv
->grid_lines
== PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
5171 || tree_view
->priv
->grid_lines
== PSPP_SHEET_VIEW_GRID_LINES_BOTH
;
5173 tree_view
->priv
->grid_lines
== PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
5174 || tree_view
->priv
->grid_lines
== PSPP_SHEET_VIEW_GRID_LINES_BOTH
;
5176 for (last_column
= g_list_last (tree_view
->priv
->columns
);
5177 last_column
&& !(PSPP_SHEET_VIEW_COLUMN (last_column
->data
)->visible
);
5178 last_column
= last_column
->prev
)
5181 for (first_column
= g_list_first (tree_view
->priv
->columns
);
5182 first_column
&& !(PSPP_SHEET_VIEW_COLUMN (first_column
->data
)->visible
);
5183 first_column
= first_column
->next
)
5186 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
5191 column
= list
->data
;
5193 if (! column
->visible
)
5196 pspp_sheet_view_column_cell_set_cell_data (column
, tree_view
->priv
->model
, iter
);
5197 pspp_sheet_view_column_cell_get_size (column
,
5199 &tmp_width
, &tmp_height
);
5201 tmp_height
+= vertical_separator
;
5202 height
= MAX (height
, tmp_height
);
5204 tmp_width
= tmp_width
+ horizontal_separator
;
5206 if (draw_vgrid_lines
)
5208 if (list
->data
== first_column
|| list
->data
== last_column
)
5209 tmp_width
+= grid_line_width
/ 2.0;
5211 tmp_width
+= grid_line_width
;
5214 if (tmp_width
> column
->requested_width
)
5215 column
->requested_width
= tmp_width
;
5218 if (draw_hgrid_lines
)
5219 height
+= grid_line_width
;
5221 tree_view
->priv
->post_validation_flag
= TRUE
;
5227 validate_visible_area (PsppSheetView
*tree_view
)
5229 GtkTreePath
*path
= NULL
;
5230 GtkTreePath
*above_path
= NULL
;
5233 gboolean size_changed
= FALSE
;
5235 gint area_above
= 0;
5236 gint area_below
= 0;
5237 GtkAllocation allocation
;
5239 if (tree_view
->priv
->row_count
== 0)
5242 if (tree_view
->priv
->scroll_to_path
== NULL
)
5245 gtk_widget_get_allocation (GTK_WIDGET (tree_view
), &allocation
);
5247 total_height
= allocation
.height
- TREE_VIEW_HEADER_HEIGHT (tree_view
);
5249 if (total_height
== 0)
5252 path
= gtk_tree_row_reference_get_path (tree_view
->priv
->scroll_to_path
);
5255 /* we are going to scroll, and will update dy */
5256 _pspp_sheet_view_find_node (tree_view
, path
, &node
);
5257 gtk_tree_model_get_iter (tree_view
->priv
->model
, &iter
, path
);
5259 if (tree_view
->priv
->scroll_to_use_align
)
5261 gint height
= ROW_HEIGHT (tree_view
);
5262 area_above
= (total_height
- height
) *
5263 tree_view
->priv
->scroll_to_row_align
;
5264 area_below
= total_height
- area_above
- height
;
5265 area_above
= MAX (area_above
, 0);
5266 area_below
= MAX (area_below
, 0);
5271 * 1) row not visible
5275 gint height
= ROW_HEIGHT (tree_view
);
5277 dy
= pspp_sheet_view_node_find_offset (tree_view
, node
);
5279 if (dy
>= gtk_adjustment_get_value (tree_view
->priv
->vadjustment
) &&
5280 dy
+ height
<= (gtk_adjustment_get_value (tree_view
->priv
->vadjustment
)
5281 + gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
)))
5283 /* row visible: keep the row at the same position */
5284 area_above
= dy
- gtk_adjustment_get_value (tree_view
->priv
->vadjustment
);
5285 area_below
= (gtk_adjustment_get_value (tree_view
->priv
->vadjustment
) +
5286 gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
))
5291 /* row not visible */
5293 && dy
+ height
<= gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
))
5295 /* row at the beginning -- fixed */
5297 area_below
= gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
)
5298 - area_above
- height
;
5300 else if (dy
>= (gtk_adjustment_get_upper (tree_view
->priv
->vadjustment
) -
5301 gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
)))
5303 /* row at the end -- fixed */
5304 area_above
= dy
- (gtk_adjustment_get_upper (tree_view
->priv
->vadjustment
) -
5305 gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
));
5306 area_below
= gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
) -
5307 area_above
- height
;
5311 area_above
= gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
) - height
;
5317 /* row somewhere in the middle, bring it to the top
5321 area_below
= total_height
- height
;
5327 /* the scroll to isn't valid; ignore it.
5330 gtk_tree_row_reference_free (tree_view
->priv
->scroll_to_path
);
5331 tree_view
->priv
->scroll_to_path
= NULL
;
5335 above_path
= gtk_tree_path_copy (path
);
5337 /* Now, we walk forwards and backwards, measuring rows. Unfortunately,
5338 * backwards is much slower then forward, as there is no iter_prev function.
5339 * We go forwards first in case we run out of tree. Then we go backwards to
5342 while (node
>= 0 && area_below
> 0)
5344 gboolean done
= FALSE
;
5347 node
= pspp_sheet_view_node_next (tree_view
, node
);
5350 gboolean has_next
= gtk_tree_model_iter_next (tree_view
->priv
->model
, &iter
);
5352 gtk_tree_path_next (path
);
5355 TREE_VIEW_INTERNAL_ASSERT_VOID (has_next
);
5365 area_below
-= ROW_HEIGHT (tree_view
);
5367 gtk_tree_path_free (path
);
5369 /* If we ran out of tree, and have extra area_below left, we need to add it
5372 area_above
+= area_below
;
5374 _pspp_sheet_view_find_node (tree_view
, above_path
, &node
);
5376 /* We walk backwards */
5377 while (area_above
> 0)
5379 node
= pspp_sheet_view_node_prev (tree_view
, node
);
5381 /* Always find the new path in the tree. We cannot just assume
5382 * a gtk_tree_path_prev() is enough here, as there might be children
5383 * in between this node and the previous sibling node. If this
5384 * appears to be a performance hotspot in profiles, we can look into
5385 * intrigate logic for keeping path, node and iter in sync like
5386 * we do for forward walks. (Which will be hard because of the lacking
5393 gtk_tree_path_free (above_path
);
5394 above_path
= _pspp_sheet_view_find_path (tree_view
, node
);
5396 gtk_tree_model_get_iter (tree_view
->priv
->model
, &iter
, above_path
);
5398 area_above
-= ROW_HEIGHT (tree_view
);
5401 /* set the dy here to scroll to the path,
5402 * and sync the top row accordingly
5404 pspp_sheet_view_set_top_row (tree_view
, above_path
, -area_above
);
5405 pspp_sheet_view_top_row_to_dy (tree_view
);
5407 /* update width/height and queue a resize */
5410 GtkRequisition requisition
;
5412 /* We temporarily guess a size, under the assumption that it will be the
5413 * same when we get our next size_allocate. If we don't do this, we'll be
5414 * in an inconsistent state if we call top_row_to_dy. */
5416 gtk_widget_size_request (GTK_WIDGET (tree_view
), &requisition
);
5417 gtk_adjustment_set_upper (tree_view
->priv
->hadjustment
, MAX (gtk_adjustment_get_upper (tree_view
->priv
->hadjustment
), (gfloat
)requisition
.width
));
5418 gtk_adjustment_set_upper (tree_view
->priv
->vadjustment
, MAX (gtk_adjustment_get_upper (tree_view
->priv
->vadjustment
), (gfloat
)requisition
.height
));
5419 gtk_adjustment_changed (tree_view
->priv
->hadjustment
);
5420 gtk_adjustment_changed (tree_view
->priv
->vadjustment
);
5421 gtk_widget_queue_resize (GTK_WIDGET (tree_view
));
5424 gtk_tree_row_reference_free (tree_view
->priv
->scroll_to_path
);
5425 tree_view
->priv
->scroll_to_path
= NULL
;
5428 gtk_tree_path_free (above_path
);
5430 if (tree_view
->priv
->scroll_to_column
)
5432 tree_view
->priv
->scroll_to_column
= NULL
;
5434 gtk_widget_queue_draw (GTK_WIDGET (tree_view
));
5438 initialize_fixed_height_mode (PsppSheetView
*tree_view
)
5440 if (!tree_view
->priv
->row_count
)
5443 if (tree_view
->priv
->fixed_height_set
)
5446 if (tree_view
->priv
->fixed_height
< 0)
5453 path
= _pspp_sheet_view_find_path (tree_view
, node
);
5454 gtk_tree_model_get_iter (tree_view
->priv
->model
, &iter
, path
);
5456 tree_view
->priv
->fixed_height
= validate_row (tree_view
, node
, &iter
, path
);
5458 gtk_tree_path_free (path
);
5460 g_object_notify (G_OBJECT (tree_view
), "fixed-height");
5464 /* Our strategy for finding nodes to validate is a little convoluted. We find
5465 * the left-most uninvalidated node. We then try walking right, validating
5466 * nodes. Once we find a valid node, we repeat the previous process of finding
5467 * the first invalid node.
5471 validate_rows_handler (PsppSheetView
*tree_view
)
5473 initialize_fixed_height_mode (tree_view
);
5474 if (tree_view
->priv
->validate_rows_timer
)
5476 g_source_remove (tree_view
->priv
->validate_rows_timer
);
5477 tree_view
->priv
->validate_rows_timer
= 0;
5484 do_presize_handler (PsppSheetView
*tree_view
)
5486 GtkRequisition requisition
;
5488 validate_visible_area (tree_view
);
5489 tree_view
->priv
->presize_handler_timer
= 0;
5491 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view
)))
5494 gtk_widget_size_request (GTK_WIDGET (tree_view
), &requisition
);
5496 gtk_adjustment_set_upper (tree_view
->priv
->hadjustment
, MAX (gtk_adjustment_get_upper (tree_view
->priv
->hadjustment
), (gfloat
)requisition
.width
));
5497 gtk_adjustment_set_upper (tree_view
->priv
->vadjustment
, MAX (gtk_adjustment_get_upper (tree_view
->priv
->vadjustment
), (gfloat
)requisition
.height
));
5498 gtk_adjustment_changed (tree_view
->priv
->hadjustment
);
5499 gtk_adjustment_changed (tree_view
->priv
->vadjustment
);
5500 gtk_widget_queue_resize (GTK_WIDGET (tree_view
));
5506 presize_handler_callback (gpointer data
)
5508 do_presize_handler (PSPP_SHEET_VIEW (data
));
5514 install_presize_handler (PsppSheetView
*tree_view
)
5516 if (! gtk_widget_get_realized (GTK_WIDGET (tree_view
)))
5519 if (! tree_view
->priv
->presize_handler_timer
)
5521 tree_view
->priv
->presize_handler_timer
=
5522 gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE
- 2, presize_handler_callback
, tree_view
, NULL
);
5524 if (! tree_view
->priv
->validate_rows_timer
)
5526 tree_view
->priv
->validate_rows_timer
=
5527 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_VALIDATE
, (GSourceFunc
) validate_rows_handler
, tree_view
, NULL
);
5532 scroll_sync_handler (PsppSheetView
*tree_view
)
5534 if (tree_view
->priv
->height
<= gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
))
5535 gtk_adjustment_set_value (GTK_ADJUSTMENT (tree_view
->priv
->vadjustment
), 0);
5536 else if (gtk_tree_row_reference_valid (tree_view
->priv
->top_row
))
5537 pspp_sheet_view_top_row_to_dy (tree_view
);
5539 pspp_sheet_view_dy_to_top_row (tree_view
);
5541 tree_view
->priv
->scroll_sync_timer
= 0;
5547 install_scroll_sync_handler (PsppSheetView
*tree_view
)
5549 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view
)))
5552 if (!tree_view
->priv
->scroll_sync_timer
)
5554 tree_view
->priv
->scroll_sync_timer
=
5555 gdk_threads_add_idle_full (PSPP_SHEET_VIEW_PRIORITY_SCROLL_SYNC
, (GSourceFunc
) scroll_sync_handler
, tree_view
, NULL
);
5560 pspp_sheet_view_set_top_row (PsppSheetView
*tree_view
,
5564 gtk_tree_row_reference_free (tree_view
->priv
->top_row
);
5568 tree_view
->priv
->top_row
= NULL
;
5569 tree_view
->priv
->top_row_dy
= 0;
5573 tree_view
->priv
->top_row
= gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view
), tree_view
->priv
->model
, path
);
5574 tree_view
->priv
->top_row_dy
= offset
;
5578 /* Always call this iff dy is in the visible range. If the tree is empty, then
5579 * it's set to be NULL, and top_row_dy is 0;
5582 pspp_sheet_view_dy_to_top_row (PsppSheetView
*tree_view
)
5588 if (tree_view
->priv
->row_count
== 0)
5590 pspp_sheet_view_set_top_row (tree_view
, NULL
, 0);
5594 offset
= pspp_sheet_view_find_offset (tree_view
,
5595 tree_view
->priv
->dy
,
5600 pspp_sheet_view_set_top_row (tree_view
, NULL
, 0);
5604 path
= _pspp_sheet_view_find_path (tree_view
, node
);
5605 pspp_sheet_view_set_top_row (tree_view
, path
, offset
);
5606 gtk_tree_path_free (path
);
5612 pspp_sheet_view_top_row_to_dy (PsppSheetView
*tree_view
)
5618 /* Avoid recursive calls */
5619 if (tree_view
->priv
->in_top_row_to_dy
)
5622 if (tree_view
->priv
->top_row
)
5623 path
= gtk_tree_row_reference_get_path (tree_view
->priv
->top_row
);
5630 _pspp_sheet_view_find_node (tree_view
, path
, &node
);
5633 gtk_tree_path_free (path
);
5637 /* keep dy and set new toprow */
5638 gtk_tree_row_reference_free (tree_view
->priv
->top_row
);
5639 tree_view
->priv
->top_row
= NULL
;
5640 tree_view
->priv
->top_row_dy
= 0;
5641 /* DO NOT install the idle handler */
5642 pspp_sheet_view_dy_to_top_row (tree_view
);
5646 if (ROW_HEIGHT (tree_view
) < tree_view
->priv
->top_row_dy
)
5648 /* new top row -- do NOT install the idle handler */
5649 pspp_sheet_view_dy_to_top_row (tree_view
);
5653 new_dy
= pspp_sheet_view_node_find_offset (tree_view
, node
);
5654 new_dy
+= tree_view
->priv
->top_row_dy
;
5656 if (new_dy
+ gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
) > tree_view
->priv
->height
)
5657 new_dy
= tree_view
->priv
->height
- gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
);
5659 new_dy
= MAX (0, new_dy
);
5661 tree_view
->priv
->in_top_row_to_dy
= TRUE
;
5662 gtk_adjustment_set_value (tree_view
->priv
->vadjustment
, (gdouble
)new_dy
);
5663 tree_view
->priv
->in_top_row_to_dy
= FALSE
;
5668 _pspp_sheet_view_install_mark_rows_col_dirty (PsppSheetView
*tree_view
)
5670 install_presize_handler (tree_view
);
5676 set_source_row (GdkDragContext
*context
,
5677 GtkTreeModel
*model
,
5678 GtkTreePath
*source_row
)
5680 g_object_set_data_full (G_OBJECT (context
),
5681 "gtk-tree-view-source-row",
5682 source_row
? gtk_tree_row_reference_new (model
, source_row
) : NULL
,
5683 (GDestroyNotify
) (source_row
? gtk_tree_row_reference_free
: NULL
));
5687 get_source_row (GdkDragContext
*context
)
5689 GtkTreeRowReference
*ref
=
5690 g_object_get_data (G_OBJECT (context
), "gtk-tree-view-source-row");
5693 return gtk_tree_row_reference_get_path (ref
);
5700 GtkTreeRowReference
*dest_row
;
5701 guint path_down_mode
: 1;
5702 guint empty_view_drop
: 1;
5703 guint drop_append_mode
: 1;
5708 dest_row_free (gpointer data
)
5710 DestRow
*dr
= (DestRow
*)data
;
5712 gtk_tree_row_reference_free (dr
->dest_row
);
5713 g_slice_free (DestRow
, dr
);
5717 set_dest_row (GdkDragContext
*context
,
5718 GtkTreeModel
*model
,
5719 GtkTreePath
*dest_row
,
5720 gboolean path_down_mode
,
5721 gboolean empty_view_drop
,
5722 gboolean drop_append_mode
)
5728 g_object_set_data_full (G_OBJECT (context
), "gtk-tree-view-dest-row",
5733 dr
= g_slice_new (DestRow
);
5735 dr
->dest_row
= gtk_tree_row_reference_new (model
, dest_row
);
5736 dr
->path_down_mode
= path_down_mode
!= FALSE
;
5737 dr
->empty_view_drop
= empty_view_drop
!= FALSE
;
5738 dr
->drop_append_mode
= drop_append_mode
!= FALSE
;
5740 g_object_set_data_full (G_OBJECT (context
), "gtk-tree-view-dest-row",
5741 dr
, (GDestroyNotify
) dest_row_free
);
5745 get_dest_row (GdkDragContext
*context
,
5746 gboolean
*path_down_mode
)
5749 g_object_get_data (G_OBJECT (context
), "gtk-tree-view-dest-row");
5753 GtkTreePath
*path
= NULL
;
5756 *path_down_mode
= dr
->path_down_mode
;
5759 path
= gtk_tree_row_reference_get_path (dr
->dest_row
);
5760 else if (dr
->empty_view_drop
)
5761 path
= gtk_tree_path_new_from_indices (0, -1);
5765 if (path
&& dr
->drop_append_mode
)
5766 gtk_tree_path_next (path
);
5774 /* Get/set whether drag_motion requested the drag data and
5775 * drag_data_received should thus not actually insert the data,
5776 * since the data doesn't result from a drop.
5779 set_status_pending (GdkDragContext
*context
,
5780 GdkDragAction suggested_action
)
5782 g_object_set_data (G_OBJECT (context
),
5783 "gtk-tree-view-status-pending",
5784 GINT_TO_POINTER (suggested_action
));
5787 static GdkDragAction
5788 get_status_pending (GdkDragContext
*context
)
5790 return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context
),
5791 "gtk-tree-view-status-pending"));
5794 static TreeViewDragInfo
*
5795 get_info (PsppSheetView
*tree_view
)
5797 return g_object_get_data (G_OBJECT (tree_view
), "gtk-tree-view-drag-info");
5801 destroy_info (TreeViewDragInfo
*di
)
5803 g_slice_free (TreeViewDragInfo
, di
);
5806 static TreeViewDragInfo
*
5807 ensure_info (PsppSheetView
*tree_view
)
5809 TreeViewDragInfo
*di
;
5811 di
= get_info (tree_view
);
5815 di
= g_slice_new0 (TreeViewDragInfo
);
5817 g_object_set_data_full (G_OBJECT (tree_view
),
5818 "gtk-tree-view-drag-info",
5820 (GDestroyNotify
) destroy_info
);
5827 remove_info (PsppSheetView
*tree_view
)
5829 g_object_set_data (G_OBJECT (tree_view
), "gtk-tree-view-drag-info", NULL
);
5834 drag_scan_timeout (gpointer data
)
5836 PsppSheetView
*tree_view
;
5838 GdkModifierType state
;
5839 GtkTreePath
*path
= NULL
;
5840 PsppSheetViewColumn
*column
= NULL
;
5841 GdkRectangle visible_rect
;
5843 GDK_THREADS_ENTER ();
5845 tree_view
= PSPP_SHEET_VIEW (data
);
5847 gdk_window_get_pointer (tree_view
->priv
->bin_window
,
5850 pspp_sheet_view_get_visible_rect (tree_view
, &visible_rect
);
5852 /* See if we are near the edge. */
5853 if ((x
- visible_rect
.x
) < SCROLL_EDGE_SIZE
||
5854 (visible_rect
.x
+ visible_rect
.width
- x
) < SCROLL_EDGE_SIZE
||
5855 (y
- visible_rect
.y
) < SCROLL_EDGE_SIZE
||
5856 (visible_rect
.y
+ visible_rect
.height
- y
) < SCROLL_EDGE_SIZE
)
5858 pspp_sheet_view_get_path_at_pos (tree_view
,
5859 tree_view
->priv
->bin_window
,
5868 pspp_sheet_view_scroll_to_cell (tree_view
,
5874 gtk_tree_path_free (path
);
5878 GDK_THREADS_LEAVE ();
5885 add_scroll_timeout (PsppSheetView
*tree_view
)
5887 if (tree_view
->priv
->scroll_timeout
== 0)
5889 tree_view
->priv
->scroll_timeout
=
5890 gdk_threads_add_timeout (150, scroll_row_timeout
, tree_view
);
5895 remove_scroll_timeout (PsppSheetView
*tree_view
)
5897 if (tree_view
->priv
->scroll_timeout
!= 0)
5899 g_source_remove (tree_view
->priv
->scroll_timeout
);
5900 tree_view
->priv
->scroll_timeout
= 0;
5905 check_model_dnd (GtkTreeModel
*model
,
5906 GType required_iface
,
5907 const gchar
*signal
)
5909 if (model
== NULL
|| !G_TYPE_CHECK_INSTANCE_TYPE ((model
), required_iface
))
5911 g_warning ("You must override the default '%s' handler "
5912 "on PsppSheetView when using models that don't support "
5913 "the %s interface and enabling drag-and-drop. The simplest way to do this "
5914 "is to connect to '%s' and call "
5915 "g_signal_stop_emission_by_name() in your signal handler to prevent "
5916 "the default handler from running. Look at the source code "
5917 "for the default handler in gtktreeview.c to get an idea what "
5918 "your handler should do. (gtktreeview.c is in the GTK source "
5919 "code.) If you're using GTK from a language other than C, "
5920 "there may be a more natural way to override default handlers, e.g. via derivation.",
5921 signal
, g_type_name (required_iface
), signal
);
5929 scroll_row_timeout (gpointer data
)
5931 PsppSheetView
*tree_view
= data
;
5933 pspp_sheet_view_horizontal_autoscroll (tree_view
);
5934 pspp_sheet_view_vertical_autoscroll (tree_view
);
5936 if (tree_view
->priv
->rubber_band_status
== RUBBER_BAND_ACTIVE
)
5937 pspp_sheet_view_update_rubber_band (tree_view
);
5942 /* Returns TRUE if event should not be propagated to parent widgets */
5944 set_destination_row (PsppSheetView
*tree_view
,
5945 GdkDragContext
*context
,
5946 /* coordinates relative to the widget */
5949 GdkDragAction
*suggested_action
,
5952 GtkTreePath
*path
= NULL
;
5953 PsppSheetViewDropPosition pos
;
5954 PsppSheetViewDropPosition old_pos
;
5955 TreeViewDragInfo
*di
;
5957 GtkTreePath
*old_dest_path
= NULL
;
5958 gboolean can_drop
= FALSE
;
5960 *suggested_action
= 0;
5963 widget
= GTK_WIDGET (tree_view
);
5965 di
= get_info (tree_view
);
5967 if (di
== NULL
|| y
- TREE_VIEW_HEADER_HEIGHT (tree_view
) < 0)
5969 /* someone unset us as a drag dest, note that if
5970 * we return FALSE drag_leave isn't called
5973 pspp_sheet_view_set_drag_dest_row (tree_view
,
5975 PSPP_SHEET_VIEW_DROP_BEFORE
);
5977 remove_scroll_timeout (PSPP_SHEET_VIEW (widget
));
5979 return FALSE
; /* no longer a drop site */
5982 *target
= gtk_drag_dest_find_target (widget
, context
,
5983 gtk_drag_dest_get_target_list (widget
));
5984 if (*target
== GDK_NONE
)
5989 if (!pspp_sheet_view_get_dest_row_at_pos (tree_view
,
5995 GtkTreeModel
*model
;
5997 /* the row got dropped on empty space, let's setup a special case
6001 gtk_tree_path_free (path
);
6003 model
= pspp_sheet_view_get_model (tree_view
);
6005 n_children
= gtk_tree_model_iter_n_children (model
, NULL
);
6008 pos
= PSPP_SHEET_VIEW_DROP_AFTER
;
6009 path
= gtk_tree_path_new_from_indices (n_children
- 1, -1);
6013 pos
= PSPP_SHEET_VIEW_DROP_BEFORE
;
6014 path
= gtk_tree_path_new_from_indices (0, -1);
6024 /* If we left the current row's "open" zone, unset the timeout for
6027 pspp_sheet_view_get_drag_dest_row (tree_view
,
6032 gtk_tree_path_free (old_dest_path
);
6034 if (TRUE
/* FIXME if the location droppable predicate */)
6042 GtkWidget
*source_widget
;
6044 *suggested_action
= gdk_drag_context_get_suggested_action (context
);
6045 source_widget
= gtk_drag_get_source_widget (context
);
6047 if (source_widget
== widget
)
6049 /* Default to MOVE, unless the user has
6050 * pressed ctrl or shift to affect available actions
6052 if ((gdk_drag_context_get_actions (context
) & GDK_ACTION_MOVE
) != 0)
6053 *suggested_action
= GDK_ACTION_MOVE
;
6056 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget
),
6061 /* can't drop here */
6062 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget
),
6064 PSPP_SHEET_VIEW_DROP_BEFORE
);
6068 gtk_tree_path_free (path
);
6074 get_logical_dest_row (PsppSheetView
*tree_view
,
6075 gboolean
*path_down_mode
,
6076 gboolean
*drop_append_mode
)
6078 /* adjust path to point to the row the drop goes in front of */
6079 GtkTreePath
*path
= NULL
;
6080 PsppSheetViewDropPosition pos
;
6082 g_return_val_if_fail (path_down_mode
!= NULL
, NULL
);
6083 g_return_val_if_fail (drop_append_mode
!= NULL
, NULL
);
6085 *path_down_mode
= FALSE
;
6086 *drop_append_mode
= 0;
6088 pspp_sheet_view_get_drag_dest_row (tree_view
, &path
, &pos
);
6093 if (pos
== PSPP_SHEET_VIEW_DROP_BEFORE
)
6095 else if (pos
== PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE
||
6096 pos
== PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER
)
6097 *path_down_mode
= TRUE
;
6101 GtkTreeModel
*model
= pspp_sheet_view_get_model (tree_view
);
6103 g_assert (pos
== PSPP_SHEET_VIEW_DROP_AFTER
);
6105 if (!gtk_tree_model_get_iter (model
, &iter
, path
) ||
6106 !gtk_tree_model_iter_next (model
, &iter
))
6107 *drop_append_mode
= 1;
6110 *drop_append_mode
= 0;
6111 gtk_tree_path_next (path
);
6119 pspp_sheet_view_maybe_begin_dragging_row (PsppSheetView
*tree_view
,
6120 GdkEventMotion
*event
)
6122 GtkWidget
*widget
= GTK_WIDGET (tree_view
);
6123 GdkDragContext
*context
;
6124 TreeViewDragInfo
*di
;
6125 GtkTreePath
*path
= NULL
;
6127 gint cell_x
, cell_y
;
6128 GtkTreeModel
*model
;
6129 gboolean retval
= FALSE
;
6131 di
= get_info (tree_view
);
6133 if (di
== NULL
|| !di
->source_set
)
6136 if (tree_view
->priv
->pressed_button
< 0)
6139 if (!gtk_drag_check_threshold (widget
,
6140 tree_view
->priv
->press_start_x
,
6141 tree_view
->priv
->press_start_y
,
6142 event
->x
, event
->y
))
6145 model
= pspp_sheet_view_get_model (tree_view
);
6150 button
= tree_view
->priv
->pressed_button
;
6151 tree_view
->priv
->pressed_button
= -1;
6153 pspp_sheet_view_get_path_at_pos (tree_view
,
6154 tree_view
->priv
->press_start_x
,
6155 tree_view
->priv
->press_start_y
,
6164 if (!GTK_IS_TREE_DRAG_SOURCE (model
) ||
6165 !gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (model
),
6169 if (!(GDK_BUTTON1_MASK
<< (button
- 1) & di
->start_button_mask
))
6172 /* Now we can begin the drag */
6176 context
= gtk_drag_begin (widget
,
6177 gtk_drag_source_get_target_list (widget
),
6182 set_source_row (context
, model
, path
);
6186 gtk_tree_path_free (path
);
6194 pspp_sheet_view_drag_begin (GtkWidget
*widget
,
6195 GdkDragContext
*context
)
6198 PsppSheetView
*tree_view
;
6199 GtkTreePath
*path
= NULL
;
6200 gint cell_x
, cell_y
;
6202 TreeViewDragInfo
*di
;
6204 tree_view
= PSPP_SHEET_VIEW (widget
);
6206 /* if the user uses a custom DND source impl, we don't set the icon here */
6207 di
= get_info (tree_view
);
6209 if (di
== NULL
|| !di
->source_set
)
6212 pspp_sheet_view_get_path_at_pos (tree_view
,
6213 tree_view
->priv
->press_start_x
,
6214 tree_view
->priv
->press_start_y
,
6220 g_return_if_fail (path
!= NULL
);
6222 row_pix
= pspp_sheet_view_create_row_drag_icon (tree_view
,
6225 gtk_drag_set_icon_pixmap (context
,
6226 gdk_drawable_get_colormap (row_pix
),
6229 /* the + 1 is for the black border in the icon */
6230 tree_view
->priv
->press_start_x
+ 1,
6233 g_object_unref (row_pix
);
6234 gtk_tree_path_free (path
);
6240 pspp_sheet_view_drag_end (GtkWidget
*widget
,
6241 GdkDragContext
*context
)
6246 /* Default signal implementations for the drag signals */
6248 pspp_sheet_view_drag_data_get (GtkWidget
*widget
,
6249 GdkDragContext
*context
,
6250 GtkSelectionData
*selection_data
,
6254 PsppSheetView
*tree_view
;
6255 GtkTreeModel
*model
;
6256 TreeViewDragInfo
*di
;
6257 GtkTreePath
*source_row
;
6259 tree_view
= PSPP_SHEET_VIEW (widget
);
6261 model
= pspp_sheet_view_get_model (tree_view
);
6266 di
= get_info (PSPP_SHEET_VIEW (widget
));
6271 source_row
= get_source_row (context
);
6273 if (source_row
== NULL
)
6276 /* We can implement the GTK_TREE_MODEL_ROW target generically for
6277 * any model; for DragSource models there are some other targets
6281 if (GTK_IS_TREE_DRAG_SOURCE (model
) &&
6282 gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (model
),
6287 /* If drag_data_get does nothing, try providing row data. */
6288 if (gtk_selection_data_get_target (selection_data
) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6290 gtk_tree_set_row_drag_data (selection_data
,
6296 gtk_tree_path_free (source_row
);
6301 pspp_sheet_view_drag_data_delete (GtkWidget
*widget
,
6302 GdkDragContext
*context
)
6304 TreeViewDragInfo
*di
;
6305 GtkTreeModel
*model
;
6306 PsppSheetView
*tree_view
;
6307 GtkTreePath
*source_row
;
6309 tree_view
= PSPP_SHEET_VIEW (widget
);
6310 model
= pspp_sheet_view_get_model (tree_view
);
6312 if (!check_model_dnd (model
, GTK_TYPE_TREE_DRAG_SOURCE
, "drag_data_delete"))
6315 di
= get_info (tree_view
);
6320 source_row
= get_source_row (context
);
6322 if (source_row
== NULL
)
6325 gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (model
),
6328 gtk_tree_path_free (source_row
);
6330 set_source_row (context
, NULL
, NULL
);
6334 pspp_sheet_view_drag_leave (GtkWidget
*widget
,
6335 GdkDragContext
*context
,
6338 /* unset any highlight row */
6339 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget
),
6341 PSPP_SHEET_VIEW_DROP_BEFORE
);
6343 remove_scroll_timeout (PSPP_SHEET_VIEW (widget
));
6348 pspp_sheet_view_drag_motion (GtkWidget
*widget
,
6349 GdkDragContext
*context
,
6350 /* coordinates relative to the widget */
6356 GtkTreePath
*path
= NULL
;
6357 PsppSheetViewDropPosition pos
;
6358 PsppSheetView
*tree_view
;
6359 GdkDragAction suggested_action
= 0;
6362 tree_view
= PSPP_SHEET_VIEW (widget
);
6364 if (!set_destination_row (tree_view
, context
, x
, y
, &suggested_action
, &target
))
6367 pspp_sheet_view_get_drag_dest_row (tree_view
, &path
, &pos
);
6369 /* we only know this *after* set_desination_row */
6370 empty
= tree_view
->priv
->empty_view_drop
;
6372 if (path
== NULL
&& !empty
)
6374 /* Can't drop here. */
6375 gdk_drag_status (context
, 0, time
);
6379 if (tree_view
->priv
->open_dest_timeout
== 0 &&
6380 (pos
== PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER
||
6381 pos
== PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE
))
6387 add_scroll_timeout (tree_view
);
6390 if (target
== gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
6392 /* Request data so we can use the source row when
6393 * determining whether to accept the drop
6395 set_status_pending (context
, suggested_action
);
6396 gtk_drag_get_data (widget
, context
, target
, time
);
6400 set_status_pending (context
, 0);
6401 gdk_drag_status (context
, suggested_action
, time
);
6406 gtk_tree_path_free (path
);
6413 pspp_sheet_view_drag_drop (GtkWidget
*widget
,
6414 GdkDragContext
*context
,
6415 /* coordinates relative to the widget */
6420 PsppSheetView
*tree_view
;
6422 GdkDragAction suggested_action
= 0;
6423 GdkAtom target
= GDK_NONE
;
6424 TreeViewDragInfo
*di
;
6425 GtkTreeModel
*model
;
6426 gboolean path_down_mode
;
6427 gboolean drop_append_mode
;
6429 tree_view
= PSPP_SHEET_VIEW (widget
);
6431 model
= pspp_sheet_view_get_model (tree_view
);
6433 remove_scroll_timeout (PSPP_SHEET_VIEW (widget
));
6435 di
= get_info (tree_view
);
6440 if (!check_model_dnd (model
, GTK_TYPE_TREE_DRAG_DEST
, "drag_drop"))
6443 if (!set_destination_row (tree_view
, context
, x
, y
, &suggested_action
, &target
))
6446 path
= get_logical_dest_row (tree_view
, &path_down_mode
, &drop_append_mode
);
6448 if (target
!= GDK_NONE
&& path
!= NULL
)
6450 /* in case a motion had requested drag data, change things so we
6451 * treat drag data receives as a drop.
6453 set_status_pending (context
, 0);
6454 set_dest_row (context
, model
, path
,
6455 path_down_mode
, tree_view
->priv
->empty_view_drop
,
6460 gtk_tree_path_free (path
);
6462 /* Unset this thing */
6463 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget
),
6465 PSPP_SHEET_VIEW_DROP_BEFORE
);
6467 if (target
!= GDK_NONE
)
6469 gtk_drag_get_data (widget
, context
, target
, time
);
6477 pspp_sheet_view_drag_data_received (GtkWidget
*widget
,
6478 GdkDragContext
*context
,
6479 /* coordinates relative to the widget */
6482 GtkSelectionData
*selection_data
,
6487 TreeViewDragInfo
*di
;
6488 gboolean accepted
= FALSE
;
6489 GtkTreeModel
*model
;
6490 PsppSheetView
*tree_view
;
6491 GtkTreePath
*dest_row
;
6492 GdkDragAction suggested_action
;
6493 gboolean path_down_mode
;
6494 gboolean drop_append_mode
;
6496 tree_view
= PSPP_SHEET_VIEW (widget
);
6498 model
= pspp_sheet_view_get_model (tree_view
);
6500 if (!check_model_dnd (model
, GTK_TYPE_TREE_DRAG_DEST
, "drag_data_received"))
6503 di
= get_info (tree_view
);
6508 suggested_action
= get_status_pending (context
);
6510 if (suggested_action
)
6512 /* We are getting this data due to a request in drag_motion,
6513 * rather than due to a request in drag_drop, so we are just
6514 * supposed to call drag_status, not actually paste in the
6517 path
= get_logical_dest_row (tree_view
, &path_down_mode
,
6521 suggested_action
= 0;
6522 else if (path_down_mode
)
6523 gtk_tree_path_down (path
);
6525 if (suggested_action
)
6527 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model
),
6533 path_down_mode
= FALSE
;
6534 gtk_tree_path_up (path
);
6536 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model
),
6539 suggested_action
= 0;
6542 suggested_action
= 0;
6546 gdk_drag_status (context
, suggested_action
, time
);
6549 gtk_tree_path_free (path
);
6551 /* If you can't drop, remove user drop indicator until the next motion */
6552 if (suggested_action
== 0)
6553 pspp_sheet_view_set_drag_dest_row (PSPP_SHEET_VIEW (widget
),
6555 PSPP_SHEET_VIEW_DROP_BEFORE
);
6560 dest_row
= get_dest_row (context
, &path_down_mode
);
6562 if (dest_row
== NULL
)
6565 if (gtk_selection_data_get_length (selection_data
) >= 0)
6569 gtk_tree_path_down (dest_row
);
6570 if (!gtk_tree_drag_dest_row_drop_possible (GTK_TREE_DRAG_DEST (model
),
6571 dest_row
, selection_data
))
6572 gtk_tree_path_up (dest_row
);
6576 if (gtk_selection_data_get_length (selection_data
) >= 0)
6578 if (gtk_tree_drag_dest_drag_data_received (GTK_TREE_DRAG_DEST (model
),
6584 gtk_drag_finish (context
,
6586 (gdk_drag_context_get_actions (context
) == GDK_ACTION_MOVE
),
6589 if (gtk_tree_path_get_depth (dest_row
) == 1
6590 && gtk_tree_path_get_indices (dest_row
)[0] == 0)
6592 /* special special case drag to "0", scroll to first item */
6593 if (!tree_view
->priv
->scroll_to_path
)
6594 pspp_sheet_view_scroll_to_cell (tree_view
, dest_row
, NULL
, FALSE
, 0.0, 0.0);
6597 gtk_tree_path_free (dest_row
);
6600 set_dest_row (context
, NULL
, NULL
, FALSE
, FALSE
, FALSE
);
6605 /* GtkContainer Methods
6610 pspp_sheet_view_remove (GtkContainer
*container
,
6613 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (container
);
6614 PsppSheetViewChild
*child
= NULL
;
6617 tmp_list
= tree_view
->priv
->children
;
6620 child
= tmp_list
->data
;
6621 if (child
->widget
== widget
)
6623 gtk_widget_unparent (widget
);
6625 tree_view
->priv
->children
= g_list_remove_link (tree_view
->priv
->children
, tmp_list
);
6626 g_list_free_1 (tmp_list
);
6627 g_slice_free (PsppSheetViewChild
, child
);
6631 tmp_list
= tmp_list
->next
;
6634 tmp_list
= tree_view
->priv
->columns
;
6638 PsppSheetViewColumn
*column
;
6639 column
= tmp_list
->data
;
6640 if (column
->button
== widget
)
6642 gtk_widget_unparent (widget
);
6645 tmp_list
= tmp_list
->next
;
6650 pspp_sheet_view_forall (GtkContainer
*container
,
6651 gboolean include_internals
,
6652 GtkCallback callback
,
6653 gpointer callback_data
)
6655 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (container
);
6656 PsppSheetViewChild
*child
= NULL
;
6657 PsppSheetViewColumn
*column
;
6660 tmp_list
= tree_view
->priv
->children
;
6663 child
= tmp_list
->data
;
6664 tmp_list
= tmp_list
->next
;
6666 (* callback
) (child
->widget
, callback_data
);
6668 if (include_internals
== FALSE
)
6671 for (tmp_list
= tree_view
->priv
->columns
; tmp_list
; tmp_list
= tmp_list
->next
)
6673 column
= tmp_list
->data
;
6676 (* callback
) (column
->button
, callback_data
);
6680 /* Returns TRUE if the treeview contains no "special" (editable or activatable)
6681 * cells. If so we draw one big row-spanning focus rectangle.
6684 pspp_sheet_view_has_special_cell (PsppSheetView
*tree_view
)
6688 if (tree_view
->priv
->special_cells
!= PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT
)
6689 return tree_view
->priv
->special_cells
= PSPP_SHEET_VIEW_SPECIAL_CELLS_YES
;
6691 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
6693 if (!((PsppSheetViewColumn
*)list
->data
)->visible
)
6695 if (_pspp_sheet_view_column_count_special_cells (list
->data
))
6703 pspp_sheet_view_focus_column (PsppSheetView
*tree_view
,
6704 PsppSheetViewColumn
*focus_column
,
6705 gboolean clamp_column_visible
)
6707 g_return_if_fail (focus_column
!= NULL
);
6709 tree_view
->priv
->focus_column
= focus_column
;
6711 if (gtk_container_get_focus_child (GTK_CONTAINER (tree_view
)) != focus_column
->button
)
6712 gtk_widget_grab_focus (focus_column
->button
);
6714 if (clamp_column_visible
)
6715 pspp_sheet_view_clamp_column_visible (tree_view
, focus_column
, FALSE
);
6718 /* Returns TRUE if the focus is within the headers, after the focus operation is
6722 pspp_sheet_view_header_focus (PsppSheetView
*tree_view
,
6723 GtkDirectionType dir
,
6724 gboolean clamp_column_visible
)
6726 GtkWidget
*focus_child
;
6727 PsppSheetViewColumn
*focus_column
;
6728 GList
*last_column
, *first_column
;
6732 if (! PSPP_SHEET_VIEW_FLAG_SET (tree_view
, PSPP_SHEET_VIEW_HEADERS_VISIBLE
))
6735 focus_child
= gtk_container_get_focus_child (GTK_CONTAINER (tree_view
));
6737 first_column
= tree_view
->priv
->columns
;
6738 while (first_column
)
6740 PsppSheetViewColumn
*c
= PSPP_SHEET_VIEW_COLUMN (first_column
->data
);
6742 if (pspp_sheet_view_column_can_focus (c
) && c
->visible
)
6744 first_column
= first_column
->next
;
6747 /* No headers are visible, or are focusable. We can't focus in or out.
6749 if (first_column
== NULL
)
6752 last_column
= g_list_last (tree_view
->priv
->columns
);
6755 PsppSheetViewColumn
*c
= PSPP_SHEET_VIEW_COLUMN (last_column
->data
);
6757 if (pspp_sheet_view_column_can_focus (c
) && c
->visible
)
6759 last_column
= last_column
->prev
;
6763 rtl
= (gtk_widget_get_direction (GTK_WIDGET (tree_view
)) == GTK_TEXT_DIR_RTL
);
6767 case GTK_DIR_TAB_BACKWARD
:
6768 case GTK_DIR_TAB_FORWARD
:
6771 if (focus_child
== NULL
)
6773 if (tree_view
->priv
->focus_column
!= NULL
&&
6774 pspp_sheet_view_column_can_focus (tree_view
->priv
->focus_column
))
6775 focus_column
= tree_view
->priv
->focus_column
;
6777 focus_column
= first_column
->data
;
6778 pspp_sheet_view_focus_column (tree_view
, focus_column
,
6779 clamp_column_visible
);
6786 if (focus_child
== NULL
)
6788 if (tree_view
->priv
->focus_column
!= NULL
)
6789 focus_column
= tree_view
->priv
->focus_column
;
6790 else if (dir
== GTK_DIR_LEFT
)
6791 focus_column
= last_column
->data
;
6793 focus_column
= first_column
->data
;
6794 pspp_sheet_view_focus_column (tree_view
, focus_column
,
6795 clamp_column_visible
);
6799 if (gtk_widget_child_focus (focus_child
, dir
))
6801 /* The focus moves inside the button. */
6802 /* This is probably a great example of bad UI */
6803 if (clamp_column_visible
)
6804 pspp_sheet_view_clamp_column_visible (tree_view
,
6805 tree_view
->priv
->focus_column
,
6810 /* We need to move the focus among the row of buttons. */
6811 for (tmp_list
= tree_view
->priv
->columns
; tmp_list
; tmp_list
= tmp_list
->next
)
6812 if (PSPP_SHEET_VIEW_COLUMN (tmp_list
->data
)->button
== focus_child
)
6815 if ((tmp_list
== first_column
&& dir
== (rtl
? GTK_DIR_RIGHT
: GTK_DIR_LEFT
))
6816 || (tmp_list
== last_column
&& dir
== (rtl
? GTK_DIR_LEFT
: GTK_DIR_RIGHT
)))
6818 gtk_widget_error_bell (GTK_WIDGET (tree_view
));
6824 PsppSheetViewColumn
*column
;
6826 if (dir
== (rtl
? GTK_DIR_LEFT
: GTK_DIR_RIGHT
))
6827 tmp_list
= tmp_list
->next
;
6829 tmp_list
= tmp_list
->prev
;
6831 if (tmp_list
== NULL
)
6833 g_warning ("Internal button not found");
6836 column
= tmp_list
->data
;
6837 if (column
->visible
&&
6838 pspp_sheet_view_column_can_focus (column
))
6842 pspp_sheet_view_focus_column (tree_view
, column
,
6843 clamp_column_visible
);
6851 g_assert_not_reached ();
6858 /* This function returns in 'path' the first focusable path, if the given path
6859 * is already focusable, it's the returned one.
6863 search_first_focusable_path (PsppSheetView
*tree_view
,
6865 gboolean search_forward
,
6868 /* XXX this function is trivial given that the sheetview doesn't support
6872 if (!path
|| !*path
)
6875 _pspp_sheet_view_find_node (tree_view
, *path
, &node
);
6883 return (*path
!= NULL
);
6887 pspp_sheet_view_focus (GtkWidget
*widget
,
6888 GtkDirectionType direction
)
6890 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
6891 GtkContainer
*container
= GTK_CONTAINER (widget
);
6892 GtkWidget
*focus_child
;
6894 if (!gtk_widget_is_sensitive (widget
) || !gtk_widget_get_can_focus (widget
))
6897 focus_child
= gtk_container_get_focus_child (container
);
6899 pspp_sheet_view_stop_editing (PSPP_SHEET_VIEW (widget
), FALSE
);
6900 /* Case 1. Headers currently have focus. */
6907 pspp_sheet_view_header_focus (tree_view
, direction
, TRUE
);
6909 case GTK_DIR_TAB_BACKWARD
:
6912 case GTK_DIR_TAB_FORWARD
:
6914 gtk_widget_grab_focus (widget
);
6917 g_assert_not_reached ();
6922 /* Case 2. We don't have focus at all. */
6923 if (!gtk_widget_has_focus (widget
))
6925 if (!pspp_sheet_view_header_focus (tree_view
, direction
, FALSE
))
6926 gtk_widget_grab_focus (widget
);
6930 /* Case 3. We have focus already. */
6931 if (direction
== GTK_DIR_TAB_BACKWARD
)
6932 return (pspp_sheet_view_header_focus (tree_view
, direction
, FALSE
));
6933 else if (direction
== GTK_DIR_TAB_FORWARD
)
6936 /* Other directions caught by the keybindings */
6937 gtk_widget_grab_focus (widget
);
6942 pspp_sheet_view_grab_focus (GtkWidget
*widget
)
6944 GTK_WIDGET_CLASS (pspp_sheet_view_parent_class
)->grab_focus (widget
);
6946 pspp_sheet_view_focus_to_cursor (PSPP_SHEET_VIEW (widget
));
6950 pspp_sheet_view_style_set (GtkWidget
*widget
,
6951 GtkStyle
*previous_style
)
6953 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
6955 PsppSheetViewColumn
*column
;
6957 if (gtk_widget_get_realized (widget
))
6959 gdk_window_set_background (tree_view
->priv
->bin_window
, >k_widget_get_style (widget
)->base
[gtk_widget_get_state (widget
)]);
6960 gtk_style_set_background (gtk_widget_get_style (widget
), tree_view
->priv
->header_window
, GTK_STATE_NORMAL
);
6961 pspp_sheet_view_set_grid_lines (tree_view
, tree_view
->priv
->grid_lines
);
6964 gtk_widget_style_get (widget
,
6965 "expander-size", &tree_view
->priv
->expander_size
,
6967 tree_view
->priv
->expander_size
+= EXPANDER_EXTRA_PADDING
;
6969 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
6971 column
= list
->data
;
6972 _pspp_sheet_view_column_cell_set_dirty (column
);
6975 tree_view
->priv
->fixed_height
= -1;
6977 /* Invalidate cached button style. */
6978 if (tree_view
->priv
->button_style
)
6980 g_object_unref (tree_view
->priv
->button_style
);
6981 tree_view
->priv
->button_style
= NULL
;
6984 gtk_widget_queue_resize (widget
);
6989 pspp_sheet_view_set_focus_child (GtkContainer
*container
,
6992 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (container
);
6995 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
6997 if (PSPP_SHEET_VIEW_COLUMN (list
->data
)->button
== child
)
6999 tree_view
->priv
->focus_column
= PSPP_SHEET_VIEW_COLUMN (list
->data
);
7004 GTK_CONTAINER_CLASS (pspp_sheet_view_parent_class
)->set_focus_child (container
, child
);
7008 pspp_sheet_view_set_adjustments (PsppSheetView
*tree_view
,
7009 GtkAdjustment
*hadj
,
7010 GtkAdjustment
*vadj
)
7012 gboolean need_adjust
= FALSE
;
7014 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
7017 g_return_if_fail (GTK_IS_ADJUSTMENT (hadj
));
7019 hadj
= GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7021 g_return_if_fail (GTK_IS_ADJUSTMENT (vadj
));
7023 vadj
= GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
7025 if (tree_view
->priv
->hadjustment
&& (tree_view
->priv
->hadjustment
!= hadj
))
7027 g_signal_handlers_disconnect_by_func (tree_view
->priv
->hadjustment
,
7028 pspp_sheet_view_adjustment_changed
,
7030 g_object_unref (tree_view
->priv
->hadjustment
);
7033 if (tree_view
->priv
->vadjustment
&& (tree_view
->priv
->vadjustment
!= vadj
))
7035 g_signal_handlers_disconnect_by_func (tree_view
->priv
->vadjustment
,
7036 pspp_sheet_view_adjustment_changed
,
7038 g_object_unref (tree_view
->priv
->vadjustment
);
7041 if (tree_view
->priv
->hadjustment
!= hadj
)
7043 tree_view
->priv
->hadjustment
= hadj
;
7044 g_object_ref_sink (tree_view
->priv
->hadjustment
);
7046 g_signal_connect (tree_view
->priv
->hadjustment
, "value-changed",
7047 G_CALLBACK (pspp_sheet_view_adjustment_changed
),
7052 if (tree_view
->priv
->vadjustment
!= vadj
)
7054 tree_view
->priv
->vadjustment
= vadj
;
7055 g_object_ref_sink (tree_view
->priv
->vadjustment
);
7057 g_signal_connect (tree_view
->priv
->vadjustment
, "value-changed",
7058 G_CALLBACK (pspp_sheet_view_adjustment_changed
),
7064 pspp_sheet_view_adjustment_changed (NULL
, tree_view
);
7069 pspp_sheet_view_real_move_cursor (PsppSheetView
*tree_view
,
7070 GtkMovementStep step
,
7073 PsppSheetSelectMode mode
;
7074 GdkModifierType state
;
7076 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), FALSE
);
7077 g_return_val_if_fail (step
== GTK_MOVEMENT_LOGICAL_POSITIONS
||
7078 step
== GTK_MOVEMENT_VISUAL_POSITIONS
||
7079 step
== GTK_MOVEMENT_DISPLAY_LINES
||
7080 step
== GTK_MOVEMENT_PAGES
||
7081 step
== GTK_MOVEMENT_BUFFER_ENDS
||
7082 step
== GTK_MOVEMENT_DISPLAY_LINE_ENDS
, FALSE
);
7084 if (tree_view
->priv
->row_count
== 0)
7086 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view
)))
7089 pspp_sheet_view_stop_editing (tree_view
, FALSE
);
7090 PSPP_SHEET_VIEW_SET_FLAG (tree_view
, PSPP_SHEET_VIEW_DRAW_KEYFOCUS
);
7091 gtk_widget_grab_focus (GTK_WIDGET (tree_view
));
7094 if (gtk_get_current_event_state (&state
))
7096 if ((state
& GDK_CONTROL_MASK
) == GDK_CONTROL_MASK
)
7097 mode
|= PSPP_SHEET_SELECT_MODE_TOGGLE
;
7098 if ((state
& GDK_SHIFT_MASK
) == GDK_SHIFT_MASK
)
7099 mode
|= PSPP_SHEET_SELECT_MODE_EXTEND
;
7101 /* else we assume not pressed */
7105 case GTK_MOVEMENT_LOGICAL_POSITIONS
:
7106 pspp_sheet_view_move_cursor_tab (tree_view
, count
);
7108 case GTK_MOVEMENT_VISUAL_POSITIONS
:
7109 pspp_sheet_view_move_cursor_left_right (tree_view
, count
, mode
);
7111 case GTK_MOVEMENT_DISPLAY_LINES
:
7112 pspp_sheet_view_move_cursor_up_down (tree_view
, count
, mode
);
7114 case GTK_MOVEMENT_PAGES
:
7115 pspp_sheet_view_move_cursor_page_up_down (tree_view
, count
, mode
);
7117 case GTK_MOVEMENT_BUFFER_ENDS
:
7118 pspp_sheet_view_move_cursor_start_end (tree_view
, count
, mode
);
7120 case GTK_MOVEMENT_DISPLAY_LINE_ENDS
:
7121 pspp_sheet_view_move_cursor_line_start_end (tree_view
, count
, mode
);
7124 g_assert_not_reached ();
7131 pspp_sheet_view_put (PsppSheetView
*tree_view
,
7132 GtkWidget
*child_widget
,
7133 /* in bin_window coordinates */
7139 PsppSheetViewChild
*child
;
7141 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
7142 g_return_if_fail (GTK_IS_WIDGET (child_widget
));
7144 child
= g_slice_new (PsppSheetViewChild
);
7146 child
->widget
= child_widget
;
7149 child
->width
= width
;
7150 child
->height
= height
;
7152 tree_view
->priv
->children
= g_list_append (tree_view
->priv
->children
, child
);
7154 if (gtk_widget_get_realized (GTK_WIDGET (tree_view
)))
7155 gtk_widget_set_parent_window (child
->widget
, tree_view
->priv
->bin_window
);
7157 gtk_widget_set_parent (child_widget
, GTK_WIDGET (tree_view
));
7161 _pspp_sheet_view_child_move_resize (PsppSheetView
*tree_view
,
7163 /* in tree coordinates */
7169 PsppSheetViewChild
*child
= NULL
;
7171 GdkRectangle allocation
;
7173 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
7174 g_return_if_fail (GTK_IS_WIDGET (widget
));
7176 for (list
= tree_view
->priv
->children
; list
; list
= list
->next
)
7178 if (((PsppSheetViewChild
*)list
->data
)->widget
== widget
)
7187 allocation
.x
= child
->x
= x
;
7188 allocation
.y
= child
->y
= y
;
7189 allocation
.width
= child
->width
= width
;
7190 allocation
.height
= child
->height
= height
;
7192 if (gtk_widget_get_realized (widget
))
7193 gtk_widget_size_allocate (widget
, &allocation
);
7197 /* TreeModel Callbacks
7201 pspp_sheet_view_row_changed (GtkTreeModel
*model
,
7206 PsppSheetView
*tree_view
= (PsppSheetView
*)data
;
7208 gboolean free_path
= FALSE
;
7209 GtkTreePath
*cursor_path
;
7211 g_return_if_fail (path
!= NULL
|| iter
!= NULL
);
7213 if (tree_view
->priv
->cursor
!= NULL
)
7214 cursor_path
= gtk_tree_row_reference_get_path (tree_view
->priv
->cursor
);
7218 if (tree_view
->priv
->edited_column
&&
7219 (cursor_path
== NULL
|| gtk_tree_path_compare (cursor_path
, path
) == 0))
7220 pspp_sheet_view_stop_editing (tree_view
, TRUE
);
7222 if (cursor_path
!= NULL
)
7223 gtk_tree_path_free (cursor_path
);
7227 path
= gtk_tree_model_get_path (model
, iter
);
7230 else if (iter
== NULL
)
7231 gtk_tree_model_get_iter (model
, iter
, path
);
7233 _pspp_sheet_view_find_node (tree_view
,
7239 if (gtk_widget_get_realized (GTK_WIDGET (tree_view
)))
7240 pspp_sheet_view_node_queue_redraw (tree_view
, node
);
7244 gtk_tree_path_free (path
);
7248 pspp_sheet_view_row_inserted (GtkTreeModel
*model
,
7253 PsppSheetView
*tree_view
= (PsppSheetView
*) data
;
7256 gint height
= tree_view
->priv
->fixed_height
;
7257 gboolean free_path
= FALSE
;
7258 gboolean node_visible
= TRUE
;
7260 g_return_if_fail (path
!= NULL
|| iter
!= NULL
);
7264 path
= gtk_tree_model_get_path (model
, iter
);
7267 else if (iter
== NULL
)
7268 gtk_tree_model_get_iter (model
, iter
, path
);
7270 tree_view
->priv
->row_count
= gtk_tree_model_iter_n_children (model
, NULL
);
7272 /* Update all row-references */
7273 gtk_tree_row_reference_inserted (G_OBJECT (data
), path
);
7274 indices
= gtk_tree_path_get_indices (path
);
7275 tmpnode
= indices
[0];
7277 range_tower_insert0 (tree_view
->priv
->selected
, tmpnode
, 1);
7281 if (node_visible
&& node_is_visible (tree_view
, tmpnode
))
7282 gtk_widget_queue_resize (GTK_WIDGET (tree_view
));
7284 gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view
));
7287 install_presize_handler (tree_view
);
7289 gtk_tree_path_free (path
);
7293 pspp_sheet_view_row_deleted (GtkTreeModel
*model
,
7297 PsppSheetView
*tree_view
= (PsppSheetView
*)data
;
7300 g_return_if_fail (path
!= NULL
);
7302 gtk_tree_row_reference_deleted (G_OBJECT (data
), path
);
7304 _pspp_sheet_view_find_node (tree_view
, path
, &node
);
7309 range_tower_delete (tree_view
->priv
->selected
, node
, 1);
7311 /* Ensure we don't have a dangling pointer to a dead node */
7312 ensure_unprelighted (tree_view
);
7314 /* Cancel editting if we've started */
7315 pspp_sheet_view_stop_editing (tree_view
, TRUE
);
7317 if (tree_view
->priv
->destroy_count_func
)
7319 gint child_count
= 0;
7320 tree_view
->priv
->destroy_count_func (tree_view
, path
, child_count
, tree_view
->priv
->destroy_count_data
);
7323 tree_view
->priv
->row_count
= gtk_tree_model_iter_n_children (model
, NULL
);
7325 if (! gtk_tree_row_reference_valid (tree_view
->priv
->top_row
))
7327 gtk_tree_row_reference_free (tree_view
->priv
->top_row
);
7328 tree_view
->priv
->top_row
= NULL
;
7331 install_scroll_sync_handler (tree_view
);
7333 gtk_widget_queue_resize (GTK_WIDGET (tree_view
));
7336 if (helper_data
.changed
)
7337 g_signal_emit_by_name (tree_view
->priv
->selection
, "changed");
7342 pspp_sheet_view_rows_reordered (GtkTreeModel
*model
,
7343 GtkTreePath
*parent
,
7348 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (data
);
7351 /* XXX need to adjust selection */
7352 len
= gtk_tree_model_iter_n_children (model
, iter
);
7357 gtk_tree_row_reference_reordered (G_OBJECT (data
),
7362 if (gtk_tree_path_get_depth (parent
) != 0)
7365 if (tree_view
->priv
->edited_column
)
7366 pspp_sheet_view_stop_editing (tree_view
, TRUE
);
7368 /* we need to be unprelighted */
7369 ensure_unprelighted (tree_view
);
7371 gtk_widget_queue_draw (GTK_WIDGET (tree_view
));
7373 pspp_sheet_view_dy_to_top_row (tree_view
);
7377 /* Internal tree functions
7382 pspp_sheet_view_get_background_xrange (PsppSheetView
*tree_view
,
7383 PsppSheetViewColumn
*column
,
7387 PsppSheetViewColumn
*tmp_column
= NULL
;
7398 rtl
= (gtk_widget_get_direction (GTK_WIDGET (tree_view
)) == GTK_TEXT_DIR_RTL
);
7401 for (list
= (rtl
? g_list_last (tree_view
->priv
->columns
) : g_list_first (tree_view
->priv
->columns
));
7403 list
= (rtl
? list
->prev
: list
->next
))
7405 tmp_column
= list
->data
;
7407 if (tmp_column
== column
)
7410 if (tmp_column
->visible
)
7411 total_width
+= tmp_column
->width
;
7414 if (tmp_column
!= column
)
7416 g_warning (G_STRLOC
": passed-in column isn't in the tree");
7425 if (column
->visible
)
7426 *x2
= total_width
+ column
->width
;
7428 *x2
= total_width
; /* width of 0 */
7432 /* Make sure the node is visible vertically */
7434 pspp_sheet_view_clamp_node_visible (PsppSheetView
*tree_view
,
7437 gint node_dy
, height
;
7438 GtkTreePath
*path
= NULL
;
7440 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view
)))
7443 /* just return if the node is visible, avoiding a costly expose */
7444 node_dy
= pspp_sheet_view_node_find_offset (tree_view
, node
);
7445 height
= ROW_HEIGHT (tree_view
);
7446 if (node_dy
>= gtk_adjustment_get_value (tree_view
->priv
->vadjustment
)
7447 && node_dy
+ height
<= (gtk_adjustment_get_value (tree_view
->priv
->vadjustment
)
7448 + gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
)))
7451 path
= _pspp_sheet_view_find_path (tree_view
, node
);
7454 /* We process updates because we want to clear old selected items when we scroll.
7455 * if this is removed, we get a "selection streak" at the bottom. */
7456 gdk_window_process_updates (tree_view
->priv
->bin_window
, TRUE
);
7457 pspp_sheet_view_scroll_to_cell (tree_view
, path
, NULL
, FALSE
, 0.0, 0.0);
7458 gtk_tree_path_free (path
);
7463 pspp_sheet_view_clamp_column_visible (PsppSheetView
*tree_view
,
7464 PsppSheetViewColumn
*column
,
7465 gboolean focus_to_cell
)
7472 x
= column
->allocation
.x
;
7473 width
= column
->allocation
.width
;
7475 if (width
> gtk_adjustment_get_page_size (tree_view
->priv
->hadjustment
))
7477 /* The column is larger than the horizontal page size. If the
7478 * column has cells which can be focussed individually, then we make
7479 * sure the cell which gets focus is fully visible (if even the
7480 * focus cell is bigger than the page size, we make sure the
7481 * left-hand side of the cell is visible).
7483 * If the column does not have those so-called special cells, we
7484 * make sure the left-hand side of the column is visible.
7487 if (focus_to_cell
&& pspp_sheet_view_has_special_cell (tree_view
))
7489 GtkTreePath
*cursor_path
;
7490 GdkRectangle background_area
, cell_area
, focus_area
;
7492 cursor_path
= gtk_tree_row_reference_get_path (tree_view
->priv
->cursor
);
7494 pspp_sheet_view_get_cell_area (tree_view
,
7495 cursor_path
, column
, &cell_area
);
7496 pspp_sheet_view_get_background_area (tree_view
,
7497 cursor_path
, column
,
7500 gtk_tree_path_free (cursor_path
);
7502 _pspp_sheet_view_column_get_focus_area (column
,
7508 width
= focus_area
.width
;
7510 if (width
< gtk_adjustment_get_page_size (tree_view
->priv
->hadjustment
))
7512 if ((gtk_adjustment_get_value (tree_view
->priv
->hadjustment
) + gtk_adjustment_get_page_size (tree_view
->priv
->hadjustment
)) < (x
+ width
))
7513 gtk_adjustment_set_value (tree_view
->priv
->hadjustment
,
7514 x
+ width
- gtk_adjustment_get_page_size (tree_view
->priv
->hadjustment
));
7515 else if (gtk_adjustment_get_value (tree_view
->priv
->hadjustment
) > x
)
7516 gtk_adjustment_set_value (tree_view
->priv
->hadjustment
, x
);
7520 gtk_adjustment_set_value (tree_view
->priv
->hadjustment
,
7522 gtk_adjustment_get_lower (tree_view
->priv
->hadjustment
),
7523 gtk_adjustment_get_upper (tree_view
->priv
->hadjustment
)
7524 - gtk_adjustment_get_page_size (tree_view
->priv
->hadjustment
)));
7528 if ((gtk_adjustment_get_value (tree_view
->priv
->hadjustment
) + gtk_adjustment_get_page_size (tree_view
->priv
->hadjustment
)) < (x
+ width
))
7529 gtk_adjustment_set_value (tree_view
->priv
->hadjustment
,
7530 x
+ width
- gtk_adjustment_get_page_size (tree_view
->priv
->hadjustment
));
7531 else if (gtk_adjustment_get_value (tree_view
->priv
->hadjustment
) > x
)
7532 gtk_adjustment_set_value (tree_view
->priv
->hadjustment
, x
);
7537 _pspp_sheet_view_find_path (PsppSheetView
*tree_view
,
7542 path
= gtk_tree_path_new ();
7544 gtk_tree_path_append_index (path
, node
);
7549 _pspp_sheet_view_find_node (PsppSheetView
*tree_view
,
7553 gint
*indices
= gtk_tree_path_get_indices (path
);
7554 gint depth
= gtk_tree_path_get_depth (path
);
7557 if (depth
== 0 || indices
[0] < 0 || indices
[0] >= tree_view
->priv
->row_count
)
7563 pspp_sheet_view_add_move_binding (GtkBindingSet
*binding_set
,
7566 gboolean add_shifted_binding
,
7567 GtkMovementStep step
,
7571 gtk_binding_entry_add_signal (binding_set
, keyval
, modmask
,
7576 if (add_shifted_binding
)
7577 gtk_binding_entry_add_signal (binding_set
, keyval
, GDK_SHIFT_MASK
,
7582 if ((modmask
& GDK_CONTROL_MASK
) == GDK_CONTROL_MASK
)
7585 gtk_binding_entry_add_signal (binding_set
, keyval
, GDK_CONTROL_MASK
| GDK_SHIFT_MASK
,
7590 gtk_binding_entry_add_signal (binding_set
, keyval
, GDK_CONTROL_MASK
,
7597 pspp_sheet_view_set_column_drag_info (PsppSheetView
*tree_view
,
7598 PsppSheetViewColumn
*column
)
7600 PsppSheetViewColumn
*left_column
;
7601 PsppSheetViewColumn
*cur_column
= NULL
;
7602 PsppSheetViewColumnReorder
*reorder
;
7607 /* We want to precalculate the motion list such that we know what column slots
7611 rtl
= (gtk_widget_get_direction (GTK_WIDGET (tree_view
)) == GTK_TEXT_DIR_RTL
);
7613 /* First, identify all possible drop spots */
7615 tmp_list
= g_list_last (tree_view
->priv
->columns
);
7617 tmp_list
= g_list_first (tree_view
->priv
->columns
);
7621 cur_column
= PSPP_SHEET_VIEW_COLUMN (tmp_list
->data
);
7622 tmp_list
= rtl
?g_list_previous (tmp_list
):g_list_next (tmp_list
);
7624 if (cur_column
->visible
== FALSE
)
7627 /* If it's not the column moving and func tells us to skip over the column, we continue. */
7628 if (left_column
!= column
&& cur_column
!= column
&&
7629 tree_view
->priv
->column_drop_func
&&
7630 ! tree_view
->priv
->column_drop_func (tree_view
, column
, left_column
, cur_column
, tree_view
->priv
->column_drop_func_data
))
7632 left_column
= cur_column
;
7635 reorder
= g_slice_new0 (PsppSheetViewColumnReorder
);
7636 reorder
->left_column
= left_column
;
7637 left_column
= reorder
->right_column
= cur_column
;
7639 tree_view
->priv
->column_drag_info
= g_list_append (tree_view
->priv
->column_drag_info
, reorder
);
7642 /* Add the last one */
7643 if (tree_view
->priv
->column_drop_func
== NULL
||
7644 ((left_column
!= column
) &&
7645 tree_view
->priv
->column_drop_func (tree_view
, column
, left_column
, NULL
, tree_view
->priv
->column_drop_func_data
)))
7647 reorder
= g_slice_new0 (PsppSheetViewColumnReorder
);
7648 reorder
->left_column
= left_column
;
7649 reorder
->right_column
= NULL
;
7650 tree_view
->priv
->column_drag_info
= g_list_append (tree_view
->priv
->column_drag_info
, reorder
);
7653 /* We quickly check to see if it even makes sense to reorder columns. */
7654 /* If there is nothing that can be moved, then we return */
7656 if (tree_view
->priv
->column_drag_info
== NULL
)
7659 /* We know there are always 2 slots possbile, as you can always return column. */
7660 /* If that's all there is, return */
7661 if (tree_view
->priv
->column_drag_info
->next
== NULL
||
7662 (tree_view
->priv
->column_drag_info
->next
->next
== NULL
&&
7663 ((PsppSheetViewColumnReorder
*)tree_view
->priv
->column_drag_info
->data
)->right_column
== column
&&
7664 ((PsppSheetViewColumnReorder
*)tree_view
->priv
->column_drag_info
->next
->data
)->left_column
== column
))
7666 for (tmp_list
= tree_view
->priv
->column_drag_info
; tmp_list
; tmp_list
= tmp_list
->next
)
7667 g_slice_free (PsppSheetViewColumnReorder
, tmp_list
->data
);
7668 g_list_free (tree_view
->priv
->column_drag_info
);
7669 tree_view
->priv
->column_drag_info
= NULL
;
7672 /* We fill in the ranges for the columns, now that we've isolated them */
7673 left
= - TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view
);
7675 for (tmp_list
= tree_view
->priv
->column_drag_info
; tmp_list
; tmp_list
= tmp_list
->next
)
7677 reorder
= (PsppSheetViewColumnReorder
*) tmp_list
->data
;
7679 reorder
->left_align
= left
;
7680 if (tmp_list
->next
!= NULL
)
7682 g_assert (tmp_list
->next
->data
);
7683 left
= reorder
->right_align
= (reorder
->right_column
->allocation
.x
+
7684 reorder
->right_column
->allocation
.width
+
7685 ((PsppSheetViewColumnReorder
*)tmp_list
->next
->data
)->left_column
->allocation
.x
)/2;
7689 gint width
= gdk_window_get_width (tree_view
->priv
->header_window
);
7690 reorder
->right_align
= width
+ TREE_VIEW_COLUMN_DRAG_DEAD_MULTIPLIER (tree_view
);
7696 _pspp_sheet_view_column_start_drag (PsppSheetView
*tree_view
,
7697 PsppSheetViewColumn
*column
)
7699 GdkEvent
*send_event
;
7700 GtkAllocation allocation
;
7702 GdkScreen
*screen
= gtk_widget_get_screen (GTK_WIDGET (tree_view
));
7703 GdkDisplay
*display
= gdk_screen_get_display (screen
);
7705 g_return_if_fail (tree_view
->priv
->column_drag_info
== NULL
);
7706 g_return_if_fail (tree_view
->priv
->cur_reorder
== NULL
);
7707 g_return_if_fail (column
->button
);
7709 pspp_sheet_view_set_column_drag_info (tree_view
, column
);
7711 if (tree_view
->priv
->column_drag_info
== NULL
)
7714 if (tree_view
->priv
->drag_window
== NULL
)
7716 GdkWindowAttr attributes
;
7717 guint attributes_mask
;
7719 attributes
.window_type
= GDK_WINDOW_CHILD
;
7720 attributes
.wclass
= GDK_INPUT_OUTPUT
;
7721 attributes
.x
= column
->allocation
.x
;
7723 attributes
.width
= column
->allocation
.width
;
7724 attributes
.height
= column
->allocation
.height
;
7725 attributes
.visual
= gtk_widget_get_visual (GTK_WIDGET (tree_view
));
7726 attributes
.event_mask
= GDK_VISIBILITY_NOTIFY_MASK
| GDK_EXPOSURE_MASK
| GDK_POINTER_MOTION_MASK
;
7727 attributes_mask
= GDK_WA_X
| GDK_WA_Y
| GDK_WA_VISUAL
;
7729 tree_view
->priv
->drag_window
= gdk_window_new (tree_view
->priv
->bin_window
,
7732 gdk_window_set_user_data (tree_view
->priv
->drag_window
, GTK_WIDGET (tree_view
));
7735 gdk_display_pointer_ungrab (display
, GDK_CURRENT_TIME
);
7736 gdk_display_keyboard_ungrab (display
, GDK_CURRENT_TIME
);
7738 gtk_grab_remove (column
->button
);
7740 send_event
= gdk_event_new (GDK_LEAVE_NOTIFY
);
7741 send_event
->crossing
.send_event
= TRUE
;
7742 send_event
->crossing
.window
= g_object_ref (gtk_button_get_event_window (GTK_BUTTON (column
->button
)));
7743 send_event
->crossing
.subwindow
= NULL
;
7744 send_event
->crossing
.detail
= GDK_NOTIFY_ANCESTOR
;
7745 send_event
->crossing
.time
= GDK_CURRENT_TIME
;
7747 gtk_propagate_event (column
->button
, send_event
);
7748 gdk_event_free (send_event
);
7750 send_event
= gdk_event_new (GDK_BUTTON_RELEASE
);
7751 send_event
->button
.window
= g_object_ref (gdk_screen_get_root_window (screen
));
7752 send_event
->button
.send_event
= TRUE
;
7753 send_event
->button
.time
= GDK_CURRENT_TIME
;
7754 send_event
->button
.x
= -1;
7755 send_event
->button
.y
= -1;
7756 send_event
->button
.axes
= NULL
;
7757 send_event
->button
.state
= 0;
7758 send_event
->button
.button
= 1;
7759 send_event
->button
.device
=
7760 gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display
));
7762 send_event
->button
.x_root
= 0;
7763 send_event
->button
.y_root
= 0;
7765 gtk_propagate_event (column
->button
, send_event
);
7766 gdk_event_free (send_event
);
7768 /* Kids, don't try this at home */
7769 g_object_ref (column
->button
);
7770 gtk_container_remove (GTK_CONTAINER (tree_view
), column
->button
);
7771 gtk_widget_set_parent_window (column
->button
, tree_view
->priv
->drag_window
);
7772 gtk_widget_set_parent (column
->button
, GTK_WIDGET (tree_view
));
7773 g_object_unref (column
->button
);
7775 tree_view
->priv
->drag_column_x
= column
->allocation
.x
;
7776 allocation
= column
->allocation
;
7778 gtk_widget_size_allocate (column
->button
, &allocation
);
7779 gtk_widget_set_parent_window (column
->button
, tree_view
->priv
->drag_window
);
7781 tree_view
->priv
->drag_column
= column
;
7782 gdk_window_show (tree_view
->priv
->drag_window
);
7784 gdk_window_get_origin (tree_view
->priv
->header_window
, &x
, &y
);
7786 gtk_widget_grab_focus (GTK_WIDGET (tree_view
));
7787 while (gtk_events_pending ())
7788 gtk_main_iteration ();
7790 PSPP_SHEET_VIEW_SET_FLAG (tree_view
, PSPP_SHEET_VIEW_IN_COLUMN_DRAG
);
7791 gdk_pointer_grab (tree_view
->priv
->drag_window
,
7793 GDK_POINTER_MOTION_MASK
|GDK_BUTTON_RELEASE_MASK
,
7794 NULL
, NULL
, GDK_CURRENT_TIME
);
7795 gdk_keyboard_grab (tree_view
->priv
->drag_window
,
7801 _pspp_sheet_view_queue_draw_node (PsppSheetView
*tree_view
,
7803 const GdkRectangle
*clip_rect
)
7806 GtkAllocation allocation
;
7808 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view
)))
7811 gtk_widget_get_allocation (GTK_WIDGET (tree_view
), &allocation
);
7813 rect
.width
= MAX (tree_view
->priv
->width
, allocation
.width
);
7815 rect
.y
= BACKGROUND_FIRST_PIXEL (tree_view
, node
);
7816 rect
.height
= ROW_HEIGHT (tree_view
);
7820 GdkRectangle new_rect
;
7822 gdk_rectangle_intersect (clip_rect
, &rect
, &new_rect
);
7824 gdk_window_invalidate_rect (tree_view
->priv
->bin_window
, &new_rect
, TRUE
);
7828 gdk_window_invalidate_rect (tree_view
->priv
->bin_window
, &rect
, TRUE
);
7833 pspp_sheet_view_queue_draw_path (PsppSheetView
*tree_view
,
7835 const GdkRectangle
*clip_rect
)
7839 _pspp_sheet_view_find_node (tree_view
, path
, &node
);
7842 _pspp_sheet_view_queue_draw_node (tree_view
, node
, clip_rect
);
7846 pspp_sheet_view_focus_to_cursor (PsppSheetView
*tree_view
)
7849 GtkTreePath
*cursor_path
;
7851 if ((tree_view
->priv
->row_count
== 0) ||
7852 (! gtk_widget_get_realized (GTK_WIDGET (tree_view
))))
7856 if (tree_view
->priv
->cursor
)
7857 cursor_path
= gtk_tree_row_reference_get_path (tree_view
->priv
->cursor
);
7859 if (cursor_path
== NULL
)
7861 /* There's no cursor. Move the cursor to the first selected row, if any
7862 * are selected, otherwise to the first row in the sheetview.
7864 GList
*selected_rows
;
7865 GtkTreeModel
*model
;
7866 PsppSheetSelection
*selection
;
7868 selection
= pspp_sheet_view_get_selection (tree_view
);
7869 selected_rows
= pspp_sheet_selection_get_selected_rows (selection
, &model
);
7873 /* XXX we could avoid doing O(n) work to get this result */
7874 cursor_path
= gtk_tree_path_copy((const GtkTreePath
*)(selected_rows
->data
));
7875 g_list_foreach (selected_rows
, (GFunc
)gtk_tree_path_free
, NULL
);
7876 g_list_free (selected_rows
);
7880 cursor_path
= gtk_tree_path_new_first ();
7881 search_first_focusable_path (tree_view
, &cursor_path
,
7885 gtk_tree_row_reference_free (tree_view
->priv
->cursor
);
7886 tree_view
->priv
->cursor
= NULL
;
7890 if (tree_view
->priv
->selection
->type
== PSPP_SHEET_SELECTION_MULTIPLE
||
7891 tree_view
->priv
->selection
->type
== PSPP_SHEET_SELECTION_RECTANGLE
)
7892 pspp_sheet_view_real_set_cursor (tree_view
, cursor_path
, FALSE
, FALSE
, 0);
7894 pspp_sheet_view_real_set_cursor (tree_view
, cursor_path
, TRUE
, FALSE
, 0);
7900 /* Now find a column for the cursor. */
7901 PSPP_SHEET_VIEW_SET_FLAG (tree_view
, PSPP_SHEET_VIEW_DRAW_KEYFOCUS
);
7903 pspp_sheet_view_queue_draw_path (tree_view
, cursor_path
, NULL
);
7904 gtk_tree_path_free (cursor_path
);
7906 if (tree_view
->priv
->focus_column
== NULL
)
7909 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
7911 if (PSPP_SHEET_VIEW_COLUMN (list
->data
)->visible
)
7913 tree_view
->priv
->focus_column
= PSPP_SHEET_VIEW_COLUMN (list
->data
);
7914 pspp_sheet_selection_unselect_all_columns (tree_view
->priv
->selection
);
7915 pspp_sheet_selection_select_column (tree_view
->priv
->selection
, tree_view
->priv
->focus_column
);
7925 pspp_sheet_view_move_cursor_up_down (PsppSheetView
*tree_view
,
7927 PsppSheetSelectMode mode
)
7929 gint selection_count
;
7930 int cursor_node
= -1;
7931 int new_cursor_node
= -1;
7932 GtkTreePath
*cursor_path
= NULL
;
7933 gboolean grab_focus
= TRUE
;
7935 if (! gtk_widget_has_focus (GTK_WIDGET (tree_view
)))
7939 if (!gtk_tree_row_reference_valid (tree_view
->priv
->cursor
))
7940 /* FIXME: we lost the cursor; should we get the first? */
7943 cursor_path
= gtk_tree_row_reference_get_path (tree_view
->priv
->cursor
);
7944 _pspp_sheet_view_find_node (tree_view
, cursor_path
, &cursor_node
);
7946 if (cursor_node
< 0)
7947 /* FIXME: we lost the cursor; should we get the first? */
7950 selection_count
= pspp_sheet_selection_count_selected_rows (tree_view
->priv
->selection
);
7952 if (selection_count
== 0
7953 && tree_view
->priv
->selection
->type
!= PSPP_SHEET_SELECTION_NONE
7954 && !(mode
& PSPP_SHEET_SELECT_MODE_TOGGLE
))
7956 /* Don't move the cursor, but just select the current node */
7957 new_cursor_node
= cursor_node
;
7962 new_cursor_node
= pspp_sheet_view_node_prev (tree_view
, cursor_node
);
7964 new_cursor_node
= pspp_sheet_view_node_next (tree_view
, cursor_node
);
7967 gtk_tree_path_free (cursor_path
);
7969 if (new_cursor_node
)
7971 cursor_path
= _pspp_sheet_view_find_path (tree_view
, new_cursor_node
);
7973 search_first_focusable_path (tree_view
, &cursor_path
,
7978 gtk_tree_path_free (cursor_path
);
7982 * If the list has only one item and multi-selection is set then select
7983 * the row (if not yet selected).
7985 if ((tree_view
->priv
->selection
->type
== PSPP_SHEET_SELECTION_MULTIPLE
||
7986 tree_view
->priv
->selection
->type
== PSPP_SHEET_SELECTION_RECTANGLE
) &&
7987 new_cursor_node
< 0)
7990 new_cursor_node
= pspp_sheet_view_node_next (tree_view
, cursor_node
);
7992 new_cursor_node
= pspp_sheet_view_node_prev (tree_view
, cursor_node
);
7994 if (new_cursor_node
< 0
7995 && !pspp_sheet_view_node_is_selected (tree_view
, cursor_node
))
7997 new_cursor_node
= cursor_node
;
8001 new_cursor_node
= -1;
8005 if (new_cursor_node
>= 0)
8007 cursor_path
= _pspp_sheet_view_find_path (tree_view
, new_cursor_node
);
8008 pspp_sheet_view_real_set_cursor (tree_view
, cursor_path
, TRUE
, TRUE
, mode
);
8009 gtk_tree_path_free (cursor_path
);
8013 pspp_sheet_view_clamp_node_visible (tree_view
, cursor_node
);
8015 if (!(mode
& PSPP_SHEET_SELECT_MODE_EXTEND
))
8017 if (! gtk_widget_keynav_failed (GTK_WIDGET (tree_view
),
8019 GTK_DIR_UP
: GTK_DIR_DOWN
))
8021 GtkWidget
*toplevel
= gtk_widget_get_toplevel (GTK_WIDGET (tree_view
));
8024 gtk_widget_child_focus (toplevel
,
8026 GTK_DIR_TAB_BACKWARD
:
8027 GTK_DIR_TAB_FORWARD
);
8034 gtk_widget_error_bell (GTK_WIDGET (tree_view
));
8039 gtk_widget_grab_focus (GTK_WIDGET (tree_view
));
8041 return new_cursor_node
>= 0;
8045 pspp_sheet_view_move_cursor_page_up_down (PsppSheetView
*tree_view
,
8047 PsppSheetSelectMode mode
)
8049 int cursor_node
= -1;
8050 GtkTreePath
*old_cursor_path
= NULL
;
8051 GtkTreePath
*cursor_path
= NULL
;
8052 int start_cursor_node
= -1;
8055 gint vertical_separator
;
8057 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view
)))
8060 if (gtk_tree_row_reference_valid (tree_view
->priv
->cursor
))
8061 old_cursor_path
= gtk_tree_row_reference_get_path (tree_view
->priv
->cursor
);
8063 /* This is sorta weird. Focus in should give us a cursor */
8066 gtk_widget_style_get (GTK_WIDGET (tree_view
), "vertical-separator", &vertical_separator
, NULL
);
8067 _pspp_sheet_view_find_node (tree_view
, old_cursor_path
, &cursor_node
);
8069 if (cursor_node
< 0)
8071 /* FIXME: we lost the cursor. Should we try to get one? */
8072 gtk_tree_path_free (old_cursor_path
);
8076 y
= pspp_sheet_view_node_find_offset (tree_view
, cursor_node
);
8077 window_y
= RBTREE_Y_TO_TREE_WINDOW_Y (tree_view
, y
);
8078 y
+= tree_view
->priv
->cursor_offset
;
8079 y
+= count
* (int)gtk_adjustment_get_page_increment (tree_view
->priv
->vadjustment
);
8080 y
= CLAMP (y
, (gint
)gtk_adjustment_get_lower (tree_view
->priv
->vadjustment
), (gint
)gtk_adjustment_get_upper (tree_view
->priv
->vadjustment
) - vertical_separator
);
8082 if (y
>= tree_view
->priv
->height
)
8083 y
= tree_view
->priv
->height
- 1;
8085 tree_view
->priv
->cursor_offset
=
8086 pspp_sheet_view_find_offset (tree_view
, y
, &cursor_node
);
8088 if (tree_view
->priv
->cursor_offset
> BACKGROUND_HEIGHT (tree_view
))
8090 cursor_node
= pspp_sheet_view_node_next (tree_view
, cursor_node
);
8091 tree_view
->priv
->cursor_offset
-= BACKGROUND_HEIGHT (tree_view
);
8094 y
-= tree_view
->priv
->cursor_offset
;
8095 cursor_path
= _pspp_sheet_view_find_path (tree_view
, cursor_node
);
8097 start_cursor_node
= cursor_node
;
8099 if (! search_first_focusable_path (tree_view
, &cursor_path
,
8103 /* It looks like we reached the end of the view without finding
8104 * a focusable row. We will step backwards to find the last
8107 cursor_node
= start_cursor_node
;
8108 cursor_path
= _pspp_sheet_view_find_path (tree_view
, cursor_node
);
8110 search_first_focusable_path (tree_view
, &cursor_path
,
8119 y
= pspp_sheet_view_node_find_offset (tree_view
, cursor_node
);
8121 pspp_sheet_view_real_set_cursor (tree_view
, cursor_path
, TRUE
, FALSE
, mode
);
8124 pspp_sheet_view_scroll_to_point (tree_view
, -1, y
);
8125 pspp_sheet_view_clamp_node_visible (tree_view
, cursor_node
);
8126 _pspp_sheet_view_queue_draw_node (tree_view
, cursor_node
, NULL
);
8128 if (!gtk_tree_path_compare (old_cursor_path
, cursor_path
))
8129 gtk_widget_error_bell (GTK_WIDGET (tree_view
));
8131 gtk_widget_grab_focus (GTK_WIDGET (tree_view
));
8134 gtk_tree_path_free (old_cursor_path
);
8135 gtk_tree_path_free (cursor_path
);
8139 pspp_sheet_view_move_cursor_left_right (PsppSheetView
*tree_view
,
8141 PsppSheetSelectMode mode
)
8143 int cursor_node
= -1;
8144 GtkTreePath
*cursor_path
= NULL
;
8145 PsppSheetViewColumn
*column
;
8148 gboolean found_column
= FALSE
;
8151 rtl
= (gtk_widget_get_direction (GTK_WIDGET (tree_view
)) == GTK_TEXT_DIR_RTL
);
8153 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view
)))
8156 if (gtk_tree_row_reference_valid (tree_view
->priv
->cursor
))
8157 cursor_path
= gtk_tree_row_reference_get_path (tree_view
->priv
->cursor
);
8161 _pspp_sheet_view_find_node (tree_view
, cursor_path
, &cursor_node
);
8162 if (cursor_node
< 0)
8164 if (gtk_tree_model_get_iter (tree_view
->priv
->model
, &iter
, cursor_path
) == FALSE
)
8166 gtk_tree_path_free (cursor_path
);
8169 gtk_tree_path_free (cursor_path
);
8171 list
= rtl
? g_list_last (tree_view
->priv
->columns
) : g_list_first (tree_view
->priv
->columns
);
8172 if (tree_view
->priv
->focus_column
)
8174 for (; list
; list
= (rtl
? list
->prev
: list
->next
))
8176 if (list
->data
== tree_view
->priv
->focus_column
)
8183 gboolean left
, right
;
8185 column
= list
->data
;
8186 if (column
->visible
== FALSE
|| column
->row_head
)
8189 pspp_sheet_view_column_cell_set_cell_data (column
,
8190 tree_view
->priv
->model
,
8195 right
= list
->prev
? TRUE
: FALSE
;
8196 left
= list
->next
? TRUE
: FALSE
;
8200 left
= list
->prev
? TRUE
: FALSE
;
8201 right
= list
->next
? TRUE
: FALSE
;
8204 if (_pspp_sheet_view_column_cell_focus (column
, count
, left
, right
))
8206 tree_view
->priv
->focus_column
= column
;
8207 found_column
= TRUE
;
8212 list
= rtl
? list
->prev
: list
->next
;
8214 list
= rtl
? list
->next
: list
->prev
;
8219 _pspp_sheet_view_queue_draw_node (tree_view
, cursor_node
, NULL
);
8220 g_signal_emit (tree_view
, tree_view_signals
[CURSOR_CHANGED
], 0);
8221 gtk_widget_grab_focus (GTK_WIDGET (tree_view
));
8225 gtk_widget_error_bell (GTK_WIDGET (tree_view
));
8228 pspp_sheet_view_clamp_column_visible (tree_view
,
8229 tree_view
->priv
->focus_column
, TRUE
);
8233 pspp_sheet_view_move_cursor_line_start_end (PsppSheetView
*tree_view
,
8235 PsppSheetSelectMode mode
)
8237 int cursor_node
= -1;
8238 GtkTreePath
*cursor_path
= NULL
;
8239 PsppSheetViewColumn
*column
;
8240 PsppSheetViewColumn
*found_column
;
8245 rtl
= (gtk_widget_get_direction (GTK_WIDGET (tree_view
)) == GTK_TEXT_DIR_RTL
);
8247 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view
)))
8250 if (gtk_tree_row_reference_valid (tree_view
->priv
->cursor
))
8251 cursor_path
= gtk_tree_row_reference_get_path (tree_view
->priv
->cursor
);
8255 _pspp_sheet_view_find_node (tree_view
, cursor_path
, &cursor_node
);
8256 if (cursor_node
< 0)
8258 if (gtk_tree_model_get_iter (tree_view
->priv
->model
, &iter
, cursor_path
) == FALSE
)
8260 gtk_tree_path_free (cursor_path
);
8263 gtk_tree_path_free (cursor_path
);
8265 list
= rtl
? g_list_last (tree_view
->priv
->columns
) : g_list_first (tree_view
->priv
->columns
);
8266 if (tree_view
->priv
->focus_column
)
8268 for (; list
; list
= (rtl
? list
->prev
: list
->next
))
8270 if (list
->data
== tree_view
->priv
->focus_column
)
8275 found_column
= NULL
;
8278 gboolean left
, right
;
8280 column
= list
->data
;
8281 if (column
->visible
== FALSE
|| column
->row_head
)
8284 pspp_sheet_view_column_cell_set_cell_data (column
,
8285 tree_view
->priv
->model
,
8290 right
= list
->prev
? TRUE
: FALSE
;
8291 left
= list
->next
? TRUE
: FALSE
;
8295 left
= list
->prev
? TRUE
: FALSE
;
8296 right
= list
->next
? TRUE
: FALSE
;
8299 if (column
->tabbable
8300 && _pspp_sheet_view_column_cell_focus (column
, count
, left
, right
))
8301 found_column
= column
;
8305 list
= rtl
? list
->prev
: list
->next
;
8307 list
= rtl
? list
->next
: list
->prev
;
8312 tree_view
->priv
->focus_column
= found_column
;
8313 _pspp_sheet_view_queue_draw_node (tree_view
, cursor_node
, NULL
);
8314 g_signal_emit (tree_view
, tree_view_signals
[CURSOR_CHANGED
], 0);
8315 gtk_widget_grab_focus (GTK_WIDGET (tree_view
));
8318 pspp_sheet_view_clamp_column_visible (tree_view
,
8319 tree_view
->priv
->focus_column
, TRUE
);
8323 try_move_cursor_tab (PsppSheetView
*tree_view
,
8324 gboolean start_at_focus_column
,
8327 PsppSheetViewColumn
*column
;
8329 int cursor_node
= -1;
8330 GtkTreePath
*cursor_path
= NULL
;
8334 if (gtk_tree_row_reference_valid (tree_view
->priv
->cursor
))
8335 cursor_path
= gtk_tree_row_reference_get_path (tree_view
->priv
->cursor
);
8339 _pspp_sheet_view_find_node (tree_view
, cursor_path
, &cursor_node
);
8340 if (cursor_node
< 0)
8342 if (gtk_tree_model_get_iter (tree_view
->priv
->model
, &iter
, cursor_path
) == FALSE
)
8344 gtk_tree_path_free (cursor_path
);
8347 gtk_tree_path_free (cursor_path
);
8349 rtl
= gtk_widget_get_direction (GTK_WIDGET (tree_view
)) == GTK_TEXT_DIR_RTL
;
8350 if (start_at_focus_column
)
8353 ? g_list_last (tree_view
->priv
->columns
)
8354 : g_list_first (tree_view
->priv
->columns
));
8355 if (tree_view
->priv
->focus_column
)
8357 for (; list
; list
= (rtl
? list
->prev
: list
->next
))
8359 if (list
->data
== tree_view
->priv
->focus_column
)
8366 list
= (rtl
^ (count
== 1)
8367 ? g_list_first (tree_view
->priv
->columns
)
8368 : g_list_last (tree_view
->priv
->columns
));
8373 gboolean left
, right
;
8375 column
= list
->data
;
8376 if (column
->visible
== FALSE
|| !column
->tabbable
)
8379 pspp_sheet_view_column_cell_set_cell_data (column
,
8380 tree_view
->priv
->model
,
8385 right
= list
->prev
? TRUE
: FALSE
;
8386 left
= list
->next
? TRUE
: FALSE
;
8390 left
= list
->prev
? TRUE
: FALSE
;
8391 right
= list
->next
? TRUE
: FALSE
;
8394 if (column
->tabbable
8395 && _pspp_sheet_view_column_cell_focus (column
, count
, left
, right
))
8397 tree_view
->priv
->focus_column
= column
;
8398 _pspp_sheet_view_queue_draw_node (tree_view
, cursor_node
, NULL
);
8399 g_signal_emit (tree_view
, tree_view_signals
[CURSOR_CHANGED
], 0);
8400 gtk_widget_grab_focus (GTK_WIDGET (tree_view
));
8405 list
= rtl
? list
->prev
: list
->next
;
8407 list
= rtl
? list
->next
: list
->prev
;
8414 pspp_sheet_view_move_cursor_tab (PsppSheetView
*tree_view
,
8417 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view
)))
8420 if (!try_move_cursor_tab (tree_view
, TRUE
, count
))
8422 if (pspp_sheet_view_move_cursor_up_down (tree_view
, count
, 0)
8423 && !try_move_cursor_tab (tree_view
, FALSE
, count
))
8424 gtk_widget_error_bell (GTK_WIDGET (tree_view
));
8427 pspp_sheet_view_clamp_column_visible (tree_view
,
8428 tree_view
->priv
->focus_column
, TRUE
);
8432 pspp_sheet_view_move_cursor_start_end (PsppSheetView
*tree_view
,
8434 PsppSheetSelectMode mode
)
8438 GtkTreePath
*old_path
;
8440 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view
)))
8443 g_return_if_fail (tree_view
->priv
->row_count
> 0);
8445 pspp_sheet_view_get_cursor (tree_view
, &old_path
, NULL
);
8449 /* Now go forward to find the first focusable row. */
8450 path
= _pspp_sheet_view_find_path (tree_view
, 0);
8451 search_first_focusable_path (tree_view
, &path
,
8452 TRUE
, &cursor_node
);
8456 /* Now go backwards to find last focusable row. */
8457 path
= _pspp_sheet_view_find_path (tree_view
, tree_view
->priv
->row_count
- 1);
8458 search_first_focusable_path (tree_view
, &path
,
8459 FALSE
, &cursor_node
);
8465 if (gtk_tree_path_compare (old_path
, path
))
8467 pspp_sheet_view_real_set_cursor (tree_view
, path
, TRUE
, TRUE
, mode
);
8468 gtk_widget_grab_focus (GTK_WIDGET (tree_view
));
8472 gtk_widget_error_bell (GTK_WIDGET (tree_view
));
8476 gtk_tree_path_free (old_path
);
8477 gtk_tree_path_free (path
);
8481 pspp_sheet_view_real_select_all (PsppSheetView
*tree_view
)
8483 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view
)))
8486 if (tree_view
->priv
->selection
->type
!= PSPP_SHEET_SELECTION_MULTIPLE
&&
8487 tree_view
->priv
->selection
->type
!= PSPP_SHEET_SELECTION_RECTANGLE
)
8490 pspp_sheet_selection_select_all (tree_view
->priv
->selection
);
8496 pspp_sheet_view_real_unselect_all (PsppSheetView
*tree_view
)
8498 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view
)))
8501 if (tree_view
->priv
->selection
->type
!= PSPP_SHEET_SELECTION_MULTIPLE
&&
8502 tree_view
->priv
->selection
->type
!= PSPP_SHEET_SELECTION_RECTANGLE
)
8505 pspp_sheet_selection_unselect_all (tree_view
->priv
->selection
);
8511 pspp_sheet_view_real_select_cursor_row (PsppSheetView
*tree_view
,
8512 gboolean start_editing
,
8513 PsppSheetSelectMode mode
)
8516 int cursor_node
= -1;
8517 GtkTreePath
*cursor_path
= NULL
;
8519 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view
)))
8522 if (tree_view
->priv
->cursor
)
8523 cursor_path
= gtk_tree_row_reference_get_path (tree_view
->priv
->cursor
);
8525 if (cursor_path
== NULL
)
8528 _pspp_sheet_view_find_node (tree_view
, cursor_path
,
8531 if (cursor_node
< 0)
8533 gtk_tree_path_free (cursor_path
);
8537 if (!(mode
& PSPP_SHEET_SELECT_MODE_EXTEND
) && start_editing
&&
8538 tree_view
->priv
->focus_column
)
8540 if (pspp_sheet_view_start_editing (tree_view
, cursor_path
))
8542 gtk_tree_path_free (cursor_path
);
8547 _pspp_sheet_selection_internal_select_node (tree_view
->priv
->selection
,
8553 /* We bail out if the original (tree, node) don't exist anymore after
8554 * handling the selection-changed callback. We do return TRUE because
8555 * the key press has been handled at this point.
8557 _pspp_sheet_view_find_node (tree_view
, cursor_path
, &new_node
);
8559 if (cursor_node
!= new_node
)
8562 pspp_sheet_view_clamp_node_visible (tree_view
, cursor_node
);
8564 gtk_widget_grab_focus (GTK_WIDGET (tree_view
));
8565 _pspp_sheet_view_queue_draw_node (tree_view
, cursor_node
, NULL
);
8567 if (!(mode
& PSPP_SHEET_SELECT_MODE_EXTEND
))
8568 pspp_sheet_view_row_activated (tree_view
, cursor_path
,
8569 tree_view
->priv
->focus_column
);
8571 gtk_tree_path_free (cursor_path
);
8577 pspp_sheet_view_real_toggle_cursor_row (PsppSheetView
*tree_view
)
8580 int cursor_node
= -1;
8581 GtkTreePath
*cursor_path
= NULL
;
8583 if (!gtk_widget_has_focus (GTK_WIDGET (tree_view
)))
8587 if (tree_view
->priv
->cursor
)
8588 cursor_path
= gtk_tree_row_reference_get_path (tree_view
->priv
->cursor
);
8590 if (cursor_path
== NULL
)
8593 _pspp_sheet_view_find_node (tree_view
, cursor_path
, &cursor_node
);
8594 if (cursor_node
< 0)
8596 gtk_tree_path_free (cursor_path
);
8600 _pspp_sheet_selection_internal_select_node (tree_view
->priv
->selection
,
8603 PSPP_SHEET_SELECT_MODE_TOGGLE
,
8606 /* We bail out if the original (tree, node) don't exist anymore after
8607 * handling the selection-changed callback. We do return TRUE because
8608 * the key press has been handled at this point.
8610 _pspp_sheet_view_find_node (tree_view
, cursor_path
, &new_node
);
8612 if (cursor_node
!= new_node
)
8615 pspp_sheet_view_clamp_node_visible (tree_view
, cursor_node
);
8617 gtk_widget_grab_focus (GTK_WIDGET (tree_view
));
8618 pspp_sheet_view_queue_draw_path (tree_view
, cursor_path
, NULL
);
8619 gtk_tree_path_free (cursor_path
);
8625 pspp_sheet_view_search_entry_flush_timeout (PsppSheetView
*tree_view
)
8627 pspp_sheet_view_search_dialog_hide (tree_view
->priv
->search_window
, tree_view
);
8628 tree_view
->priv
->typeselect_flush_timeout
= 0;
8633 /* Cut and paste from gtkwindow.c */
8635 send_focus_change (GtkWidget
*widget
,
8638 GdkEvent
*fevent
= gdk_event_new (GDK_FOCUS_CHANGE
);
8640 fevent
->focus_change
.type
= GDK_FOCUS_CHANGE
;
8641 fevent
->focus_change
.window
= g_object_ref (gtk_widget_get_window (widget
));
8642 fevent
->focus_change
.in
= in
;
8644 gtk_widget_send_focus_change (widget
, fevent
);
8645 gdk_event_free (fevent
);
8649 pspp_sheet_view_ensure_interactive_directory (PsppSheetView
*tree_view
)
8651 GtkWidget
*frame
, *vbox
, *toplevel
;
8654 if (tree_view
->priv
->search_custom_entry_set
)
8657 toplevel
= gtk_widget_get_toplevel (GTK_WIDGET (tree_view
));
8658 screen
= gtk_widget_get_screen (GTK_WIDGET (tree_view
));
8660 if (tree_view
->priv
->search_window
!= NULL
)
8662 if (gtk_window_get_group (GTK_WINDOW (toplevel
)))
8663 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel
)),
8664 GTK_WINDOW (tree_view
->priv
->search_window
));
8665 else if (gtk_window_get_group (GTK_WINDOW (tree_view
->priv
->search_window
)))
8666 gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (tree_view
->priv
->search_window
)),
8667 GTK_WINDOW (tree_view
->priv
->search_window
));
8668 gtk_window_set_screen (GTK_WINDOW (tree_view
->priv
->search_window
), screen
);
8672 tree_view
->priv
->search_window
= gtk_window_new (GTK_WINDOW_POPUP
);
8673 gtk_window_set_screen (GTK_WINDOW (tree_view
->priv
->search_window
), screen
);
8675 if (gtk_window_get_group (GTK_WINDOW (toplevel
)))
8676 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel
)),
8677 GTK_WINDOW (tree_view
->priv
->search_window
));
8679 gtk_window_set_type_hint (GTK_WINDOW (tree_view
->priv
->search_window
),
8680 GDK_WINDOW_TYPE_HINT_UTILITY
);
8681 gtk_window_set_modal (GTK_WINDOW (tree_view
->priv
->search_window
), TRUE
);
8682 g_signal_connect (tree_view
->priv
->search_window
, "delete-event",
8683 G_CALLBACK (pspp_sheet_view_search_delete_event
),
8685 g_signal_connect (tree_view
->priv
->search_window
, "key-press-event",
8686 G_CALLBACK (pspp_sheet_view_search_key_press_event
),
8688 g_signal_connect (tree_view
->priv
->search_window
, "button-press-event",
8689 G_CALLBACK (pspp_sheet_view_search_button_press_event
),
8691 g_signal_connect (tree_view
->priv
->search_window
, "scroll-event",
8692 G_CALLBACK (pspp_sheet_view_search_scroll_event
),
8695 frame
= gtk_frame_new (NULL
);
8696 gtk_frame_set_shadow_type (GTK_FRAME (frame
), GTK_SHADOW_ETCHED_IN
);
8697 gtk_widget_show (frame
);
8698 gtk_container_add (GTK_CONTAINER (tree_view
->priv
->search_window
), frame
);
8700 vbox
= gtk_vbox_new (FALSE
, 0);
8701 gtk_widget_show (vbox
);
8702 gtk_container_add (GTK_CONTAINER (frame
), vbox
);
8703 gtk_container_set_border_width (GTK_CONTAINER (vbox
), 3);
8706 tree_view
->priv
->search_entry
= gtk_entry_new ();
8707 gtk_widget_show (tree_view
->priv
->search_entry
);
8708 g_signal_connect (tree_view
->priv
->search_entry
, "populate-popup",
8709 G_CALLBACK (pspp_sheet_view_search_disable_popdown
),
8711 g_signal_connect (tree_view
->priv
->search_entry
,
8712 "activate", G_CALLBACK (pspp_sheet_view_search_activate
),
8716 g_signal_connect (GTK_ENTRY (tree_view
->priv
->search_entry
)->im_context
,
8718 G_CALLBACK (pspp_sheet_view_search_preedit_changed
),
8722 gtk_container_add (GTK_CONTAINER (vbox
),
8723 tree_view
->priv
->search_entry
);
8725 gtk_widget_realize (tree_view
->priv
->search_entry
);
8728 /* Pops up the interactive search entry. If keybinding is TRUE then the user
8729 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
8732 pspp_sheet_view_real_start_interactive_search (PsppSheetView
*tree_view
,
8733 gboolean keybinding
)
8735 /* We only start interactive search if we have focus or the columns
8736 * have focus. If one of our children have focus, we don't want to
8740 gboolean found_focus
= FALSE
;
8741 GtkWidgetClass
*entry_parent_class
;
8743 if (!tree_view
->priv
->enable_search
&& !keybinding
)
8746 if (tree_view
->priv
->search_custom_entry_set
)
8749 if (tree_view
->priv
->search_window
!= NULL
&&
8750 gtk_widget_get_visible (tree_view
->priv
->search_window
))
8753 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
8755 PsppSheetViewColumn
*column
;
8757 column
= list
->data
;
8758 if (! column
->visible
)
8761 if (column
->button
&& gtk_widget_has_focus (column
->button
))
8768 if (gtk_widget_has_focus (GTK_WIDGET (tree_view
)))
8774 if (tree_view
->priv
->search_column
< 0)
8777 pspp_sheet_view_ensure_interactive_directory (tree_view
);
8780 gtk_entry_set_text (GTK_ENTRY (tree_view
->priv
->search_entry
), "");
8783 tree_view
->priv
->search_position_func (tree_view
, tree_view
->priv
->search_window
, tree_view
->priv
->search_position_user_data
);
8784 gtk_widget_show (tree_view
->priv
->search_window
);
8785 if (tree_view
->priv
->search_entry_changed_id
== 0)
8787 tree_view
->priv
->search_entry_changed_id
=
8788 g_signal_connect (tree_view
->priv
->search_entry
, "changed",
8789 G_CALLBACK (pspp_sheet_view_search_init
),
8793 tree_view
->priv
->typeselect_flush_timeout
=
8794 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT
,
8795 (GSourceFunc
) pspp_sheet_view_search_entry_flush_timeout
,
8798 /* Grab focus will select all the text. We don't want that to happen, so we
8799 * call the parent instance and bypass the selection change. This is probably
8800 * really non-kosher. */
8801 entry_parent_class
= g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (tree_view
->priv
->search_entry
));
8802 (entry_parent_class
->grab_focus
) (tree_view
->priv
->search_entry
);
8804 /* send focus-in event */
8805 send_focus_change (tree_view
->priv
->search_entry
, TRUE
);
8807 /* search first matching iter */
8808 pspp_sheet_view_search_init (tree_view
->priv
->search_entry
, tree_view
);
8814 pspp_sheet_view_start_interactive_search (PsppSheetView
*tree_view
)
8816 return pspp_sheet_view_real_start_interactive_search (tree_view
, TRUE
);
8819 /* this function returns the new width of the column being resized given
8820 * the column and x position of the cursor; the x cursor position is passed
8821 * in as a pointer and automagicly corrected if it's beyond min/max limits
8824 pspp_sheet_view_new_column_width (PsppSheetView
*tree_view
,
8828 PsppSheetViewColumn
*column
;
8832 /* first translate the x position from gtk_widget_get_window (widget)
8833 * to clist->clist_window
8835 rtl
= (gtk_widget_get_direction (GTK_WIDGET (tree_view
)) == GTK_TEXT_DIR_RTL
);
8836 column
= g_list_nth (tree_view
->priv
->columns
, i
)->data
;
8837 width
= rtl
? (column
->allocation
.x
+ column
->allocation
.width
- *x
) : (*x
- column
->allocation
.x
);
8839 /* Clamp down the value */
8840 if (column
->min_width
== -1)
8841 width
= MAX (column
->button_request
, width
);
8843 width
= MAX (column
->min_width
, width
);
8844 if (column
->max_width
!= -1)
8845 width
= MIN (width
, column
->max_width
);
8847 *x
= rtl
? (column
->allocation
.x
+ column
->allocation
.width
- width
) : (column
->allocation
.x
+ width
);
8853 /* FIXME this adjust_allocation is a big cut-and-paste from
8854 * GtkCList, needs to be some "official" way to do this
8864 /* The window to which gtk_widget_get_window (widget) is relative */
8865 #define ALLOCATION_WINDOW(widget) \
8866 (!gtk_widget_get_has_window (widget) ? \
8867 gtk_widget_get_window (widget) : \
8868 gdk_window_get_parent (gtk_widget_get_window (widget)))
8871 adjust_allocation_recurse (GtkWidget
*widget
,
8874 ScrollData
*scroll_data
= data
;
8875 GtkAllocation allocation
;
8876 gtk_widget_get_allocation (widget
, &allocation
);
8877 /* Need to really size allocate instead of just poking
8878 * into widget->allocation if the widget is not realized.
8879 * FIXME someone figure out why this was.
8881 if (!gtk_widget_get_realized (widget
))
8883 if (gtk_widget_get_visible (widget
))
8885 GdkRectangle tmp_rectangle
= allocation
;
8886 tmp_rectangle
.x
+= scroll_data
->dx
;
8887 tmp_rectangle
.y
+= scroll_data
->dy
;
8889 gtk_widget_size_allocate (widget
, &tmp_rectangle
);
8894 if (ALLOCATION_WINDOW (widget
) == scroll_data
->window
)
8896 allocation
.x
+= scroll_data
->dx
;
8897 allocation
.y
+= scroll_data
->dy
;
8899 if (GTK_IS_CONTAINER (widget
))
8900 gtk_container_forall (GTK_CONTAINER (widget
),
8901 adjust_allocation_recurse
,
8908 adjust_allocation (GtkWidget
*widget
,
8912 ScrollData scroll_data
;
8914 if (gtk_widget_get_realized (widget
))
8915 scroll_data
.window
= ALLOCATION_WINDOW (widget
);
8917 scroll_data
.window
= NULL
;
8919 scroll_data
.dx
= dx
;
8920 scroll_data
.dy
= dy
;
8922 adjust_allocation_recurse (widget
, &scroll_data
);
8926 pspp_sheet_view_column_update_button (PsppSheetViewColumn
*tree_column
);
8930 pspp_sheet_view_adjustment_changed (GtkAdjustment
*adjustment
,
8931 PsppSheetView
*tree_view
)
8933 if (gtk_widget_get_realized (GTK_WIDGET (tree_view
)))
8938 gdk_window_move (tree_view
->priv
->bin_window
,
8939 - gtk_adjustment_get_value (tree_view
->priv
->hadjustment
),
8940 TREE_VIEW_HEADER_HEIGHT (tree_view
));
8941 gdk_window_move (tree_view
->priv
->header_window
,
8942 - gtk_adjustment_get_value (tree_view
->priv
->hadjustment
),
8944 dy
= tree_view
->priv
->dy
- (int) gtk_adjustment_get_value (tree_view
->priv
->vadjustment
);
8947 update_prelight (tree_view
,
8948 tree_view
->priv
->event_last_x
,
8949 tree_view
->priv
->event_last_y
- dy
);
8951 if (tree_view
->priv
->edited_column
&&
8952 GTK_IS_WIDGET (tree_view
->priv
->edited_column
->editable_widget
))
8956 PsppSheetViewChild
*child
= NULL
;
8958 widget
= GTK_WIDGET (tree_view
->priv
->edited_column
->editable_widget
);
8959 adjust_allocation (widget
, 0, dy
);
8961 for (list
= tree_view
->priv
->children
; list
; list
= list
->next
)
8963 child
= (PsppSheetViewChild
*)list
->data
;
8964 if (child
->widget
== widget
)
8972 gdk_window_scroll (tree_view
->priv
->bin_window
, 0, dy
);
8974 if (tree_view
->priv
->dy
!= (int) gtk_adjustment_get_value (tree_view
->priv
->vadjustment
))
8976 /* update our dy and top_row */
8977 tree_view
->priv
->dy
= (int) gtk_adjustment_get_value (tree_view
->priv
->vadjustment
);
8979 if (!tree_view
->priv
->in_top_row_to_dy
)
8980 pspp_sheet_view_dy_to_top_row (tree_view
);
8991 * pspp_sheet_view_new:
8993 * Creates a new #PsppSheetView widget.
8995 * Return value: A newly created #PsppSheetView widget.
8998 pspp_sheet_view_new (void)
9000 return g_object_new (PSPP_TYPE_SHEET_VIEW
, NULL
);
9004 * pspp_sheet_view_new_with_model:
9005 * @model: the model.
9007 * Creates a new #PsppSheetView widget with the model initialized to @model.
9009 * Return value: A newly created #PsppSheetView widget.
9012 pspp_sheet_view_new_with_model (GtkTreeModel
*model
)
9014 return g_object_new (PSPP_TYPE_SHEET_VIEW
, "model", model
, NULL
);
9021 * pspp_sheet_view_get_model:
9022 * @tree_view: a #PsppSheetView
9024 * Returns the model the #PsppSheetView is based on. Returns %NULL if the
9027 * Return value: A #GtkTreeModel, or %NULL if none is currently being used.
9030 pspp_sheet_view_get_model (PsppSheetView
*tree_view
)
9032 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), NULL
);
9034 return tree_view
->priv
->model
;
9038 * pspp_sheet_view_set_model:
9039 * @tree_view: A #GtkTreeNode.
9040 * @model: (allow-none): The model.
9042 * Sets the model for a #PsppSheetView. If the @tree_view already has a model
9043 * set, it will remove it before setting the new model. If @model is %NULL,
9044 * then it will unset the old model.
9047 pspp_sheet_view_set_model (PsppSheetView
*tree_view
,
9048 GtkTreeModel
*model
)
9050 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
9051 g_return_if_fail (model
== NULL
|| GTK_IS_TREE_MODEL (model
));
9053 if (model
== tree_view
->priv
->model
)
9056 if (tree_view
->priv
->scroll_to_path
)
9058 gtk_tree_row_reference_free (tree_view
->priv
->scroll_to_path
);
9059 tree_view
->priv
->scroll_to_path
= NULL
;
9062 if (tree_view
->priv
->model
)
9064 GList
*tmplist
= tree_view
->priv
->columns
;
9066 if (tree_view
->priv
->selected
)
9067 range_tower_set0 (tree_view
->priv
->selected
, 0, ULONG_MAX
);
9068 pspp_sheet_view_stop_editing (tree_view
, TRUE
);
9070 g_signal_handlers_disconnect_by_func (tree_view
->priv
->model
,
9071 pspp_sheet_view_row_changed
,
9073 g_signal_handlers_disconnect_by_func (tree_view
->priv
->model
,
9074 pspp_sheet_view_row_inserted
,
9076 g_signal_handlers_disconnect_by_func (tree_view
->priv
->model
,
9077 pspp_sheet_view_row_deleted
,
9079 g_signal_handlers_disconnect_by_func (tree_view
->priv
->model
,
9080 pspp_sheet_view_rows_reordered
,
9083 for (; tmplist
; tmplist
= tmplist
->next
)
9084 _pspp_sheet_view_column_unset_model (tmplist
->data
,
9085 tree_view
->priv
->model
);
9087 tree_view
->priv
->prelight_node
= -1;
9089 gtk_tree_row_reference_free (tree_view
->priv
->drag_dest_row
);
9090 tree_view
->priv
->drag_dest_row
= NULL
;
9091 gtk_tree_row_reference_free (tree_view
->priv
->cursor
);
9092 tree_view
->priv
->cursor
= NULL
;
9093 gtk_tree_row_reference_free (tree_view
->priv
->anchor
);
9094 tree_view
->priv
->anchor
= NULL
;
9095 gtk_tree_row_reference_free (tree_view
->priv
->top_row
);
9096 tree_view
->priv
->top_row
= NULL
;
9097 gtk_tree_row_reference_free (tree_view
->priv
->scroll_to_path
);
9098 tree_view
->priv
->scroll_to_path
= NULL
;
9100 tree_view
->priv
->scroll_to_column
= NULL
;
9102 g_object_unref (tree_view
->priv
->model
);
9104 tree_view
->priv
->search_column
= -1;
9105 tree_view
->priv
->fixed_height
= -1;
9106 tree_view
->priv
->dy
= tree_view
->priv
->top_row_dy
= 0;
9107 tree_view
->priv
->last_button_x
= -1;
9108 tree_view
->priv
->last_button_y
= -1;
9111 tree_view
->priv
->model
= model
;
9113 if (tree_view
->priv
->model
)
9117 if (tree_view
->priv
->search_column
== -1)
9119 for (i
= 0; i
< gtk_tree_model_get_n_columns (model
); i
++)
9121 GType type
= gtk_tree_model_get_column_type (model
, i
);
9123 if (g_value_type_transformable (type
, G_TYPE_STRING
))
9125 tree_view
->priv
->search_column
= i
;
9131 g_object_ref (tree_view
->priv
->model
);
9132 g_signal_connect (tree_view
->priv
->model
,
9134 G_CALLBACK (pspp_sheet_view_row_changed
),
9136 g_signal_connect (tree_view
->priv
->model
,
9138 G_CALLBACK (pspp_sheet_view_row_inserted
),
9140 g_signal_connect (tree_view
->priv
->model
,
9142 G_CALLBACK (pspp_sheet_view_row_deleted
),
9144 g_signal_connect (tree_view
->priv
->model
,
9146 G_CALLBACK (pspp_sheet_view_rows_reordered
),
9149 tree_view
->priv
->row_count
= gtk_tree_model_iter_n_children (tree_view
->priv
->model
, NULL
);
9151 /* FIXME: do I need to do this? pspp_sheet_view_create_buttons (tree_view); */
9152 install_presize_handler (tree_view
);
9155 g_object_notify (G_OBJECT (tree_view
), "model");
9157 if (tree_view
->priv
->selection
)
9158 _pspp_sheet_selection_emit_changed (tree_view
->priv
->selection
);
9160 if (gtk_widget_get_realized (GTK_WIDGET (tree_view
)))
9161 gtk_widget_queue_resize (GTK_WIDGET (tree_view
));
9165 * pspp_sheet_view_get_selection:
9166 * @tree_view: A #PsppSheetView.
9168 * Gets the #PsppSheetSelection associated with @tree_view.
9170 * Return value: A #PsppSheetSelection object.
9172 PsppSheetSelection
*
9173 pspp_sheet_view_get_selection (PsppSheetView
*tree_view
)
9175 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), NULL
);
9177 return tree_view
->priv
->selection
;
9181 * pspp_sheet_view_get_hadjustment:
9182 * @tree_view: A #PsppSheetView
9184 * Gets the #GtkAdjustment currently being used for the horizontal aspect.
9186 * Return value: A #GtkAdjustment object, or %NULL if none is currently being
9190 pspp_sheet_view_get_hadjustment (PsppSheetView
*tree_view
)
9192 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), NULL
);
9194 return pspp_sheet_view_do_get_hadjustment (tree_view
);
9197 static GtkAdjustment
*
9198 pspp_sheet_view_do_get_hadjustment (PsppSheetView
*tree_view
)
9200 return tree_view
->priv
->hadjustment
;
9204 * pspp_sheet_view_set_hadjustment:
9205 * @tree_view: A #PsppSheetView
9206 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9208 * Sets the #GtkAdjustment for the current horizontal aspect.
9211 pspp_sheet_view_set_hadjustment (PsppSheetView
*tree_view
,
9212 GtkAdjustment
*adjustment
)
9214 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
9216 pspp_sheet_view_set_adjustments (tree_view
,
9218 tree_view
->priv
->vadjustment
);
9220 g_object_notify (G_OBJECT (tree_view
), "hadjustment");
9224 pspp_sheet_view_do_set_hadjustment (PsppSheetView
*tree_view
,
9225 GtkAdjustment
*adjustment
)
9227 PsppSheetViewPrivate
*priv
= tree_view
->priv
;
9229 if (adjustment
&& priv
->hadjustment
== adjustment
)
9232 if (priv
->hadjustment
!= NULL
)
9234 g_signal_handlers_disconnect_by_func (priv
->hadjustment
,
9235 pspp_sheet_view_adjustment_changed
,
9237 g_object_unref (priv
->hadjustment
);
9240 if (adjustment
== NULL
)
9241 adjustment
= gtk_adjustment_new (0.0, 0.0, 0.0,
9244 g_signal_connect (adjustment
, "value-changed",
9245 G_CALLBACK (pspp_sheet_view_adjustment_changed
), tree_view
);
9246 priv
->hadjustment
= g_object_ref_sink (adjustment
);
9247 /* FIXME: Adjustment should probably be populated here with fresh values, but
9248 * internal details are too complicated for me to decipher right now.
9250 pspp_sheet_view_adjustment_changed (NULL
, tree_view
);
9252 g_object_notify (G_OBJECT (tree_view
), "hadjustment");
9256 * pspp_sheet_view_get_vadjustment:
9257 * @tree_view: A #PsppSheetView
9259 * Gets the #GtkAdjustment currently being used for the vertical aspect.
9261 * Return value: (transfer none): A #GtkAdjustment object, or %NULL
9262 * if none is currently being used.
9264 * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
9267 pspp_sheet_view_get_vadjustment (PsppSheetView
*tree_view
)
9269 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), NULL
);
9271 return pspp_sheet_view_do_get_vadjustment (tree_view
);
9274 static GtkAdjustment
*
9275 pspp_sheet_view_do_get_vadjustment (PsppSheetView
*tree_view
)
9277 return tree_view
->priv
->vadjustment
;
9281 * pspp_sheet_view_set_vadjustment:
9282 * @tree_view: A #PsppSheetView
9283 * @adjustment: (allow-none): The #GtkAdjustment to set, or %NULL
9285 * Sets the #GtkAdjustment for the current vertical aspect.
9287 * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
9290 pspp_sheet_view_set_vadjustment (PsppSheetView
*tree_view
,
9291 GtkAdjustment
*adjustment
)
9293 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
9294 g_return_if_fail (adjustment
== NULL
|| GTK_IS_ADJUSTMENT (adjustment
));
9296 pspp_sheet_view_do_set_vadjustment (tree_view
, adjustment
);
9300 pspp_sheet_view_do_set_vadjustment (PsppSheetView
*tree_view
,
9301 GtkAdjustment
*adjustment
)
9303 PsppSheetViewPrivate
*priv
= tree_view
->priv
;
9305 if (adjustment
&& priv
->vadjustment
== adjustment
)
9308 if (priv
->vadjustment
!= NULL
)
9310 g_signal_handlers_disconnect_by_func (priv
->vadjustment
,
9311 pspp_sheet_view_adjustment_changed
,
9313 g_object_unref (priv
->vadjustment
);
9316 if (adjustment
== NULL
)
9317 adjustment
= gtk_adjustment_new (0.0, 0.0, 0.0,
9320 g_signal_connect (adjustment
, "value-changed",
9321 G_CALLBACK (pspp_sheet_view_adjustment_changed
), tree_view
);
9322 priv
->vadjustment
= g_object_ref_sink (adjustment
);
9323 /* FIXME: Adjustment should probably be populated here with fresh values, but
9324 * internal details are too complicated for me to decipher right now.
9326 pspp_sheet_view_adjustment_changed (NULL
, tree_view
);
9327 g_object_notify (G_OBJECT (tree_view
), "vadjustment");
9330 /* Column and header operations */
9333 * pspp_sheet_view_get_headers_visible:
9334 * @tree_view: A #PsppSheetView.
9336 * Returns %TRUE if the headers on the @tree_view are visible.
9338 * Return value: Whether the headers are visible or not.
9341 pspp_sheet_view_get_headers_visible (PsppSheetView
*tree_view
)
9343 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), FALSE
);
9345 return PSPP_SHEET_VIEW_FLAG_SET (tree_view
, PSPP_SHEET_VIEW_HEADERS_VISIBLE
);
9349 * pspp_sheet_view_set_headers_visible:
9350 * @tree_view: A #PsppSheetView.
9351 * @headers_visible: %TRUE if the headers are visible
9353 * Sets the visibility state of the headers.
9356 pspp_sheet_view_set_headers_visible (PsppSheetView
*tree_view
,
9357 gboolean headers_visible
)
9361 PsppSheetViewColumn
*column
;
9362 GtkAllocation allocation
;
9364 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
9366 gtk_widget_get_allocation (GTK_WIDGET (tree_view
), &allocation
);
9368 headers_visible
= !! headers_visible
;
9370 if (PSPP_SHEET_VIEW_FLAG_SET (tree_view
, PSPP_SHEET_VIEW_HEADERS_VISIBLE
) == headers_visible
)
9373 if (headers_visible
)
9374 PSPP_SHEET_VIEW_SET_FLAG (tree_view
, PSPP_SHEET_VIEW_HEADERS_VISIBLE
);
9376 PSPP_SHEET_VIEW_UNSET_FLAG (tree_view
, PSPP_SHEET_VIEW_HEADERS_VISIBLE
);
9378 if (gtk_widget_get_realized (GTK_WIDGET (tree_view
)))
9380 gdk_window_get_position (tree_view
->priv
->bin_window
, &x
, &y
);
9381 if (headers_visible
)
9383 gdk_window_move_resize (tree_view
->priv
->bin_window
, x
, y
+ TREE_VIEW_HEADER_HEIGHT (tree_view
),
9384 tree_view
->priv
->width
, allocation
.height
- + TREE_VIEW_HEADER_HEIGHT (tree_view
));
9386 if (gtk_widget_get_mapped (GTK_WIDGET (tree_view
)))
9387 pspp_sheet_view_map_buttons (tree_view
);
9391 gdk_window_move_resize (tree_view
->priv
->bin_window
, x
, y
, tree_view
->priv
->width
, tree_view
->priv
->height
);
9393 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
9395 column
= list
->data
;
9397 gtk_widget_unmap (column
->button
);
9399 gdk_window_hide (tree_view
->priv
->header_window
);
9403 gtk_adjustment_set_page_size (tree_view
->priv
->vadjustment
, allocation
.height
- TREE_VIEW_HEADER_HEIGHT (tree_view
));
9404 gtk_adjustment_set_page_increment (tree_view
->priv
->vadjustment
, (allocation
.height
- TREE_VIEW_HEADER_HEIGHT (tree_view
)) / 2);
9405 gtk_adjustment_set_lower (tree_view
->priv
->vadjustment
, 0);
9406 gtk_adjustment_set_upper (tree_view
->priv
->vadjustment
, tree_view
->priv
->height
);
9407 gtk_adjustment_changed (tree_view
->priv
->vadjustment
);
9409 gtk_widget_queue_resize (GTK_WIDGET (tree_view
));
9411 g_object_notify (G_OBJECT (tree_view
), "headers-visible");
9415 * pspp_sheet_view_columns_autosize:
9416 * @tree_view: A #PsppSheetView.
9418 * Resizes all columns to their optimal width. Only works after the
9419 * treeview has been realized.
9422 pspp_sheet_view_columns_autosize (PsppSheetView
*tree_view
)
9424 gboolean dirty
= FALSE
;
9426 PsppSheetViewColumn
*column
;
9428 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
9430 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
9432 column
= list
->data
;
9433 _pspp_sheet_view_column_cell_set_dirty (column
);
9438 gtk_widget_queue_resize (GTK_WIDGET (tree_view
));
9442 * pspp_sheet_view_set_headers_clickable:
9443 * @tree_view: A #PsppSheetView.
9444 * @setting: %TRUE if the columns are clickable.
9446 * Allow the column title buttons to be clicked.
9449 pspp_sheet_view_set_headers_clickable (PsppSheetView
*tree_view
,
9454 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
9456 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
9457 pspp_sheet_view_column_set_clickable (PSPP_SHEET_VIEW_COLUMN (list
->data
), setting
);
9459 g_object_notify (G_OBJECT (tree_view
), "headers-clickable");
9464 * pspp_sheet_view_get_headers_clickable:
9465 * @tree_view: A #PsppSheetView.
9467 * Returns whether all header columns are clickable.
9469 * Return value: %TRUE if all header columns are clickable, otherwise %FALSE
9474 pspp_sheet_view_get_headers_clickable (PsppSheetView
*tree_view
)
9478 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), FALSE
);
9480 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
9481 if (!PSPP_SHEET_VIEW_COLUMN (list
->data
)->clickable
)
9488 * pspp_sheet_view_set_rules_hint
9489 * @tree_view: a #PsppSheetView
9490 * @setting: %TRUE if the tree requires reading across rows
9492 * This function tells GTK+ that the user interface for your
9493 * application requires users to read across tree rows and associate
9494 * cells with one another. By default, GTK+ will then render the tree
9495 * with alternating row colors. Do <emphasis>not</emphasis> use it
9496 * just because you prefer the appearance of the ruled tree; that's a
9497 * question for the theme. Some themes will draw tree rows in
9498 * alternating colors even when rules are turned off, and users who
9499 * prefer that appearance all the time can choose those themes. You
9500 * should call this function only as a <emphasis>semantic</emphasis>
9501 * hint to the theme engine that your tree makes alternating colors
9502 * useful from a functional standpoint (since it has lots of columns,
9507 pspp_sheet_view_set_rules_hint (PsppSheetView
*tree_view
,
9510 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
9512 setting
= setting
!= FALSE
;
9514 if (tree_view
->priv
->has_rules
!= setting
)
9516 tree_view
->priv
->has_rules
= setting
;
9517 gtk_widget_queue_draw (GTK_WIDGET (tree_view
));
9520 g_object_notify (G_OBJECT (tree_view
), "rules-hint");
9524 * pspp_sheet_view_get_rules_hint
9525 * @tree_view: a #PsppSheetView
9527 * Gets the setting set by pspp_sheet_view_set_rules_hint().
9529 * Return value: %TRUE if rules are useful for the user of this tree
9532 pspp_sheet_view_get_rules_hint (PsppSheetView
*tree_view
)
9534 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), FALSE
);
9536 return tree_view
->priv
->has_rules
;
9539 /* Public Column functions
9543 * pspp_sheet_view_append_column:
9544 * @tree_view: A #PsppSheetView.
9545 * @column: The #PsppSheetViewColumn to add.
9547 * Appends @column to the list of columns.
9549 * Return value: The number of columns in @tree_view after appending.
9552 pspp_sheet_view_append_column (PsppSheetView
*tree_view
,
9553 PsppSheetViewColumn
*column
)
9555 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), -1);
9556 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column
), -1);
9557 g_return_val_if_fail (column
->tree_view
== NULL
, -1);
9559 return pspp_sheet_view_insert_column (tree_view
, column
, -1);
9564 * pspp_sheet_view_remove_column:
9565 * @tree_view: A #PsppSheetView.
9566 * @column: The #PsppSheetViewColumn to remove.
9568 * Removes @column from @tree_view.
9570 * Return value: The number of columns in @tree_view after removing.
9573 pspp_sheet_view_remove_column (PsppSheetView
*tree_view
,
9574 PsppSheetViewColumn
*column
)
9576 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), -1);
9577 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column
), -1);
9578 g_return_val_if_fail (column
->tree_view
== GTK_WIDGET (tree_view
), -1);
9580 if (tree_view
->priv
->focus_column
== column
)
9581 tree_view
->priv
->focus_column
= NULL
;
9583 if (tree_view
->priv
->edited_column
== column
)
9585 pspp_sheet_view_stop_editing (tree_view
, TRUE
);
9587 /* no need to, but just to be sure ... */
9588 tree_view
->priv
->edited_column
= NULL
;
9591 _pspp_sheet_view_column_unset_tree_view (column
);
9593 tree_view
->priv
->columns
= g_list_remove (tree_view
->priv
->columns
, column
);
9594 tree_view
->priv
->n_columns
--;
9596 if (gtk_widget_get_realized (GTK_WIDGET (tree_view
)))
9600 _pspp_sheet_view_column_unrealize_button (column
);
9601 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
9603 PsppSheetViewColumn
*tmp_column
;
9605 tmp_column
= PSPP_SHEET_VIEW_COLUMN (list
->data
);
9606 if (tmp_column
->visible
)
9607 _pspp_sheet_view_column_cell_set_dirty (tmp_column
);
9610 if (tree_view
->priv
->n_columns
== 0 &&
9611 pspp_sheet_view_get_headers_visible (tree_view
) &&
9612 tree_view
->priv
->header_window
)
9613 gdk_window_hide (tree_view
->priv
->header_window
);
9615 gtk_widget_queue_resize (GTK_WIDGET (tree_view
));
9618 g_object_unref (column
);
9619 g_signal_emit (tree_view
, tree_view_signals
[COLUMNS_CHANGED
], 0);
9621 return tree_view
->priv
->n_columns
;
9625 * pspp_sheet_view_insert_column:
9626 * @tree_view: A #PsppSheetView.
9627 * @column: The #PsppSheetViewColumn to be inserted.
9628 * @position: The position to insert @column in.
9630 * This inserts the @column into the @tree_view at @position. If @position is
9631 * -1, then the column is inserted at the end.
9633 * Return value: The number of columns in @tree_view after insertion.
9636 pspp_sheet_view_insert_column (PsppSheetView
*tree_view
,
9637 PsppSheetViewColumn
*column
,
9640 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), -1);
9641 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column
), -1);
9642 g_return_val_if_fail (column
->tree_view
== NULL
, -1);
9644 g_object_ref_sink (column
);
9646 if (tree_view
->priv
->n_columns
== 0 &&
9647 gtk_widget_get_realized (GTK_WIDGET (tree_view
)) &&
9648 pspp_sheet_view_get_headers_visible (tree_view
))
9650 gdk_window_show (tree_view
->priv
->header_window
);
9653 tree_view
->priv
->columns
= g_list_insert (tree_view
->priv
->columns
,
9655 tree_view
->priv
->n_columns
++;
9657 _pspp_sheet_view_column_set_tree_view (column
, tree_view
);
9659 if (gtk_widget_get_realized (GTK_WIDGET (tree_view
)))
9663 _pspp_sheet_view_column_realize_button (column
);
9665 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
9667 column
= PSPP_SHEET_VIEW_COLUMN (list
->data
);
9668 if (column
->visible
)
9669 _pspp_sheet_view_column_cell_set_dirty (column
);
9671 gtk_widget_queue_resize (GTK_WIDGET (tree_view
));
9674 g_signal_emit (tree_view
, tree_view_signals
[COLUMNS_CHANGED
], 0);
9676 return tree_view
->priv
->n_columns
;
9680 * pspp_sheet_view_insert_column_with_attributes:
9681 * @tree_view: A #PsppSheetView
9682 * @position: The position to insert the new column in.
9683 * @title: The title to set the header to.
9684 * @cell: The #GtkCellRenderer.
9685 * @Varargs: A %NULL-terminated list of attributes.
9687 * Creates a new #PsppSheetViewColumn and inserts it into the @tree_view at
9688 * @position. If @position is -1, then the newly created column is inserted at
9689 * the end. The column is initialized with the attributes given.
9691 * Return value: The number of columns in @tree_view after insertion.
9694 pspp_sheet_view_insert_column_with_attributes (PsppSheetView
*tree_view
,
9697 GtkCellRenderer
*cell
,
9700 PsppSheetViewColumn
*column
;
9705 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), -1);
9707 column
= pspp_sheet_view_column_new ();
9708 pspp_sheet_view_column_set_title (column
, title
);
9709 pspp_sheet_view_column_pack_start (column
, cell
, TRUE
);
9711 va_start (args
, cell
);
9713 attribute
= va_arg (args
, gchar
*);
9715 while (attribute
!= NULL
)
9717 column_id
= va_arg (args
, gint
);
9718 pspp_sheet_view_column_add_attribute (column
, cell
, attribute
, column_id
);
9719 attribute
= va_arg (args
, gchar
*);
9724 pspp_sheet_view_insert_column (tree_view
, column
, position
);
9726 return tree_view
->priv
->n_columns
;
9730 * pspp_sheet_view_insert_column_with_data_func:
9731 * @tree_view: a #PsppSheetView
9732 * @position: Position to insert, -1 for append
9733 * @title: column title
9734 * @cell: cell renderer for column
9735 * @func: function to set attributes of cell renderer
9736 * @data: data for @func
9737 * @dnotify: destroy notifier for @data
9739 * Convenience function that inserts a new column into the #PsppSheetView
9740 * with the given cell renderer and a #GtkCellDataFunc to set cell renderer
9741 * attributes (normally using data from the model). See also
9742 * pspp_sheet_view_column_set_cell_data_func(), pspp_sheet_view_column_pack_start().
9744 * Return value: number of columns in the tree view post-insert
9747 pspp_sheet_view_insert_column_with_data_func (PsppSheetView
*tree_view
,
9750 GtkCellRenderer
*cell
,
9751 PsppSheetCellDataFunc func
,
9753 GDestroyNotify dnotify
)
9755 PsppSheetViewColumn
*column
;
9757 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), -1);
9759 column
= pspp_sheet_view_column_new ();
9760 pspp_sheet_view_column_set_title (column
, title
);
9761 pspp_sheet_view_column_pack_start (column
, cell
, TRUE
);
9762 pspp_sheet_view_column_set_cell_data_func (column
, cell
, func
, data
, dnotify
);
9764 pspp_sheet_view_insert_column (tree_view
, column
, position
);
9766 return tree_view
->priv
->n_columns
;
9770 * pspp_sheet_view_get_column:
9771 * @tree_view: A #PsppSheetView.
9772 * @n: The position of the column, counting from 0.
9774 * Gets the #PsppSheetViewColumn at the given position in the #tree_view.
9776 * Return value: The #PsppSheetViewColumn, or %NULL if the position is outside the
9779 PsppSheetViewColumn
*
9780 pspp_sheet_view_get_column (PsppSheetView
*tree_view
,
9783 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), NULL
);
9785 if (n
< 0 || n
>= tree_view
->priv
->n_columns
)
9788 if (tree_view
->priv
->columns
== NULL
)
9791 return PSPP_SHEET_VIEW_COLUMN (g_list_nth (tree_view
->priv
->columns
, n
)->data
);
9795 * pspp_sheet_view_get_columns:
9796 * @tree_view: A #PsppSheetView
9798 * Returns a #GList of all the #PsppSheetViewColumn s currently in @tree_view.
9799 * The returned list must be freed with g_list_free ().
9801 * Return value: (element-type PsppSheetViewColumn) (transfer container): A list of #PsppSheetViewColumn s
9804 pspp_sheet_view_get_columns (PsppSheetView
*tree_view
)
9806 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), NULL
);
9808 return g_list_copy (tree_view
->priv
->columns
);
9812 * pspp_sheet_view_move_column_after:
9813 * @tree_view: A #PsppSheetView
9814 * @column: The #PsppSheetViewColumn to be moved.
9815 * @base_column: (allow-none): The #PsppSheetViewColumn to be moved relative to, or %NULL.
9817 * Moves @column to be after to @base_column. If @base_column is %NULL, then
9818 * @column is placed in the first position.
9821 pspp_sheet_view_move_column_after (PsppSheetView
*tree_view
,
9822 PsppSheetViewColumn
*column
,
9823 PsppSheetViewColumn
*base_column
)
9825 GList
*column_list_el
, *base_el
= NULL
;
9827 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
9829 column_list_el
= g_list_find (tree_view
->priv
->columns
, column
);
9830 g_return_if_fail (column_list_el
!= NULL
);
9834 base_el
= g_list_find (tree_view
->priv
->columns
, base_column
);
9835 g_return_if_fail (base_el
!= NULL
);
9838 if (column_list_el
->prev
== base_el
)
9841 tree_view
->priv
->columns
= g_list_remove_link (tree_view
->priv
->columns
, column_list_el
);
9842 if (base_el
== NULL
)
9844 column_list_el
->prev
= NULL
;
9845 column_list_el
->next
= tree_view
->priv
->columns
;
9846 if (column_list_el
->next
)
9847 column_list_el
->next
->prev
= column_list_el
;
9848 tree_view
->priv
->columns
= column_list_el
;
9852 column_list_el
->prev
= base_el
;
9853 column_list_el
->next
= base_el
->next
;
9854 if (column_list_el
->next
)
9855 column_list_el
->next
->prev
= column_list_el
;
9856 base_el
->next
= column_list_el
;
9859 if (gtk_widget_get_realized (GTK_WIDGET (tree_view
)))
9861 gtk_widget_queue_resize (GTK_WIDGET (tree_view
));
9862 pspp_sheet_view_size_allocate_columns (GTK_WIDGET (tree_view
), NULL
);
9865 g_signal_emit (tree_view
, tree_view_signals
[COLUMNS_CHANGED
], 0);
9869 * pspp_sheet_view_set_column_drag_function:
9870 * @tree_view: A #PsppSheetView.
9871 * @func: (allow-none): A function to determine which columns are reorderable, or %NULL.
9872 * @user_data: (allow-none): User data to be passed to @func, or %NULL
9873 * @destroy: (allow-none): Destroy notifier for @user_data, or %NULL
9875 * Sets a user function for determining where a column may be dropped when
9876 * dragged. This function is called on every column pair in turn at the
9877 * beginning of a column drag to determine where a drop can take place. The
9878 * arguments passed to @func are: the @tree_view, the #PsppSheetViewColumn being
9879 * dragged, the two #PsppSheetViewColumn s determining the drop spot, and
9880 * @user_data. If either of the #PsppSheetViewColumn arguments for the drop spot
9881 * are %NULL, then they indicate an edge. If @func is set to be %NULL, then
9882 * @tree_view reverts to the default behavior of allowing all columns to be
9883 * dropped everywhere.
9886 pspp_sheet_view_set_column_drag_function (PsppSheetView
*tree_view
,
9887 PsppSheetViewColumnDropFunc func
,
9889 GDestroyNotify destroy
)
9891 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
9893 if (tree_view
->priv
->column_drop_func_data_destroy
)
9894 tree_view
->priv
->column_drop_func_data_destroy (tree_view
->priv
->column_drop_func_data
);
9896 tree_view
->priv
->column_drop_func
= func
;
9897 tree_view
->priv
->column_drop_func_data
= user_data
;
9898 tree_view
->priv
->column_drop_func_data_destroy
= destroy
;
9902 * pspp_sheet_view_scroll_to_point:
9903 * @tree_view: a #PsppSheetView
9904 * @tree_x: X coordinate of new top-left pixel of visible area, or -1
9905 * @tree_y: Y coordinate of new top-left pixel of visible area, or -1
9907 * Scrolls the tree view such that the top-left corner of the visible
9908 * area is @tree_x, @tree_y, where @tree_x and @tree_y are specified
9909 * in tree coordinates. The @tree_view must be realized before
9910 * this function is called. If it isn't, you probably want to be
9911 * using pspp_sheet_view_scroll_to_cell().
9913 * If either @tree_x or @tree_y are -1, then that direction isn't scrolled.
9916 pspp_sheet_view_scroll_to_point (PsppSheetView
*tree_view
,
9920 GtkAdjustment
*hadj
;
9921 GtkAdjustment
*vadj
;
9923 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
9924 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view
)));
9926 hadj
= tree_view
->priv
->hadjustment
;
9927 vadj
= tree_view
->priv
->vadjustment
;
9930 gtk_adjustment_set_value (hadj
, CLAMP (tree_x
, gtk_adjustment_get_lower (hadj
), gtk_adjustment_get_upper (hadj
) - gtk_adjustment_get_page_size (hadj
)));
9932 gtk_adjustment_set_value (vadj
, CLAMP (tree_y
, gtk_adjustment_get_lower (vadj
), gtk_adjustment_get_upper (vadj
) - gtk_adjustment_get_page_size (vadj
)));
9936 * pspp_sheet_view_scroll_to_cell:
9937 * @tree_view: A #PsppSheetView.
9938 * @path: (allow-none): The path of the row to move to, or %NULL.
9939 * @column: (allow-none): The #PsppSheetViewColumn to move horizontally to, or %NULL.
9940 * @use_align: whether to use alignment arguments, or %FALSE.
9941 * @row_align: The vertical alignment of the row specified by @path.
9942 * @col_align: The horizontal alignment of the column specified by @column.
9944 * Moves the alignments of @tree_view to the position specified by @column and
9945 * @path. If @column is %NULL, then no horizontal scrolling occurs. Likewise,
9946 * if @path is %NULL no vertical scrolling occurs. At a minimum, one of @column
9947 * or @path need to be non-%NULL. @row_align determines where the row is
9948 * placed, and @col_align determines where @column is placed. Both are expected
9949 * to be between 0.0 and 1.0. 0.0 means left/top alignment, 1.0 means
9950 * right/bottom alignment, 0.5 means center.
9952 * If @use_align is %FALSE, then the alignment arguments are ignored, and the
9953 * tree does the minimum amount of work to scroll the cell onto the screen.
9954 * This means that the cell will be scrolled to the edge closest to its current
9955 * position. If the cell is currently visible on the screen, nothing is done.
9957 * This function only works if the model is set, and @path is a valid row on the
9958 * model. If the model changes before the @tree_view is realized, the centered
9959 * path will be modified to reflect this change.
9962 pspp_sheet_view_scroll_to_cell (PsppSheetView
*tree_view
,
9964 PsppSheetViewColumn
*column
,
9969 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
9970 g_return_if_fail (tree_view
->priv
->model
!= NULL
);
9971 g_return_if_fail (row_align
>= 0.0 && row_align
<= 1.0);
9972 g_return_if_fail (col_align
>= 0.0 && col_align
<= 1.0);
9973 g_return_if_fail (path
!= NULL
|| column
!= NULL
);
9976 g_print ("pspp_sheet_view_scroll_to_cell:\npath: %s\ncolumn: %s\nuse_align: %d\nrow_align: %f\ncol_align: %f\n",
9977 gtk_tree_path_to_string (path
), column
?"non-null":"null", use_align
, row_align
, col_align
);
9979 row_align
= CLAMP (row_align
, 0.0, 1.0);
9980 col_align
= CLAMP (col_align
, 0.0, 1.0);
9983 /* Note: Despite the benefits that come from having one code path for the
9984 * scrolling code, we short-circuit validate_visible_area's immplementation as
9985 * it is much slower than just going to the point.
9987 if (!gtk_widget_get_visible (GTK_WIDGET (tree_view
)) ||
9988 !gtk_widget_get_realized (GTK_WIDGET (tree_view
))
9989 /* XXX || GTK_WIDGET_ALLOC_NEEDED (tree_view) */)
9991 if (tree_view
->priv
->scroll_to_path
)
9992 gtk_tree_row_reference_free (tree_view
->priv
->scroll_to_path
);
9994 tree_view
->priv
->scroll_to_path
= NULL
;
9995 tree_view
->priv
->scroll_to_column
= NULL
;
9998 tree_view
->priv
->scroll_to_path
= gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view
), tree_view
->priv
->model
, path
);
10000 tree_view
->priv
->scroll_to_column
= column
;
10001 tree_view
->priv
->scroll_to_use_align
= use_align
;
10002 tree_view
->priv
->scroll_to_row_align
= row_align
;
10003 tree_view
->priv
->scroll_to_col_align
= col_align
;
10005 install_presize_handler (tree_view
);
10009 GdkRectangle cell_rect
;
10010 GdkRectangle vis_rect
;
10011 gint dest_x
, dest_y
;
10013 pspp_sheet_view_get_background_area (tree_view
, path
, column
, &cell_rect
);
10014 pspp_sheet_view_get_visible_rect (tree_view
, &vis_rect
);
10016 cell_rect
.y
= TREE_WINDOW_Y_TO_RBTREE_Y (tree_view
, cell_rect
.y
);
10018 dest_x
= vis_rect
.x
;
10019 dest_y
= vis_rect
.y
;
10025 dest_x
= cell_rect
.x
- ((vis_rect
.width
- cell_rect
.width
) * col_align
);
10029 if (cell_rect
.x
< vis_rect
.x
)
10030 dest_x
= cell_rect
.x
;
10031 if (cell_rect
.x
+ cell_rect
.width
> vis_rect
.x
+ vis_rect
.width
)
10032 dest_x
= cell_rect
.x
+ cell_rect
.width
- vis_rect
.width
;
10040 dest_y
= cell_rect
.y
- ((vis_rect
.height
- cell_rect
.height
) * row_align
);
10041 dest_y
= MAX (dest_y
, 0);
10045 if (cell_rect
.y
< vis_rect
.y
)
10046 dest_y
= cell_rect
.y
;
10047 if (cell_rect
.y
+ cell_rect
.height
> vis_rect
.y
+ vis_rect
.height
)
10048 dest_y
= cell_rect
.y
+ cell_rect
.height
- vis_rect
.height
;
10052 pspp_sheet_view_scroll_to_point (tree_view
, dest_x
, dest_y
);
10057 * pspp_sheet_view_row_activated:
10058 * @tree_view: A #PsppSheetView
10059 * @path: The #GtkTreePath to be activated.
10060 * @column: The #PsppSheetViewColumn to be activated.
10062 * Activates the cell determined by @path and @column.
10065 pspp_sheet_view_row_activated (PsppSheetView
*tree_view
,
10067 PsppSheetViewColumn
*column
)
10069 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
10071 g_signal_emit (tree_view
, tree_view_signals
[ROW_ACTIVATED
], 0, path
, column
);
10076 * pspp_sheet_view_get_reorderable:
10077 * @tree_view: a #PsppSheetView
10079 * Retrieves whether the user can reorder the tree via drag-and-drop. See
10080 * pspp_sheet_view_set_reorderable().
10082 * Return value: %TRUE if the tree can be reordered.
10085 pspp_sheet_view_get_reorderable (PsppSheetView
*tree_view
)
10087 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), FALSE
);
10089 return tree_view
->priv
->reorderable
;
10093 * pspp_sheet_view_set_reorderable:
10094 * @tree_view: A #PsppSheetView.
10095 * @reorderable: %TRUE, if the tree can be reordered.
10097 * This function is a convenience function to allow you to reorder
10098 * models that support the #GtkDragSourceIface and the
10099 * #GtkDragDestIface. Both #GtkTreeStore and #GtkListStore support
10100 * these. If @reorderable is %TRUE, then the user can reorder the
10101 * model by dragging and dropping rows. The developer can listen to
10102 * these changes by connecting to the model's row_inserted and
10103 * row_deleted signals. The reordering is implemented by setting up
10104 * the tree view as a drag source and destination. Therefore, drag and
10105 * drop can not be used in a reorderable view for any other purpose.
10107 * This function does not give you any degree of control over the order -- any
10108 * reordering is allowed. If more control is needed, you should probably
10109 * handle drag and drop manually.
10112 pspp_sheet_view_set_reorderable (PsppSheetView
*tree_view
,
10113 gboolean reorderable
)
10115 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
10117 reorderable
= reorderable
!= FALSE
;
10119 if (tree_view
->priv
->reorderable
== reorderable
)
10124 const GtkTargetEntry row_targets
[] = {
10125 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET
, 0 }
10128 pspp_sheet_view_enable_model_drag_source (tree_view
,
10131 G_N_ELEMENTS (row_targets
),
10133 pspp_sheet_view_enable_model_drag_dest (tree_view
,
10135 G_N_ELEMENTS (row_targets
),
10140 pspp_sheet_view_unset_rows_drag_source (tree_view
);
10141 pspp_sheet_view_unset_rows_drag_dest (tree_view
);
10144 tree_view
->priv
->reorderable
= reorderable
;
10146 g_object_notify (G_OBJECT (tree_view
), "reorderable");
10149 /* If CLEAR_AND_SELECT is true, then the row will be selected and, unless Shift
10150 is pressed, other rows will be unselected.
10152 If CLAMP_NODE is true, then the sheetview will scroll to make the row
10155 pspp_sheet_view_real_set_cursor (PsppSheetView
*tree_view
,
10157 gboolean clear_and_select
,
10158 gboolean clamp_node
,
10159 PsppSheetSelectMode mode
)
10163 if (gtk_tree_row_reference_valid (tree_view
->priv
->cursor
))
10165 GtkTreePath
*cursor_path
;
10166 cursor_path
= gtk_tree_row_reference_get_path (tree_view
->priv
->cursor
);
10167 pspp_sheet_view_queue_draw_path (tree_view
, cursor_path
, NULL
);
10168 gtk_tree_path_free (cursor_path
);
10171 gtk_tree_row_reference_free (tree_view
->priv
->cursor
);
10172 tree_view
->priv
->cursor
= NULL
;
10174 _pspp_sheet_view_find_node (tree_view
, path
, &node
);
10175 tree_view
->priv
->cursor
=
10176 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view
),
10177 tree_view
->priv
->model
,
10180 if (tree_view
->priv
->row_count
> 0)
10184 if (clear_and_select
&& !(mode
& PSPP_SHEET_SELECT_MODE_TOGGLE
))
10185 _pspp_sheet_selection_internal_select_node (tree_view
->priv
->selection
,
10189 /* We have to re-find tree and node here again, somebody might have
10190 * cleared the node or the whole tree in the PsppSheetSelection::changed
10191 * callback. If the nodes differ we bail out here.
10193 _pspp_sheet_view_find_node (tree_view
, path
, &new_node
);
10195 if (node
!= new_node
)
10200 pspp_sheet_view_clamp_node_visible (tree_view
, node
);
10201 _pspp_sheet_view_queue_draw_node (tree_view
, node
, NULL
);
10205 g_signal_emit (tree_view
, tree_view_signals
[CURSOR_CHANGED
], 0);
10209 * pspp_sheet_view_get_cursor:
10210 * @tree_view: A #PsppSheetView
10211 * @path: (allow-none): A pointer to be filled with the current cursor path, or %NULL
10212 * @focus_column: (allow-none): A pointer to be filled with the current focus column, or %NULL
10214 * Fills in @path and @focus_column with the current path and focus column. If
10215 * the cursor isn't currently set, then *@path will be %NULL. If no column
10216 * currently has focus, then *@focus_column will be %NULL.
10218 * The returned #GtkTreePath must be freed with gtk_tree_path_free() when
10219 * you are done with it.
10222 pspp_sheet_view_get_cursor (PsppSheetView
*tree_view
,
10223 GtkTreePath
**path
,
10224 PsppSheetViewColumn
**focus_column
)
10226 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
10230 if (gtk_tree_row_reference_valid (tree_view
->priv
->cursor
))
10231 *path
= gtk_tree_row_reference_get_path (tree_view
->priv
->cursor
);
10238 *focus_column
= tree_view
->priv
->focus_column
;
10243 * pspp_sheet_view_set_cursor:
10244 * @tree_view: A #PsppSheetView
10245 * @path: A #GtkTreePath
10246 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10247 * @start_editing: %TRUE if the specified cell should start being edited.
10249 * Sets the current keyboard focus to be at @path, and selects it. This is
10250 * useful when you want to focus the user's attention on a particular row. If
10251 * @focus_column is not %NULL, then focus is given to the column specified by
10252 * it. Additionally, if @focus_column is specified, and @start_editing is
10253 * %TRUE, then editing should be started in the specified cell.
10254 * This function is often followed by @gtk_widget_grab_focus (@tree_view)
10255 * in order to give keyboard focus to the widget. Please note that editing
10256 * can only happen when the widget is realized.
10258 * If @path is invalid for @model, the current cursor (if any) will be unset
10259 * and the function will return without failing.
10262 pspp_sheet_view_set_cursor (PsppSheetView
*tree_view
,
10264 PsppSheetViewColumn
*focus_column
,
10265 gboolean start_editing
)
10267 pspp_sheet_view_set_cursor_on_cell (tree_view
, path
, focus_column
,
10268 NULL
, start_editing
);
10272 * pspp_sheet_view_set_cursor_on_cell:
10273 * @tree_view: A #PsppSheetView
10274 * @path: A #GtkTreePath
10275 * @focus_column: (allow-none): A #PsppSheetViewColumn, or %NULL
10276 * @focus_cell: (allow-none): A #GtkCellRenderer, or %NULL
10277 * @start_editing: %TRUE if the specified cell should start being edited.
10279 * Sets the current keyboard focus to be at @path, and selects it. This is
10280 * useful when you want to focus the user's attention on a particular row. If
10281 * @focus_column is not %NULL, then focus is given to the column specified by
10282 * it. If @focus_column and @focus_cell are not %NULL, and @focus_column
10283 * contains 2 or more editable or activatable cells, then focus is given to
10284 * the cell specified by @focus_cell. Additionally, if @focus_column is
10285 * specified, and @start_editing is %TRUE, then editing should be started in
10286 * the specified cell. This function is often followed by
10287 * @gtk_widget_grab_focus (@tree_view) in order to give keyboard focus to the
10288 * widget. Please note that editing can only happen when the widget is
10291 * If @path is invalid for @model, the current cursor (if any) will be unset
10292 * and the function will return without failing.
10297 pspp_sheet_view_set_cursor_on_cell (PsppSheetView
*tree_view
,
10299 PsppSheetViewColumn
*focus_column
,
10300 GtkCellRenderer
*focus_cell
,
10301 gboolean start_editing
)
10303 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
10304 g_return_if_fail (path
!= NULL
);
10305 g_return_if_fail (focus_column
== NULL
|| PSPP_IS_SHEET_VIEW_COLUMN (focus_column
));
10307 if (!tree_view
->priv
->model
)
10312 g_return_if_fail (focus_column
);
10313 g_return_if_fail (GTK_IS_CELL_RENDERER (focus_cell
));
10316 /* cancel the current editing, if it exists */
10317 if (tree_view
->priv
->edited_column
&&
10318 tree_view
->priv
->edited_column
->editable_widget
)
10319 pspp_sheet_view_stop_editing (tree_view
, TRUE
);
10321 pspp_sheet_view_real_set_cursor (tree_view
, path
, TRUE
, TRUE
, 0);
10323 if (focus_column
&& focus_column
->visible
)
10326 gboolean column_in_tree
= FALSE
;
10328 for (list
= tree_view
->priv
->columns
; list
; list
= list
->next
)
10329 if (list
->data
== focus_column
)
10331 column_in_tree
= TRUE
;
10334 g_return_if_fail (column_in_tree
);
10335 tree_view
->priv
->focus_column
= focus_column
;
10337 pspp_sheet_view_column_focus_cell (focus_column
, focus_cell
);
10339 pspp_sheet_view_start_editing (tree_view
, path
);
10341 pspp_sheet_selection_unselect_all_columns (tree_view
->priv
->selection
);
10342 pspp_sheet_selection_select_column (tree_view
->priv
->selection
, focus_column
);
10348 * pspp_sheet_view_get_bin_window:
10349 * @tree_view: A #PsppSheetView
10351 * Returns the window that @tree_view renders to. This is used primarily to
10352 * compare to <literal>event->window</literal> to confirm that the event on
10353 * @tree_view is on the right window.
10355 * Return value: A #GdkWindow, or %NULL when @tree_view hasn't been realized yet
10358 pspp_sheet_view_get_bin_window (PsppSheetView
*tree_view
)
10360 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), NULL
);
10362 return tree_view
->priv
->bin_window
;
10366 * pspp_sheet_view_get_path_at_pos:
10367 * @tree_view: A #PsppSheetView.
10368 * @x: The x position to be identified (relative to bin_window).
10369 * @y: The y position to be identified (relative to bin_window).
10370 * @path: (out) (allow-none): A pointer to a #GtkTreePath pointer to be filled in, or %NULL
10371 * @column: (out) (allow-none): A pointer to a #PsppSheetViewColumn pointer to be filled in, or %NULL
10372 * @cell_x: (out) (allow-none): A pointer where the X coordinate relative to the cell can be placed, or %NULL
10373 * @cell_y: (out) (allow-none): A pointer where the Y coordinate relative to the cell can be placed, or %NULL
10375 * Finds the path at the point (@x, @y), relative to bin_window coordinates
10376 * (please see pspp_sheet_view_get_bin_window()).
10377 * That is, @x and @y are relative to an events coordinates. @x and @y must
10378 * come from an event on the @tree_view only where <literal>event->window ==
10379 * pspp_sheet_view_get_bin_window (<!-- -->)</literal>. It is primarily for
10380 * things like popup menus. If @path is non-%NULL, then it will be filled
10381 * with the #GtkTreePath at that point. This path should be freed with
10382 * gtk_tree_path_free(). If @column is non-%NULL, then it will be filled
10383 * with the column at that point. @cell_x and @cell_y return the coordinates
10384 * relative to the cell background (i.e. the @background_area passed to
10385 * gtk_cell_renderer_render()). This function is only meaningful if
10386 * @tree_view is realized. Therefore this function will always return %FALSE
10387 * if @tree_view is not realized or does not have a model.
10389 * For converting widget coordinates (eg. the ones you get from
10390 * GtkWidget::query-tooltip), please see
10391 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10393 * Return value: %TRUE if a row exists at that coordinate.
10396 pspp_sheet_view_get_path_at_pos (PsppSheetView
*tree_view
,
10399 GtkTreePath
**path
,
10400 PsppSheetViewColumn
**column
,
10407 g_return_val_if_fail (tree_view
!= NULL
, FALSE
);
10414 if (tree_view
->priv
->bin_window
== NULL
)
10417 if (tree_view
->priv
->row_count
== 0)
10420 if (x
> gtk_adjustment_get_upper (tree_view
->priv
->hadjustment
))
10423 if (x
< 0 || y
< 0)
10426 if (column
|| cell_x
)
10428 PsppSheetViewColumn
*tmp_column
;
10429 PsppSheetViewColumn
*last_column
= NULL
;
10431 gint remaining_x
= x
;
10432 gboolean found
= FALSE
;
10435 rtl
= (gtk_widget_get_direction (GTK_WIDGET (tree_view
)) == GTK_TEXT_DIR_RTL
);
10436 for (list
= (rtl
? g_list_last (tree_view
->priv
->columns
) : g_list_first (tree_view
->priv
->columns
));
10438 list
= (rtl
? list
->prev
: list
->next
))
10440 tmp_column
= list
->data
;
10442 if (tmp_column
->visible
== FALSE
)
10445 last_column
= tmp_column
;
10446 if (remaining_x
<= tmp_column
->width
)
10451 *column
= tmp_column
;
10454 *cell_x
= remaining_x
;
10458 remaining_x
-= tmp_column
->width
;
10461 /* If found is FALSE and there is a last_column, then it the remainder
10462 * space is in that area
10469 *column
= last_column
;
10472 *cell_x
= last_column
->width
+ remaining_x
;
10481 y_offset
= pspp_sheet_view_find_offset (tree_view
,
10482 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view
, y
),
10489 *cell_y
= y_offset
;
10492 *path
= _pspp_sheet_view_find_path (tree_view
, node
);
10497 /* Computes 'cell_area' from 'background_area', which must be the background
10498 area for a cell. Set 'subtract_focus_rect' to TRUE to compute the cell area
10499 as passed to a GtkCellRenderer's "render" function, or to FALSE to compute
10500 the cell area as passed to _pspp_sheet_view_column_cell_render().
10502 'column' is required to properly adjust 'cell_area->x' and
10503 'cell_area->width'. It may be set to NULL if these values are not of
10504 interest. In this case 'cell_area->x' and 'cell_area->width' will be
10507 pspp_sheet_view_adjust_cell_area (PsppSheetView
*tree_view
,
10508 PsppSheetViewColumn
*column
,
10509 const GdkRectangle
*background_area
,
10510 gboolean subtract_focus_rect
,
10511 GdkRectangle
*cell_area
)
10513 gint vertical_separator
;
10514 gint horizontal_separator
;
10516 *cell_area
= *background_area
;
10518 gtk_widget_style_get (GTK_WIDGET (tree_view
),
10519 "vertical-separator", &vertical_separator
,
10520 "horizontal-separator", &horizontal_separator
,
10522 cell_area
->x
+= horizontal_separator
/ 2;
10523 cell_area
->y
+= vertical_separator
/ 2;
10524 cell_area
->width
-= horizontal_separator
;
10525 cell_area
->height
-= vertical_separator
;
10527 if (subtract_focus_rect
)
10529 int focus_line_width
;
10531 gtk_widget_style_get (GTK_WIDGET (tree_view
),
10532 "focus-line-width", &focus_line_width
,
10534 cell_area
->x
+= focus_line_width
;
10535 cell_area
->y
+= focus_line_width
;
10536 cell_area
->width
-= 2 * focus_line_width
;
10537 cell_area
->height
-= 2 * focus_line_width
;
10540 if (tree_view
->priv
->grid_lines
!= PSPP_SHEET_VIEW_GRID_LINES_NONE
)
10542 gint grid_line_width
;
10543 gtk_widget_style_get (GTK_WIDGET (tree_view
),
10544 "grid-line-width", &grid_line_width
,
10547 if ((tree_view
->priv
->grid_lines
== PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
10548 || tree_view
->priv
->grid_lines
== PSPP_SHEET_VIEW_GRID_LINES_BOTH
)
10551 PsppSheetViewColumn
*first_column
, *last_column
;
10554 /* Find the last visible column. */
10555 last_column
= NULL
;
10556 for (list
= g_list_last (tree_view
->priv
->columns
);
10560 PsppSheetViewColumn
*c
= list
->data
;
10568 /* Find the first visible column. */
10569 first_column
= NULL
;
10570 for (list
= g_list_first (tree_view
->priv
->columns
);
10574 PsppSheetViewColumn
*c
= list
->data
;
10582 if (column
== first_column
)
10584 cell_area
->width
-= grid_line_width
/ 2;
10586 else if (column
== last_column
)
10588 cell_area
->x
+= grid_line_width
/ 2;
10589 cell_area
->width
-= grid_line_width
/ 2;
10593 cell_area
->x
+= grid_line_width
/ 2;
10594 cell_area
->width
-= grid_line_width
;
10598 if (tree_view
->priv
->grid_lines
== PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
10599 || tree_view
->priv
->grid_lines
== PSPP_SHEET_VIEW_GRID_LINES_BOTH
)
10601 cell_area
->y
+= grid_line_width
/ 2;
10602 cell_area
->height
-= grid_line_width
;
10606 if (column
== NULL
)
10609 cell_area
->width
= 0;
10614 * pspp_sheet_view_get_cell_area:
10615 * @tree_view: a #PsppSheetView
10616 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10617 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordinates
10618 * @rect: rectangle to fill with cell rect
10620 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10621 * row specified by @path and the column specified by @column. If @path is
10622 * %NULL, or points to a path not currently displayed, the @y and @height fields
10623 * of the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10624 * fields will be filled with 0. The sum of all cell rects does not cover the
10625 * entire tree; there are extra pixels in between rows, for example. The
10626 * returned rectangle is equivalent to the @cell_area passed to
10627 * gtk_cell_renderer_render(). This function is only valid if @tree_view is
10631 pspp_sheet_view_get_cell_area (PsppSheetView
*tree_view
,
10633 PsppSheetViewColumn
*column
,
10634 GdkRectangle
*rect
)
10636 GdkRectangle background_area
;
10638 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
10639 g_return_if_fail (column
== NULL
|| PSPP_IS_SHEET_VIEW_COLUMN (column
));
10640 g_return_if_fail (rect
!= NULL
);
10641 g_return_if_fail (!column
|| column
->tree_view
== (GtkWidget
*) tree_view
);
10642 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view
)));
10644 pspp_sheet_view_get_background_area (tree_view
, path
, column
,
10646 pspp_sheet_view_adjust_cell_area (tree_view
, column
, &background_area
,
10651 * pspp_sheet_view_get_background_area:
10652 * @tree_view: a #PsppSheetView
10653 * @path: (allow-none): a #GtkTreePath for the row, or %NULL to get only horizontal coordinates
10654 * @column: (allow-none): a #PsppSheetViewColumn for the column, or %NULL to get only vertical coordiantes
10655 * @rect: rectangle to fill with cell background rect
10657 * Fills the bounding rectangle in bin_window coordinates for the cell at the
10658 * row specified by @path and the column specified by @column. If @path is
10659 * %NULL, or points to a node not found in the tree, the @y and @height fields of
10660 * the rectangle will be filled with 0. If @column is %NULL, the @x and @width
10661 * fields will be filled with 0. The returned rectangle is equivalent to the
10662 * @background_area passed to gtk_cell_renderer_render(). These background
10663 * areas tile to cover the entire bin window. Contrast with the @cell_area,
10664 * returned by pspp_sheet_view_get_cell_area(), which returns only the cell
10665 * itself, excluding surrounding borders.
10669 pspp_sheet_view_get_background_area (PsppSheetView
*tree_view
,
10671 PsppSheetViewColumn
*column
,
10672 GdkRectangle
*rect
)
10676 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
10677 g_return_if_fail (column
== NULL
|| PSPP_IS_SHEET_VIEW_COLUMN (column
));
10678 g_return_if_fail (rect
!= NULL
);
10687 /* Get vertical coords */
10689 _pspp_sheet_view_find_node (tree_view
, path
, &node
);
10693 rect
->y
= BACKGROUND_FIRST_PIXEL (tree_view
, node
);
10695 rect
->height
= ROW_HEIGHT (tree_view
);
10702 pspp_sheet_view_get_background_xrange (tree_view
, column
, &rect
->x
, &x2
);
10703 rect
->width
= x2
- rect
->x
;
10708 * pspp_sheet_view_get_visible_rect:
10709 * @tree_view: a #PsppSheetView
10710 * @visible_rect: rectangle to fill
10712 * Fills @visible_rect with the currently-visible region of the
10713 * buffer, in tree coordinates. Convert to bin_window coordinates with
10714 * pspp_sheet_view_convert_tree_to_bin_window_coords().
10715 * Tree coordinates start at 0,0 for row 0 of the tree, and cover the entire
10716 * scrollable area of the tree.
10719 pspp_sheet_view_get_visible_rect (PsppSheetView
*tree_view
,
10720 GdkRectangle
*visible_rect
)
10724 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
10726 widget
= GTK_WIDGET (tree_view
);
10730 GtkAllocation allocation
;
10731 gtk_widget_get_allocation (widget
, &allocation
);
10732 visible_rect
->x
= gtk_adjustment_get_value (tree_view
->priv
->hadjustment
);
10733 visible_rect
->y
= gtk_adjustment_get_value (tree_view
->priv
->vadjustment
);
10734 visible_rect
->width
= allocation
.width
;
10735 visible_rect
->height
= allocation
.height
- TREE_VIEW_HEADER_HEIGHT (tree_view
);
10740 * pspp_sheet_view_widget_to_tree_coords:
10741 * @tree_view: a #PsppSheetView
10742 * @wx: X coordinate relative to bin_window
10743 * @wy: Y coordinate relative to bin_window
10744 * @tx: return location for tree X coordinate
10745 * @ty: return location for tree Y coordinate
10747 * Converts bin_window coordinates to coordinates for the
10748 * tree (the full scrollable area of the tree).
10750 * Deprecated: 2.12: Due to historial reasons the name of this function is
10751 * incorrect. For converting coordinates relative to the widget to
10752 * bin_window coordinates, please see
10753 * pspp_sheet_view_convert_widget_to_bin_window_coords().
10757 pspp_sheet_view_widget_to_tree_coords (PsppSheetView
*tree_view
,
10763 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
10766 *tx
= wx
+ gtk_adjustment_get_value (tree_view
->priv
->hadjustment
);
10768 *ty
= wy
+ tree_view
->priv
->dy
;
10772 * pspp_sheet_view_tree_to_widget_coords:
10773 * @tree_view: a #PsppSheetView
10774 * @tx: tree X coordinate
10775 * @ty: tree Y coordinate
10776 * @wx: return location for X coordinate relative to bin_window
10777 * @wy: return location for Y coordinate relative to bin_window
10779 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10780 * to bin_window coordinates.
10782 * Deprecated: 2.12: Due to historial reasons the name of this function is
10783 * incorrect. For converting bin_window coordinates to coordinates relative
10784 * to bin_window, please see
10785 * pspp_sheet_view_convert_bin_window_to_widget_coords().
10789 pspp_sheet_view_tree_to_widget_coords (PsppSheetView
*tree_view
,
10795 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
10798 *wx
= tx
- gtk_adjustment_get_value (tree_view
->priv
->hadjustment
);
10800 *wy
= ty
- tree_view
->priv
->dy
;
10805 * pspp_sheet_view_convert_widget_to_tree_coords:
10806 * @tree_view: a #PsppSheetView
10807 * @wx: X coordinate relative to the widget
10808 * @wy: Y coordinate relative to the widget
10809 * @tx: return location for tree X coordinate
10810 * @ty: return location for tree Y coordinate
10812 * Converts widget coordinates to coordinates for the
10813 * tree (the full scrollable area of the tree).
10818 pspp_sheet_view_convert_widget_to_tree_coords (PsppSheetView
*tree_view
,
10826 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
10828 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view
,
10831 pspp_sheet_view_convert_bin_window_to_tree_coords (tree_view
,
10837 * pspp_sheet_view_convert_tree_to_widget_coords:
10838 * @tree_view: a #PsppSheetView
10839 * @tx: X coordinate relative to the tree
10840 * @ty: Y coordinate relative to the tree
10841 * @wx: return location for widget X coordinate
10842 * @wy: return location for widget Y coordinate
10844 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10845 * to widget coordinates.
10850 pspp_sheet_view_convert_tree_to_widget_coords (PsppSheetView
*tree_view
,
10858 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
10860 pspp_sheet_view_convert_tree_to_bin_window_coords (tree_view
,
10863 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view
,
10869 * pspp_sheet_view_convert_widget_to_bin_window_coords:
10870 * @tree_view: a #PsppSheetView
10871 * @wx: X coordinate relative to the widget
10872 * @wy: Y coordinate relative to the widget
10873 * @bx: return location for bin_window X coordinate
10874 * @by: return location for bin_window Y coordinate
10876 * Converts widget coordinates to coordinates for the bin_window
10877 * (see pspp_sheet_view_get_bin_window()).
10882 pspp_sheet_view_convert_widget_to_bin_window_coords (PsppSheetView
*tree_view
,
10888 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
10891 *bx
= wx
+ gtk_adjustment_get_value (tree_view
->priv
->hadjustment
);
10893 *by
= wy
- TREE_VIEW_HEADER_HEIGHT (tree_view
);
10897 * pspp_sheet_view_convert_bin_window_to_widget_coords:
10898 * @tree_view: a #PsppSheetView
10899 * @bx: bin_window X coordinate
10900 * @by: bin_window Y coordinate
10901 * @wx: return location for widget X coordinate
10902 * @wy: return location for widget Y coordinate
10904 * Converts bin_window coordinates (see pspp_sheet_view_get_bin_window())
10905 * to widget relative coordinates.
10910 pspp_sheet_view_convert_bin_window_to_widget_coords (PsppSheetView
*tree_view
,
10916 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
10919 *wx
= bx
- gtk_adjustment_get_value (tree_view
->priv
->hadjustment
);
10921 *wy
= by
+ TREE_VIEW_HEADER_HEIGHT (tree_view
);
10925 * pspp_sheet_view_convert_tree_to_bin_window_coords:
10926 * @tree_view: a #PsppSheetView
10927 * @tx: tree X coordinate
10928 * @ty: tree Y coordinate
10929 * @bx: return location for X coordinate relative to bin_window
10930 * @by: return location for Y coordinate relative to bin_window
10932 * Converts tree coordinates (coordinates in full scrollable area of the tree)
10933 * to bin_window coordinates.
10938 pspp_sheet_view_convert_tree_to_bin_window_coords (PsppSheetView
*tree_view
,
10944 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
10949 *by
= ty
- tree_view
->priv
->dy
;
10953 * pspp_sheet_view_convert_bin_window_to_tree_coords:
10954 * @tree_view: a #PsppSheetView
10955 * @bx: X coordinate relative to bin_window
10956 * @by: Y coordinate relative to bin_window
10957 * @tx: return location for tree X coordinate
10958 * @ty: return location for tree Y coordinate
10960 * Converts bin_window coordinates to coordinates for the
10961 * tree (the full scrollable area of the tree).
10966 pspp_sheet_view_convert_bin_window_to_tree_coords (PsppSheetView
*tree_view
,
10972 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
10977 *ty
= by
+ tree_view
->priv
->dy
;
10983 * pspp_sheet_view_get_visible_range:
10984 * @tree_view: A #PsppSheetView
10985 * @start_path: (allow-none): Return location for start of region, or %NULL.
10986 * @end_path: (allow-none): Return location for end of region, or %NULL.
10988 * Sets @start_path and @end_path to be the first and last visible path.
10989 * Note that there may be invisible paths in between.
10991 * The paths should be freed with gtk_tree_path_free() after use.
10993 * Returns: %TRUE, if valid paths were placed in @start_path and @end_path.
10998 pspp_sheet_view_get_visible_range (PsppSheetView
*tree_view
,
10999 GtkTreePath
**start_path
,
11000 GtkTreePath
**end_path
)
11005 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), FALSE
);
11007 if (!tree_view
->priv
->row_count
)
11014 pspp_sheet_view_find_offset (tree_view
,
11015 TREE_WINDOW_Y_TO_RBTREE_Y (tree_view
, 0),
11018 *start_path
= _pspp_sheet_view_find_path (tree_view
, node
);
11027 if (tree_view
->priv
->height
< gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
))
11028 y
= tree_view
->priv
->height
- 1;
11030 y
= TREE_WINDOW_Y_TO_RBTREE_Y (tree_view
, gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
)) - 1;
11032 pspp_sheet_view_find_offset (tree_view
, y
, &node
);
11034 *end_path
= _pspp_sheet_view_find_path (tree_view
, node
);
11043 unset_reorderable (PsppSheetView
*tree_view
)
11045 if (tree_view
->priv
->reorderable
)
11047 tree_view
->priv
->reorderable
= FALSE
;
11048 g_object_notify (G_OBJECT (tree_view
), "reorderable");
11053 * pspp_sheet_view_enable_model_drag_source:
11054 * @tree_view: a #PsppSheetView
11055 * @start_button_mask: Mask of allowed buttons to start drag
11056 * @targets: the table of targets that the drag will support
11057 * @n_targets: the number of items in @targets
11058 * @actions: the bitmask of possible actions for a drag from this
11061 * Turns @tree_view into a drag source for automatic DND. Calling this
11062 * method sets #PsppSheetView:reorderable to %FALSE.
11065 pspp_sheet_view_enable_model_drag_source (PsppSheetView
*tree_view
,
11066 GdkModifierType start_button_mask
,
11067 const GtkTargetEntry
*targets
,
11069 GdkDragAction actions
)
11071 TreeViewDragInfo
*di
;
11073 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
11075 gtk_drag_source_set (GTK_WIDGET (tree_view
),
11081 di
= ensure_info (tree_view
);
11083 di
->start_button_mask
= start_button_mask
;
11084 di
->source_actions
= actions
;
11085 di
->source_set
= TRUE
;
11087 unset_reorderable (tree_view
);
11091 * pspp_sheet_view_enable_model_drag_dest:
11092 * @tree_view: a #PsppSheetView
11093 * @targets: the table of targets that the drag will support
11094 * @n_targets: the number of items in @targets
11095 * @actions: the bitmask of possible actions for a drag from this
11098 * Turns @tree_view into a drop destination for automatic DND. Calling
11099 * this method sets #PsppSheetView:reorderable to %FALSE.
11102 pspp_sheet_view_enable_model_drag_dest (PsppSheetView
*tree_view
,
11103 const GtkTargetEntry
*targets
,
11105 GdkDragAction actions
)
11107 TreeViewDragInfo
*di
;
11109 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
11111 gtk_drag_dest_set (GTK_WIDGET (tree_view
),
11117 di
= ensure_info (tree_view
);
11118 di
->dest_set
= TRUE
;
11120 unset_reorderable (tree_view
);
11124 * pspp_sheet_view_unset_rows_drag_source:
11125 * @tree_view: a #PsppSheetView
11127 * Undoes the effect of
11128 * pspp_sheet_view_enable_model_drag_source(). Calling this method sets
11129 * #PsppSheetView:reorderable to %FALSE.
11132 pspp_sheet_view_unset_rows_drag_source (PsppSheetView
*tree_view
)
11134 TreeViewDragInfo
*di
;
11136 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
11138 di
= get_info (tree_view
);
11142 if (di
->source_set
)
11144 gtk_drag_source_unset (GTK_WIDGET (tree_view
));
11145 di
->source_set
= FALSE
;
11148 if (!di
->dest_set
&& !di
->source_set
)
11149 remove_info (tree_view
);
11152 unset_reorderable (tree_view
);
11156 * pspp_sheet_view_unset_rows_drag_dest:
11157 * @tree_view: a #PsppSheetView
11159 * Undoes the effect of
11160 * pspp_sheet_view_enable_model_drag_dest(). Calling this method sets
11161 * #PsppSheetView:reorderable to %FALSE.
11164 pspp_sheet_view_unset_rows_drag_dest (PsppSheetView
*tree_view
)
11166 TreeViewDragInfo
*di
;
11168 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
11170 di
= get_info (tree_view
);
11176 gtk_drag_dest_unset (GTK_WIDGET (tree_view
));
11177 di
->dest_set
= FALSE
;
11180 if (!di
->dest_set
&& !di
->source_set
)
11181 remove_info (tree_view
);
11184 unset_reorderable (tree_view
);
11188 * pspp_sheet_view_set_drag_dest_row:
11189 * @tree_view: a #PsppSheetView
11190 * @path: (allow-none): The path of the row to highlight, or %NULL.
11191 * @pos: Specifies whether to drop before, after or into the row
11193 * Sets the row that is highlighted for feedback.
11196 pspp_sheet_view_set_drag_dest_row (PsppSheetView
*tree_view
,
11198 PsppSheetViewDropPosition pos
)
11200 GtkTreePath
*current_dest
;
11202 /* Note; this function is exported to allow a custom DND
11203 * implementation, so it can't touch TreeViewDragInfo
11206 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
11208 current_dest
= NULL
;
11210 if (tree_view
->priv
->drag_dest_row
)
11212 current_dest
= gtk_tree_row_reference_get_path (tree_view
->priv
->drag_dest_row
);
11213 gtk_tree_row_reference_free (tree_view
->priv
->drag_dest_row
);
11216 /* special case a drop on an empty model */
11217 tree_view
->priv
->empty_view_drop
= 0;
11219 if (pos
== PSPP_SHEET_VIEW_DROP_BEFORE
&& path
11220 && gtk_tree_path_get_depth (path
) == 1
11221 && gtk_tree_path_get_indices (path
)[0] == 0)
11225 n_children
= gtk_tree_model_iter_n_children (tree_view
->priv
->model
,
11229 tree_view
->priv
->empty_view_drop
= 1;
11232 tree_view
->priv
->drag_dest_pos
= pos
;
11236 tree_view
->priv
->drag_dest_row
=
11237 gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view
), tree_view
->priv
->model
, path
);
11238 pspp_sheet_view_queue_draw_path (tree_view
, path
, NULL
);
11241 tree_view
->priv
->drag_dest_row
= NULL
;
11245 int node
, new_node
;
11247 _pspp_sheet_view_find_node (tree_view
, current_dest
, &node
);
11248 _pspp_sheet_view_queue_draw_node (tree_view
, node
, NULL
);
11252 new_node
= pspp_sheet_view_node_next (tree_view
, node
);
11254 _pspp_sheet_view_queue_draw_node (tree_view
, new_node
, NULL
);
11256 new_node
= pspp_sheet_view_node_prev (tree_view
, node
);
11258 _pspp_sheet_view_queue_draw_node (tree_view
, new_node
, NULL
);
11260 gtk_tree_path_free (current_dest
);
11265 * pspp_sheet_view_get_drag_dest_row:
11266 * @tree_view: a #PsppSheetView
11267 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11268 * @pos: (allow-none): Return location for the drop position, or %NULL
11270 * Gets information about the row that is highlighted for feedback.
11273 pspp_sheet_view_get_drag_dest_row (PsppSheetView
*tree_view
,
11274 GtkTreePath
**path
,
11275 PsppSheetViewDropPosition
*pos
)
11277 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
11281 if (tree_view
->priv
->drag_dest_row
)
11282 *path
= gtk_tree_row_reference_get_path (tree_view
->priv
->drag_dest_row
);
11285 if (tree_view
->priv
->empty_view_drop
)
11286 *path
= gtk_tree_path_new_from_indices (0, -1);
11293 *pos
= tree_view
->priv
->drag_dest_pos
;
11297 * pspp_sheet_view_get_dest_row_at_pos:
11298 * @tree_view: a #PsppSheetView
11299 * @drag_x: the position to determine the destination row for
11300 * @drag_y: the position to determine the destination row for
11301 * @path: (allow-none): Return location for the path of the highlighted row, or %NULL.
11302 * @pos: (allow-none): Return location for the drop position, or %NULL
11304 * Determines the destination row for a given position. @drag_x and
11305 * @drag_y are expected to be in widget coordinates. This function is only
11306 * meaningful if @tree_view is realized. Therefore this function will always
11307 * return %FALSE if @tree_view is not realized or does not have a model.
11309 * Return value: whether there is a row at the given position, %TRUE if this
11310 * is indeed the case.
11313 pspp_sheet_view_get_dest_row_at_pos (PsppSheetView
*tree_view
,
11316 GtkTreePath
**path
,
11317 PsppSheetViewDropPosition
*pos
)
11321 gdouble offset_into_row
;
11324 PsppSheetViewColumn
*column
= NULL
;
11325 GtkTreePath
*tmp_path
= NULL
;
11327 /* Note; this function is exported to allow a custom DND
11328 * implementation, so it can't touch TreeViewDragInfo
11331 g_return_val_if_fail (tree_view
!= NULL
, FALSE
);
11332 g_return_val_if_fail (drag_x
>= 0, FALSE
);
11333 g_return_val_if_fail (drag_y
>= 0, FALSE
);
11338 if (tree_view
->priv
->bin_window
== NULL
)
11341 if (tree_view
->priv
->row_count
== 0)
11344 /* If in the top third of a row, we drop before that row; if
11345 * in the bottom third, drop after that row; if in the middle,
11346 * and the row has children, drop into the row.
11348 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view
, drag_x
, drag_y
,
11351 if (!pspp_sheet_view_get_path_at_pos (tree_view
,
11360 pspp_sheet_view_get_background_area (tree_view
, tmp_path
, column
,
11363 offset_into_row
= cell_y
;
11368 gtk_tree_path_free (tmp_path
);
11372 third
= cell
.height
/ 3.0;
11376 if (offset_into_row
< third
)
11378 *pos
= PSPP_SHEET_VIEW_DROP_BEFORE
;
11380 else if (offset_into_row
< (cell
.height
/ 2.0))
11382 *pos
= PSPP_SHEET_VIEW_DROP_INTO_OR_BEFORE
;
11384 else if (offset_into_row
< third
* 2.0)
11386 *pos
= PSPP_SHEET_VIEW_DROP_INTO_OR_AFTER
;
11390 *pos
= PSPP_SHEET_VIEW_DROP_AFTER
;
11398 #if GTK3_TRANSITION
11399 /* KEEP IN SYNC WITH PSPP_SHEET_VIEW_BIN_EXPOSE */
11401 * pspp_sheet_view_create_row_drag_icon:
11402 * @tree_view: a #PsppSheetView
11403 * @path: a #GtkTreePath in @tree_view
11405 * Creates a #GdkPixmap representation of the row at @path.
11406 * This image is used for a drag icon.
11408 * Return value: a newly-allocated pixmap of the drag icon.
11411 pspp_sheet_view_create_row_drag_icon (PsppSheetView
*tree_view
,
11418 GdkRectangle background_area
;
11419 GdkRectangle expose_area
;
11421 /* start drawing inside the black outline */
11423 GdkDrawable
*drawable
;
11424 gint bin_window_width
;
11427 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), NULL
);
11428 g_return_val_if_fail (path
!= NULL
, NULL
);
11430 widget
= GTK_WIDGET (tree_view
);
11432 if (!gtk_widget_get_realized (widget
))
11435 _pspp_sheet_view_find_node (tree_view
,
11442 if (!gtk_tree_model_get_iter (tree_view
->priv
->model
,
11449 background_area
.y
= y
;
11450 background_area
.height
= ROW_HEIGHT (tree_view
);
11452 bin_window_width
= gdk_window_get_width (tree_view
->priv
->bin_window
);
11454 drawable
= gdk_pixmap_new (tree_view
->priv
->bin_window
,
11455 bin_window_width
+ 2,
11456 background_area
.height
+ 2,
11461 expose_area
.width
= bin_window_width
+ 2;
11462 expose_area
.height
= background_area
.height
+ 2;
11464 #if GTK3_TRANSITION
11465 gdk_draw_rectangle (drawable
,
11466 widget
->style
->base_gc
[gtk_widget_get_state (widget
)],
11469 bin_window_width
+ 2,
11470 background_area
.height
+ 2);
11473 rtl
= gtk_widget_get_direction (GTK_WIDGET (tree_view
)) == GTK_TEXT_DIR_RTL
;
11475 for (list
= (rtl
? g_list_last (tree_view
->priv
->columns
) : g_list_first (tree_view
->priv
->columns
));
11477 list
= (rtl
? list
->prev
: list
->next
))
11479 PsppSheetViewColumn
*column
= list
->data
;
11480 GdkRectangle cell_area
;
11481 gint vertical_separator
;
11483 if (!column
->visible
)
11486 pspp_sheet_view_column_cell_set_cell_data (column
, tree_view
->priv
->model
, &iter
);
11488 background_area
.x
= cell_offset
;
11489 background_area
.width
= column
->width
;
11491 gtk_widget_style_get (widget
,
11492 "vertical-separator", &vertical_separator
,
11495 cell_area
= background_area
;
11497 cell_area
.y
+= vertical_separator
/ 2;
11498 cell_area
.height
-= vertical_separator
;
11500 if (pspp_sheet_view_column_cell_is_visible (column
))
11501 _pspp_sheet_view_column_cell_render (column
,
11507 cell_offset
+= column
->width
;
11510 #if GTK3_TRANSITION
11511 gdk_draw_rectangle (drawable
,
11512 widget
->style
->black_gc
,
11515 bin_window_width
+ 1,
11516 background_area
.height
+ 1);
11524 * pspp_sheet_view_set_destroy_count_func:
11525 * @tree_view: A #PsppSheetView
11526 * @func: (allow-none): Function to be called when a view row is destroyed, or %NULL
11527 * @data: (allow-none): User data to be passed to @func, or %NULL
11528 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11530 * This function should almost never be used. It is meant for private use by
11531 * ATK for determining the number of visible children that are removed when a row is deleted.
11534 pspp_sheet_view_set_destroy_count_func (PsppSheetView
*tree_view
,
11535 PsppSheetDestroyCountFunc func
,
11537 GDestroyNotify destroy
)
11539 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
11541 if (tree_view
->priv
->destroy_count_destroy
)
11542 tree_view
->priv
->destroy_count_destroy (tree_view
->priv
->destroy_count_data
);
11544 tree_view
->priv
->destroy_count_func
= func
;
11545 tree_view
->priv
->destroy_count_data
= data
;
11546 tree_view
->priv
->destroy_count_destroy
= destroy
;
11551 * Interactive search
11555 * pspp_sheet_view_set_enable_search:
11556 * @tree_view: A #PsppSheetView
11557 * @enable_search: %TRUE, if the user can search interactively
11559 * If @enable_search is set, then the user can type in text to search through
11560 * the tree interactively (this is sometimes called "typeahead find").
11562 * Note that even if this is %FALSE, the user can still initiate a search
11563 * using the "start-interactive-search" key binding.
11566 pspp_sheet_view_set_enable_search (PsppSheetView
*tree_view
,
11567 gboolean enable_search
)
11569 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
11571 enable_search
= !!enable_search
;
11573 if (tree_view
->priv
->enable_search
!= enable_search
)
11575 tree_view
->priv
->enable_search
= enable_search
;
11576 g_object_notify (G_OBJECT (tree_view
), "enable-search");
11581 * pspp_sheet_view_get_enable_search:
11582 * @tree_view: A #PsppSheetView
11584 * Returns whether or not the tree allows to start interactive searching
11585 * by typing in text.
11587 * Return value: whether or not to let the user search interactively
11590 pspp_sheet_view_get_enable_search (PsppSheetView
*tree_view
)
11592 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), FALSE
);
11594 return tree_view
->priv
->enable_search
;
11599 * pspp_sheet_view_get_search_column:
11600 * @tree_view: A #PsppSheetView
11602 * Gets the column searched on by the interactive search code.
11604 * Return value: the column the interactive search code searches in.
11607 pspp_sheet_view_get_search_column (PsppSheetView
*tree_view
)
11609 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), -1);
11611 return (tree_view
->priv
->search_column
);
11615 * pspp_sheet_view_set_search_column:
11616 * @tree_view: A #PsppSheetView
11617 * @column: the column of the model to search in, or -1 to disable searching
11619 * Sets @column as the column where the interactive search code should
11620 * search in for the current model.
11622 * If the search column is set, users can use the "start-interactive-search"
11623 * key binding to bring up search popup. The enable-search property controls
11624 * whether simply typing text will also start an interactive search.
11626 * Note that @column refers to a column of the current model. The search
11627 * column is reset to -1 when the model is changed.
11630 pspp_sheet_view_set_search_column (PsppSheetView
*tree_view
,
11633 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
11634 g_return_if_fail (column
>= -1);
11636 if (tree_view
->priv
->search_column
== column
)
11639 tree_view
->priv
->search_column
= column
;
11640 g_object_notify (G_OBJECT (tree_view
), "search-column");
11644 * pspp_sheet_view_get_search_equal_func:
11645 * @tree_view: A #PsppSheetView
11647 * Returns the compare function currently in use.
11649 * Return value: the currently used compare function for the search code.
11652 PsppSheetViewSearchEqualFunc
11653 pspp_sheet_view_get_search_equal_func (PsppSheetView
*tree_view
)
11655 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), NULL
);
11657 return tree_view
->priv
->search_equal_func
;
11661 * pspp_sheet_view_set_search_equal_func:
11662 * @tree_view: A #PsppSheetView
11663 * @search_equal_func: the compare function to use during the search
11664 * @search_user_data: (allow-none): user data to pass to @search_equal_func, or %NULL
11665 * @search_destroy: (allow-none): Destroy notifier for @search_user_data, or %NULL
11667 * Sets the compare function for the interactive search capabilities; note
11668 * that somewhat like strcmp() returning 0 for equality
11669 * #PsppSheetViewSearchEqualFunc returns %FALSE on matches.
11672 pspp_sheet_view_set_search_equal_func (PsppSheetView
*tree_view
,
11673 PsppSheetViewSearchEqualFunc search_equal_func
,
11674 gpointer search_user_data
,
11675 GDestroyNotify search_destroy
)
11677 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
11678 g_return_if_fail (search_equal_func
!= NULL
);
11680 if (tree_view
->priv
->search_destroy
)
11681 tree_view
->priv
->search_destroy (tree_view
->priv
->search_user_data
);
11683 tree_view
->priv
->search_equal_func
= search_equal_func
;
11684 tree_view
->priv
->search_user_data
= search_user_data
;
11685 tree_view
->priv
->search_destroy
= search_destroy
;
11686 if (tree_view
->priv
->search_equal_func
== NULL
)
11687 tree_view
->priv
->search_equal_func
= pspp_sheet_view_search_equal_func
;
11691 * pspp_sheet_view_get_search_entry:
11692 * @tree_view: A #PsppSheetView
11694 * Returns the #GtkEntry which is currently in use as interactive search
11695 * entry for @tree_view. In case the built-in entry is being used, %NULL
11696 * will be returned.
11698 * Return value: the entry currently in use as search entry.
11703 pspp_sheet_view_get_search_entry (PsppSheetView
*tree_view
)
11705 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), NULL
);
11707 if (tree_view
->priv
->search_custom_entry_set
)
11708 return GTK_ENTRY (tree_view
->priv
->search_entry
);
11714 * pspp_sheet_view_set_search_entry:
11715 * @tree_view: A #PsppSheetView
11716 * @entry: (allow-none): the entry the interactive search code of @tree_view should use or %NULL
11718 * Sets the entry which the interactive search code will use for this
11719 * @tree_view. This is useful when you want to provide a search entry
11720 * in our interface at all time at a fixed position. Passing %NULL for
11721 * @entry will make the interactive search code use the built-in popup
11727 pspp_sheet_view_set_search_entry (PsppSheetView
*tree_view
,
11730 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
11731 g_return_if_fail (entry
== NULL
|| GTK_IS_ENTRY (entry
));
11733 if (tree_view
->priv
->search_custom_entry_set
)
11735 if (tree_view
->priv
->search_entry_changed_id
)
11737 g_signal_handler_disconnect (tree_view
->priv
->search_entry
,
11738 tree_view
->priv
->search_entry_changed_id
);
11739 tree_view
->priv
->search_entry_changed_id
= 0;
11741 g_signal_handlers_disconnect_by_func (tree_view
->priv
->search_entry
,
11742 G_CALLBACK (pspp_sheet_view_search_key_press_event
),
11745 g_object_unref (tree_view
->priv
->search_entry
);
11747 else if (tree_view
->priv
->search_window
)
11749 gtk_widget_destroy (tree_view
->priv
->search_window
);
11751 tree_view
->priv
->search_window
= NULL
;
11756 tree_view
->priv
->search_entry
= g_object_ref (entry
);
11757 tree_view
->priv
->search_custom_entry_set
= TRUE
;
11759 if (tree_view
->priv
->search_entry_changed_id
== 0)
11761 tree_view
->priv
->search_entry_changed_id
=
11762 g_signal_connect (tree_view
->priv
->search_entry
, "changed",
11763 G_CALLBACK (pspp_sheet_view_search_init
),
11767 g_signal_connect (tree_view
->priv
->search_entry
, "key-press-event",
11768 G_CALLBACK (pspp_sheet_view_search_key_press_event
),
11771 pspp_sheet_view_search_init (tree_view
->priv
->search_entry
, tree_view
);
11775 tree_view
->priv
->search_entry
= NULL
;
11776 tree_view
->priv
->search_custom_entry_set
= FALSE
;
11781 * pspp_sheet_view_set_search_position_func:
11782 * @tree_view: A #PsppSheetView
11783 * @func: (allow-none): the function to use to position the search dialog, or %NULL
11784 * to use the default search position function
11785 * @data: (allow-none): user data to pass to @func, or %NULL
11786 * @destroy: (allow-none): Destroy notifier for @data, or %NULL
11788 * Sets the function to use when positioning the search dialog.
11793 pspp_sheet_view_set_search_position_func (PsppSheetView
*tree_view
,
11794 PsppSheetViewSearchPositionFunc func
,
11795 gpointer user_data
,
11796 GDestroyNotify destroy
)
11798 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
11800 if (tree_view
->priv
->search_position_destroy
)
11801 tree_view
->priv
->search_position_destroy (tree_view
->priv
->search_position_user_data
);
11803 tree_view
->priv
->search_position_func
= func
;
11804 tree_view
->priv
->search_position_user_data
= user_data
;
11805 tree_view
->priv
->search_position_destroy
= destroy
;
11806 if (tree_view
->priv
->search_position_func
== NULL
)
11807 tree_view
->priv
->search_position_func
= pspp_sheet_view_search_position_func
;
11811 * pspp_sheet_view_get_search_position_func:
11812 * @tree_view: A #PsppSheetView
11814 * Returns the positioning function currently in use.
11816 * Return value: the currently used function for positioning the search dialog.
11820 PsppSheetViewSearchPositionFunc
11821 pspp_sheet_view_get_search_position_func (PsppSheetView
*tree_view
)
11823 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), NULL
);
11825 return tree_view
->priv
->search_position_func
;
11830 pspp_sheet_view_search_dialog_hide (GtkWidget
*search_dialog
,
11831 PsppSheetView
*tree_view
)
11833 if (tree_view
->priv
->disable_popdown
)
11836 if (tree_view
->priv
->search_entry_changed_id
)
11838 g_signal_handler_disconnect (tree_view
->priv
->search_entry
,
11839 tree_view
->priv
->search_entry_changed_id
);
11840 tree_view
->priv
->search_entry_changed_id
= 0;
11842 if (tree_view
->priv
->typeselect_flush_timeout
)
11844 g_source_remove (tree_view
->priv
->typeselect_flush_timeout
);
11845 tree_view
->priv
->typeselect_flush_timeout
= 0;
11848 if (gtk_widget_get_visible (search_dialog
))
11850 /* send focus-in event */
11851 send_focus_change (GTK_WIDGET (tree_view
->priv
->search_entry
), FALSE
);
11852 gtk_widget_hide (search_dialog
);
11853 gtk_entry_set_text (GTK_ENTRY (tree_view
->priv
->search_entry
), "");
11854 send_focus_change (GTK_WIDGET (tree_view
), TRUE
);
11859 pspp_sheet_view_search_position_func (PsppSheetView
*tree_view
,
11860 GtkWidget
*search_dialog
,
11861 gpointer user_data
)
11864 gint tree_x
, tree_y
;
11865 gint tree_width
, tree_height
;
11866 GdkWindow
*tree_window
= gtk_widget_get_window (GTK_WIDGET (tree_view
));
11867 GdkScreen
*screen
= gdk_window_get_screen (tree_window
);
11868 GtkRequisition requisition
;
11870 GdkRectangle monitor
;
11872 monitor_num
= gdk_screen_get_monitor_at_window (screen
, tree_window
);
11873 gdk_screen_get_monitor_geometry (screen
, monitor_num
, &monitor
);
11875 gtk_widget_realize (search_dialog
);
11877 gdk_window_get_origin (tree_window
, &tree_x
, &tree_y
);
11878 tree_width
= gdk_window_get_width (tree_window
);
11879 tree_height
= gdk_window_get_height (tree_window
);
11881 gtk_widget_size_request (search_dialog
, &requisition
);
11883 if (tree_x
+ tree_width
> gdk_screen_get_width (screen
))
11884 x
= gdk_screen_get_width (screen
) - requisition
.width
;
11885 else if (tree_x
+ tree_width
- requisition
.width
< 0)
11888 x
= tree_x
+ tree_width
- requisition
.width
;
11890 if (tree_y
+ tree_height
+ requisition
.height
> gdk_screen_get_height (screen
))
11891 y
= gdk_screen_get_height (screen
) - requisition
.height
;
11892 else if (tree_y
+ tree_height
< 0) /* isn't really possible ... */
11895 y
= tree_y
+ tree_height
;
11897 gtk_window_move (GTK_WINDOW (search_dialog
), x
, y
);
11901 pspp_sheet_view_search_disable_popdown (GtkEntry
*entry
,
11905 PsppSheetView
*tree_view
= (PsppSheetView
*)data
;
11907 tree_view
->priv
->disable_popdown
= 1;
11908 g_signal_connect (menu
, "hide",
11909 G_CALLBACK (pspp_sheet_view_search_enable_popdown
), data
);
11912 #if GTK3_TRANSITION
11913 /* Because we're visible but offscreen, we just set a flag in the preedit
11917 pspp_sheet_view_search_preedit_changed (GtkIMContext
*im_context
,
11918 PsppSheetView
*tree_view
)
11920 tree_view
->priv
->imcontext_changed
= 1;
11921 if (tree_view
->priv
->typeselect_flush_timeout
)
11923 g_source_remove (tree_view
->priv
->typeselect_flush_timeout
);
11924 tree_view
->priv
->typeselect_flush_timeout
=
11925 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT
,
11926 (GSourceFunc
) pspp_sheet_view_search_entry_flush_timeout
,
11934 pspp_sheet_view_search_activate (GtkEntry
*entry
,
11935 PsppSheetView
*tree_view
)
11940 pspp_sheet_view_search_dialog_hide (tree_view
->priv
->search_window
,
11943 /* If we have a row selected and it's the cursor row, we activate
11945 if (gtk_tree_row_reference_valid (tree_view
->priv
->cursor
))
11947 path
= gtk_tree_row_reference_get_path (tree_view
->priv
->cursor
);
11949 _pspp_sheet_view_find_node (tree_view
, path
, &node
);
11951 if (node
>= 0 && pspp_sheet_view_node_is_selected (tree_view
, node
))
11952 pspp_sheet_view_row_activated (tree_view
, path
, tree_view
->priv
->focus_column
);
11954 gtk_tree_path_free (path
);
11959 pspp_sheet_view_real_search_enable_popdown (gpointer data
)
11961 PsppSheetView
*tree_view
= (PsppSheetView
*)data
;
11963 tree_view
->priv
->disable_popdown
= 0;
11969 pspp_sheet_view_search_enable_popdown (GtkWidget
*widget
,
11972 gdk_threads_add_timeout_full (G_PRIORITY_HIGH
, 200, pspp_sheet_view_real_search_enable_popdown
, g_object_ref (data
), g_object_unref
);
11976 pspp_sheet_view_search_delete_event (GtkWidget
*widget
,
11977 GdkEventAny
*event
,
11978 PsppSheetView
*tree_view
)
11980 g_return_val_if_fail (GTK_IS_WIDGET (widget
), FALSE
);
11982 pspp_sheet_view_search_dialog_hide (widget
, tree_view
);
11988 pspp_sheet_view_search_button_press_event (GtkWidget
*widget
,
11989 GdkEventButton
*event
,
11990 PsppSheetView
*tree_view
)
11992 g_return_val_if_fail (GTK_IS_WIDGET (widget
), FALSE
);
11994 pspp_sheet_view_search_dialog_hide (widget
, tree_view
);
11996 if (event
->window
== tree_view
->priv
->bin_window
)
11997 pspp_sheet_view_button_press (GTK_WIDGET (tree_view
), event
);
12003 pspp_sheet_view_search_scroll_event (GtkWidget
*widget
,
12004 GdkEventScroll
*event
,
12005 PsppSheetView
*tree_view
)
12007 gboolean retval
= FALSE
;
12009 if (event
->direction
== GDK_SCROLL_UP
)
12011 pspp_sheet_view_search_move (widget
, tree_view
, TRUE
);
12014 else if (event
->direction
== GDK_SCROLL_DOWN
)
12016 pspp_sheet_view_search_move (widget
, tree_view
, FALSE
);
12020 /* renew the flush timeout */
12021 if (retval
&& tree_view
->priv
->typeselect_flush_timeout
12022 && !tree_view
->priv
->search_custom_entry_set
)
12024 g_source_remove (tree_view
->priv
->typeselect_flush_timeout
);
12025 tree_view
->priv
->typeselect_flush_timeout
=
12026 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT
,
12027 (GSourceFunc
) pspp_sheet_view_search_entry_flush_timeout
,
12035 pspp_sheet_view_search_key_press_event (GtkWidget
*widget
,
12036 GdkEventKey
*event
,
12037 PsppSheetView
*tree_view
)
12039 gboolean retval
= FALSE
;
12041 g_return_val_if_fail (GTK_IS_WIDGET (widget
), FALSE
);
12042 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), FALSE
);
12044 /* close window and cancel the search */
12045 if (!tree_view
->priv
->search_custom_entry_set
12046 && (event
->keyval
== GDK_Escape
||
12047 event
->keyval
== GDK_Tab
||
12048 event
->keyval
== GDK_KP_Tab
||
12049 event
->keyval
== GDK_ISO_Left_Tab
))
12051 pspp_sheet_view_search_dialog_hide (widget
, tree_view
);
12055 /* select previous matching iter */
12056 if (event
->keyval
== GDK_Up
|| event
->keyval
== GDK_KP_Up
)
12058 if (!pspp_sheet_view_search_move (widget
, tree_view
, TRUE
))
12059 gtk_widget_error_bell (widget
);
12064 if (((event
->state
& (GTK_DEFAULT_ACCEL_MOD_MASK
| GDK_SHIFT_MASK
)) == (GTK_DEFAULT_ACCEL_MOD_MASK
| GDK_SHIFT_MASK
))
12065 && (event
->keyval
== GDK_g
|| event
->keyval
== GDK_G
))
12067 if (!pspp_sheet_view_search_move (widget
, tree_view
, TRUE
))
12068 gtk_widget_error_bell (widget
);
12073 /* select next matching iter */
12074 if (event
->keyval
== GDK_Down
|| event
->keyval
== GDK_KP_Down
)
12076 if (!pspp_sheet_view_search_move (widget
, tree_view
, FALSE
))
12077 gtk_widget_error_bell (widget
);
12082 if (((event
->state
& (GTK_DEFAULT_ACCEL_MOD_MASK
| GDK_SHIFT_MASK
)) == GTK_DEFAULT_ACCEL_MOD_MASK
)
12083 && (event
->keyval
== GDK_g
|| event
->keyval
== GDK_G
))
12085 if (!pspp_sheet_view_search_move (widget
, tree_view
, FALSE
))
12086 gtk_widget_error_bell (widget
);
12091 /* renew the flush timeout */
12092 if (retval
&& tree_view
->priv
->typeselect_flush_timeout
12093 && !tree_view
->priv
->search_custom_entry_set
)
12095 g_source_remove (tree_view
->priv
->typeselect_flush_timeout
);
12096 tree_view
->priv
->typeselect_flush_timeout
=
12097 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT
,
12098 (GSourceFunc
) pspp_sheet_view_search_entry_flush_timeout
,
12105 /* this function returns FALSE if there is a search string but
12106 * nothing was found, and TRUE otherwise.
12109 pspp_sheet_view_search_move (GtkWidget
*window
,
12110 PsppSheetView
*tree_view
,
12118 GtkTreeModel
*model
;
12119 PsppSheetSelection
*selection
;
12121 text
= gtk_entry_get_text (GTK_ENTRY (tree_view
->priv
->search_entry
));
12123 g_return_val_if_fail (text
!= NULL
, FALSE
);
12125 len
= strlen (text
);
12127 if (up
&& tree_view
->priv
->selected_iter
== 1)
12128 return strlen (text
) < 1;
12130 len
= strlen (text
);
12135 model
= pspp_sheet_view_get_model (tree_view
);
12136 selection
= pspp_sheet_view_get_selection (tree_view
);
12139 pspp_sheet_selection_unselect_all (selection
);
12140 if (!gtk_tree_model_get_iter_first (model
, &iter
))
12143 ret
= pspp_sheet_view_search_iter (model
, selection
, &iter
, text
,
12144 &count
, up
?((tree_view
->priv
->selected_iter
) - 1):((tree_view
->priv
->selected_iter
+ 1)));
12149 tree_view
->priv
->selected_iter
+= up
?(-1):(1);
12154 /* return to old iter */
12156 gtk_tree_model_get_iter_first (model
, &iter
);
12157 pspp_sheet_view_search_iter (model
, selection
,
12159 &count
, tree_view
->priv
->selected_iter
);
12165 pspp_sheet_view_search_equal_func (GtkTreeModel
*model
,
12169 gpointer search_data
)
12171 gboolean retval
= TRUE
;
12173 gchar
*normalized_string
;
12174 gchar
*normalized_key
;
12175 gchar
*case_normalized_string
= NULL
;
12176 gchar
*case_normalized_key
= NULL
;
12177 GValue value
= {0,};
12178 GValue transformed
= {0,};
12180 gtk_tree_model_get_value (model
, iter
, column
, &value
);
12182 g_value_init (&transformed
, G_TYPE_STRING
);
12184 if (!g_value_transform (&value
, &transformed
))
12186 g_value_unset (&value
);
12190 g_value_unset (&value
);
12192 str
= g_value_get_string (&transformed
);
12195 g_value_unset (&transformed
);
12199 normalized_string
= g_utf8_normalize (str
, -1, G_NORMALIZE_ALL
);
12200 normalized_key
= g_utf8_normalize (key
, -1, G_NORMALIZE_ALL
);
12202 if (normalized_string
&& normalized_key
)
12204 case_normalized_string
= g_utf8_casefold (normalized_string
, -1);
12205 case_normalized_key
= g_utf8_casefold (normalized_key
, -1);
12207 if (strncmp (case_normalized_key
, case_normalized_string
, strlen (case_normalized_key
)) == 0)
12211 g_value_unset (&transformed
);
12212 g_free (normalized_key
);
12213 g_free (normalized_string
);
12214 g_free (case_normalized_key
);
12215 g_free (case_normalized_string
);
12221 pspp_sheet_view_search_iter (GtkTreeModel
*model
,
12222 PsppSheetSelection
*selection
,
12231 PsppSheetView
*tree_view
= pspp_sheet_selection_get_tree_view (selection
);
12233 path
= gtk_tree_model_get_path (model
, iter
);
12234 _pspp_sheet_view_find_node (tree_view
, path
, &node
);
12238 gboolean done
= FALSE
;
12240 if (! tree_view
->priv
->search_equal_func (model
, tree_view
->priv
->search_column
, text
, iter
, tree_view
->priv
->search_user_data
))
12245 pspp_sheet_view_scroll_to_cell (tree_view
, path
, NULL
,
12247 pspp_sheet_selection_select_iter (selection
, iter
);
12248 pspp_sheet_view_real_set_cursor (tree_view
, path
, FALSE
, TRUE
, 0);
12251 gtk_tree_path_free (path
);
12260 node
= pspp_sheet_view_node_next (tree_view
, node
);
12266 has_next
= gtk_tree_model_iter_next (model
, iter
);
12269 gtk_tree_path_next (path
);
12272 TREE_VIEW_INTERNAL_ASSERT (has_next
, FALSE
);
12277 gtk_tree_path_free (path
);
12279 /* we've run out of tree, done with this func */
12291 pspp_sheet_view_search_init (GtkWidget
*entry
,
12292 PsppSheetView
*tree_view
)
12298 GtkTreeModel
*model
;
12299 PsppSheetSelection
*selection
;
12301 g_return_if_fail (GTK_IS_ENTRY (entry
));
12302 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
12304 text
= gtk_entry_get_text (GTK_ENTRY (entry
));
12306 model
= pspp_sheet_view_get_model (tree_view
);
12307 selection
= pspp_sheet_view_get_selection (tree_view
);
12310 pspp_sheet_selection_unselect_all (selection
);
12311 if (tree_view
->priv
->typeselect_flush_timeout
12312 && !tree_view
->priv
->search_custom_entry_set
)
12314 g_source_remove (tree_view
->priv
->typeselect_flush_timeout
);
12315 tree_view
->priv
->typeselect_flush_timeout
=
12316 gdk_threads_add_timeout (PSPP_SHEET_VIEW_SEARCH_DIALOG_TIMEOUT
,
12317 (GSourceFunc
) pspp_sheet_view_search_entry_flush_timeout
,
12324 if (!gtk_tree_model_get_iter_first (model
, &iter
))
12327 ret
= pspp_sheet_view_search_iter (model
, selection
,
12332 tree_view
->priv
->selected_iter
= 1;
12336 pspp_sheet_view_remove_widget (GtkCellEditable
*cell_editable
,
12337 PsppSheetView
*tree_view
)
12339 if (tree_view
->priv
->edited_column
== NULL
)
12342 _pspp_sheet_view_column_stop_editing (tree_view
->priv
->edited_column
);
12343 tree_view
->priv
->edited_column
= NULL
;
12345 if (gtk_widget_has_focus (GTK_WIDGET (cell_editable
)))
12346 gtk_widget_grab_focus (GTK_WIDGET (tree_view
));
12348 g_signal_handlers_disconnect_by_func (cell_editable
,
12349 pspp_sheet_view_remove_widget
,
12351 g_signal_handlers_disconnect_by_func (cell_editable
,
12352 pspp_sheet_view_editable_button_press_event
,
12354 g_signal_handlers_disconnect_by_func (cell_editable
,
12355 pspp_sheet_view_editable_clicked
,
12358 gtk_container_remove (GTK_CONTAINER (tree_view
),
12359 GTK_WIDGET (cell_editable
));
12361 /* FIXME should only redraw a single node */
12362 gtk_widget_queue_draw (GTK_WIDGET (tree_view
));
12366 pspp_sheet_view_start_editing (PsppSheetView
*tree_view
,
12367 GtkTreePath
*cursor_path
)
12370 GdkRectangle background_area
;
12371 GdkRectangle cell_area
;
12372 GtkCellEditable
*editable_widget
= NULL
;
12373 gchar
*path_string
;
12374 guint flags
= 0; /* can be 0, as the flags are primarily for rendering */
12375 gint retval
= FALSE
;
12378 g_assert (tree_view
->priv
->focus_column
);
12380 if (!gtk_widget_get_realized (GTK_WIDGET (tree_view
)))
12383 _pspp_sheet_view_find_node (tree_view
, cursor_path
, &cursor_node
);
12384 if (cursor_node
< 0)
12387 path_string
= gtk_tree_path_to_string (cursor_path
);
12388 gtk_tree_model_get_iter (tree_view
->priv
->model
, &iter
, cursor_path
);
12390 pspp_sheet_view_column_cell_set_cell_data (tree_view
->priv
->focus_column
,
12391 tree_view
->priv
->model
,
12393 pspp_sheet_view_get_background_area (tree_view
,
12395 tree_view
->priv
->focus_column
,
12397 pspp_sheet_view_get_cell_area (tree_view
,
12399 tree_view
->priv
->focus_column
,
12402 if (_pspp_sheet_view_column_cell_event (tree_view
->priv
->focus_column
,
12411 if (editable_widget
!= NULL
)
12415 GtkCellRenderer
*cell
;
12418 cell
= _pspp_sheet_view_column_get_edited_cell (tree_view
->priv
->focus_column
);
12420 _pspp_sheet_view_column_get_neighbor_sizes (tree_view
->priv
->focus_column
, cell
, &left
, &right
);
12423 area
.width
-= right
+ left
;
12425 pspp_sheet_view_real_start_editing (tree_view
,
12426 tree_view
->priv
->focus_column
,
12435 g_free (path_string
);
12440 pspp_sheet_view_editable_button_press_event (GtkWidget
*widget
,
12441 GdkEventButton
*event
,
12442 PsppSheetView
*sheet_view
)
12446 node
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget
),
12447 "pspp-sheet-view-node"));
12448 return pspp_sheet_view_row_head_clicked (sheet_view
,
12450 sheet_view
->priv
->edited_column
,
12455 pspp_sheet_view_editable_clicked (GtkButton
*button
,
12456 PsppSheetView
*sheet_view
)
12458 pspp_sheet_view_editable_button_press_event (GTK_WIDGET (button
), NULL
,
12463 is_all_selected (GtkWidget
*widget
)
12465 GtkEntryBuffer
*buffer
;
12466 gint start_pos
, end_pos
;
12468 if (!GTK_IS_ENTRY (widget
))
12471 buffer
= gtk_entry_get_buffer (GTK_ENTRY (widget
));
12472 return (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget
),
12473 &start_pos
, &end_pos
)
12475 && end_pos
== gtk_entry_buffer_get_length (buffer
));
12479 is_at_left (GtkWidget
*widget
)
12481 return (GTK_IS_ENTRY (widget
)
12482 && gtk_editable_get_position (GTK_EDITABLE (widget
)) == 0);
12486 is_at_right (GtkWidget
*widget
)
12488 GtkEntryBuffer
*buffer
;
12491 if (!GTK_IS_ENTRY (widget
))
12494 buffer
= gtk_entry_get_buffer (GTK_ENTRY (widget
));
12495 length
= gtk_entry_buffer_get_length (buffer
);
12496 return gtk_editable_get_position (GTK_EDITABLE (widget
)) == length
;
12500 pspp_sheet_view_event (GtkWidget
*widget
,
12501 GdkEventKey
*event
,
12502 PsppSheetView
*tree_view
)
12504 PsppSheetViewColumn
*column
;
12511 /* Intercept only key press events.
12512 It would make sense to use "key-press-event" instead of "event", but
12513 GtkEntry attaches its own signal handler to "key-press-event" that runs
12514 before ours and overrides our desired behavior for GDK_Up and GDK_Down.
12516 if (event
->type
!= GDK_KEY_PRESS
)
12519 keyval
= event
->keyval
;
12521 switch (event
->state
& (GDK_CONTROL_MASK
| GDK_SHIFT_MASK
| GDK_MOD1_MASK
))
12524 switch (event
->keyval
)
12526 case GDK_Left
: case GDK_KP_Left
:
12527 case GDK_Home
: case GDK_KP_Home
:
12528 if (!is_all_selected (widget
) && !is_at_left (widget
))
12532 case GDK_Right
: case GDK_KP_Right
:
12533 case GDK_End
: case GDK_KP_End
:
12534 if (!is_all_selected (widget
) && !is_at_right (widget
))
12538 case GDK_Up
: case GDK_KP_Up
:
12539 case GDK_Down
: case GDK_KP_Down
:
12542 case GDK_Page_Up
: case GDK_KP_Page_Up
:
12543 case GDK_Page_Down
: case GDK_KP_Page_Down
:
12554 case GDK_Tab
: case GDK_KP_Tab
:
12555 case GDK_ISO_Left_Tab
:
12564 case GDK_SHIFT_MASK
:
12565 switch (event
->keyval
)
12568 case GDK_ISO_Left_Tab
:
12577 case GDK_CONTROL_MASK
:
12578 switch (event
->keyval
)
12580 case GDK_Left
: case GDK_KP_Left
:
12581 if (!is_all_selected (widget
) && !is_at_left (widget
))
12585 case GDK_Right
: case GDK_KP_Right
:
12586 if (!is_all_selected (widget
) && !is_at_right (widget
))
12590 case GDK_Up
: case GDK_KP_Up
:
12591 case GDK_Down
: case GDK_KP_Down
:
12603 row
= tree_view
->priv
->edited_row
;
12604 column
= tree_view
->priv
->edited_column
;
12605 path
= gtk_tree_path_new_from_indices (row
, -1);
12607 pspp_sheet_view_stop_editing (tree_view
, cancel
);
12608 gtk_widget_grab_focus (GTK_WIDGET (tree_view
));
12610 pspp_sheet_view_set_cursor (tree_view
, path
, column
, FALSE
);
12611 gtk_tree_path_free (path
);
12613 handled
= gtk_binding_set_activate (edit_bindings
, keyval
, event
->state
,
12614 G_OBJECT (tree_view
));
12616 g_signal_stop_emission_by_name (widget
, "event");
12618 pspp_sheet_view_get_cursor (tree_view
, &path
, NULL
);
12619 pspp_sheet_view_start_editing (tree_view
, path
);
12620 gtk_tree_path_free (path
);
12626 pspp_sheet_view_override_cell_keypresses (GtkWidget
*widget
,
12629 PsppSheetView
*sheet_view
= data
;
12631 g_signal_connect (widget
, "event",
12632 G_CALLBACK (pspp_sheet_view_event
),
12635 if (GTK_IS_CONTAINER (widget
))
12636 gtk_container_foreach (GTK_CONTAINER (widget
),
12637 pspp_sheet_view_override_cell_keypresses
,
12642 pspp_sheet_view_real_start_editing (PsppSheetView
*tree_view
,
12643 PsppSheetViewColumn
*column
,
12645 GtkCellEditable
*cell_editable
,
12646 GdkRectangle
*cell_area
,
12650 PsppSheetSelectionMode mode
= pspp_sheet_selection_get_mode (tree_view
->priv
->selection
);
12651 gint pre_val
= gtk_adjustment_get_value (tree_view
->priv
->vadjustment
);
12652 GtkRequisition requisition
;
12655 g_return_if_fail (gtk_tree_path_get_depth (path
) == 1);
12657 tree_view
->priv
->edited_column
= column
;
12658 _pspp_sheet_view_column_start_editing (column
, GTK_CELL_EDITABLE (cell_editable
));
12660 row
= gtk_tree_path_get_indices (path
)[0];
12661 tree_view
->priv
->edited_row
= row
;
12662 pspp_sheet_view_real_set_cursor (tree_view
, path
, FALSE
, TRUE
, 0);
12663 cell_area
->y
+= pre_val
- (int)gtk_adjustment_get_value (tree_view
->priv
->vadjustment
);
12665 pspp_sheet_selection_unselect_all_columns (tree_view
->priv
->selection
);
12666 pspp_sheet_selection_select_column (tree_view
->priv
->selection
, column
);
12667 tree_view
->priv
->anchor_column
= column
;
12669 gtk_widget_size_request (GTK_WIDGET (cell_editable
), &requisition
);
12671 PSPP_SHEET_VIEW_SET_FLAG (tree_view
, PSPP_SHEET_VIEW_DRAW_KEYFOCUS
);
12673 if (requisition
.height
< cell_area
->height
)
12675 gint diff
= cell_area
->height
- requisition
.height
;
12676 pspp_sheet_view_put (tree_view
,
12677 GTK_WIDGET (cell_editable
),
12678 cell_area
->x
, cell_area
->y
+ diff
/2,
12679 cell_area
->width
, requisition
.height
);
12683 pspp_sheet_view_put (tree_view
,
12684 GTK_WIDGET (cell_editable
),
12685 cell_area
->x
, cell_area
->y
,
12686 cell_area
->width
, cell_area
->height
);
12689 gtk_cell_editable_start_editing (GTK_CELL_EDITABLE (cell_editable
),
12690 (GdkEvent
*)event
);
12692 gtk_widget_grab_focus (GTK_WIDGET (cell_editable
));
12693 g_signal_connect (cell_editable
, "remove-widget",
12694 G_CALLBACK (pspp_sheet_view_remove_widget
), tree_view
);
12695 if (mode
== PSPP_SHEET_SELECTION_RECTANGLE
&& column
->row_head
&&
12696 GTK_IS_BUTTON (cell_editable
))
12698 g_signal_connect (cell_editable
, "button-press-event",
12699 G_CALLBACK (pspp_sheet_view_editable_button_press_event
),
12701 g_object_set_data (G_OBJECT (cell_editable
), "pspp-sheet-view-node",
12702 GINT_TO_POINTER (row
));
12703 g_signal_connect (cell_editable
, "clicked",
12704 G_CALLBACK (pspp_sheet_view_editable_clicked
),
12708 pspp_sheet_view_override_cell_keypresses (GTK_WIDGET (cell_editable
),
12713 pspp_sheet_view_stop_editing (PsppSheetView
*tree_view
,
12714 gboolean cancel_editing
)
12716 PsppSheetViewColumn
*column
;
12717 GtkCellRenderer
*cell
;
12719 if (tree_view
->priv
->edited_column
== NULL
)
12723 * This is very evil. We need to do this, because
12724 * gtk_cell_editable_editing_done may trigger pspp_sheet_view_row_changed
12725 * later on. If pspp_sheet_view_row_changed notices
12726 * tree_view->priv->edited_column != NULL, it'll call
12727 * pspp_sheet_view_stop_editing again. Bad things will happen then.
12729 * Please read that again if you intend to modify anything here.
12732 column
= tree_view
->priv
->edited_column
;
12733 tree_view
->priv
->edited_column
= NULL
;
12735 cell
= _pspp_sheet_view_column_get_edited_cell (column
);
12736 gtk_cell_renderer_stop_editing (cell
, cancel_editing
);
12738 if (!cancel_editing
)
12739 gtk_cell_editable_editing_done (column
->editable_widget
);
12741 tree_view
->priv
->edited_column
= column
;
12743 gtk_cell_editable_remove_widget (column
->editable_widget
);
12748 * pspp_sheet_view_set_hover_selection:
12749 * @tree_view: a #PsppSheetView
12750 * @hover: %TRUE to enable hover selection mode
12752 * Enables of disables the hover selection mode of @tree_view.
12753 * Hover selection makes the selected row follow the pointer.
12754 * Currently, this works only for the selection modes
12755 * %PSPP_SHEET_SELECTION_SINGLE and %PSPP_SHEET_SELECTION_BROWSE.
12760 pspp_sheet_view_set_hover_selection (PsppSheetView
*tree_view
,
12763 hover
= hover
!= FALSE
;
12765 if (hover
!= tree_view
->priv
->hover_selection
)
12767 tree_view
->priv
->hover_selection
= hover
;
12769 g_object_notify (G_OBJECT (tree_view
), "hover-selection");
12774 * pspp_sheet_view_get_hover_selection:
12775 * @tree_view: a #PsppSheetView
12777 * Returns whether hover selection mode is turned on for @tree_view.
12779 * Return value: %TRUE if @tree_view is in hover selection mode
12784 pspp_sheet_view_get_hover_selection (PsppSheetView
*tree_view
)
12786 return tree_view
->priv
->hover_selection
;
12790 * pspp_sheet_view_set_rubber_banding:
12791 * @tree_view: a #PsppSheetView
12792 * @enable: %TRUE to enable rubber banding
12794 * Enables or disables rubber banding in @tree_view. If the selection mode is
12795 * #PSPP_SHEET_SELECTION_MULTIPLE or #PSPP_SHEET_SELECTION_RECTANGLE, rubber
12796 * banding will allow the user to select multiple rows by dragging the mouse.
12801 pspp_sheet_view_set_rubber_banding (PsppSheetView
*tree_view
,
12804 enable
= enable
!= FALSE
;
12806 if (enable
!= tree_view
->priv
->rubber_banding_enable
)
12808 tree_view
->priv
->rubber_banding_enable
= enable
;
12810 g_object_notify (G_OBJECT (tree_view
), "rubber-banding");
12815 * pspp_sheet_view_get_rubber_banding:
12816 * @tree_view: a #PsppSheetView
12818 * Returns whether rubber banding is turned on for @tree_view. If the
12819 * selection mode is #PSPP_SHEET_SELECTION_MULTIPLE or
12820 * #PSPP_SHEET_SELECTION_RECTANGLE, rubber banding will allow the user to
12821 * select multiple rows by dragging the mouse.
12823 * Return value: %TRUE if rubber banding in @tree_view is enabled.
12828 pspp_sheet_view_get_rubber_banding (PsppSheetView
*tree_view
)
12830 return tree_view
->priv
->rubber_banding_enable
;
12834 * pspp_sheet_view_is_rubber_banding_active:
12835 * @tree_view: a #PsppSheetView
12837 * Returns whether a rubber banding operation is currently being done
12840 * Return value: %TRUE if a rubber banding operation is currently being
12841 * done in @tree_view.
12846 pspp_sheet_view_is_rubber_banding_active (PsppSheetView
*tree_view
)
12848 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), FALSE
);
12850 if (tree_view
->priv
->rubber_banding_enable
12851 && tree_view
->priv
->rubber_band_status
== RUBBER_BAND_ACTIVE
)
12858 pspp_sheet_view_grab_notify (GtkWidget
*widget
,
12859 gboolean was_grabbed
)
12861 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
12863 tree_view
->priv
->in_grab
= !was_grabbed
;
12867 tree_view
->priv
->pressed_button
= -1;
12869 if (tree_view
->priv
->rubber_band_status
)
12870 pspp_sheet_view_stop_rubber_band (tree_view
);
12875 pspp_sheet_view_state_changed (GtkWidget
*widget
,
12876 GtkStateType previous_state
)
12878 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
12880 if (gtk_widget_get_realized (widget
))
12882 GtkStyle
*style
= gtk_widget_get_style (widget
);
12883 gdk_window_set_background (tree_view
->priv
->bin_window
, &style
->base
[gtk_widget_get_state (widget
)]);
12886 gtk_widget_queue_draw (widget
);
12890 * pspp_sheet_view_get_grid_lines:
12891 * @tree_view: a #PsppSheetView
12893 * Returns which grid lines are enabled in @tree_view.
12895 * Return value: a #PsppSheetViewGridLines value indicating which grid lines
12900 PsppSheetViewGridLines
12901 pspp_sheet_view_get_grid_lines (PsppSheetView
*tree_view
)
12903 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), 0);
12905 return tree_view
->priv
->grid_lines
;
12909 * pspp_sheet_view_set_grid_lines:
12910 * @tree_view: a #PsppSheetView
12911 * @grid_lines: a #PsppSheetViewGridLines value indicating which grid lines to
12914 * Sets which grid lines to draw in @tree_view.
12919 pspp_sheet_view_set_grid_lines (PsppSheetView
*tree_view
,
12920 PsppSheetViewGridLines grid_lines
)
12922 PsppSheetViewPrivate
*priv
;
12923 PsppSheetViewGridLines old_grid_lines
;
12925 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
12927 priv
= tree_view
->priv
;
12929 old_grid_lines
= priv
->grid_lines
;
12930 priv
->grid_lines
= grid_lines
;
12932 if (old_grid_lines
!= grid_lines
)
12934 gtk_widget_queue_draw (GTK_WIDGET (tree_view
));
12936 g_object_notify (G_OBJECT (tree_view
), "enable-grid-lines");
12941 * pspp_sheet_view_get_special_cells:
12942 * @tree_view: a #PsppSheetView
12944 * Returns which grid lines are enabled in @tree_view.
12946 * Return value: a #PsppSheetViewSpecialCells value indicating whether rows in
12947 * the sheet view contain special cells.
12949 PsppSheetViewSpecialCells
12950 pspp_sheet_view_get_special_cells (PsppSheetView
*tree_view
)
12952 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), 0);
12954 return tree_view
->priv
->special_cells
;
12958 * pspp_sheet_view_set_special_cells:
12959 * @tree_view: a #PsppSheetView
12960 * @special_cells: a #PsppSheetViewSpecialCells value indicating whether rows in
12961 * the sheet view contain special cells.
12963 * Sets whether rows in the sheet view contain special cells, controlling the
12964 * rendering of row selections.
12967 pspp_sheet_view_set_special_cells (PsppSheetView
*tree_view
,
12968 PsppSheetViewSpecialCells special_cells
)
12970 PsppSheetViewPrivate
*priv
;
12972 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
12974 priv
= tree_view
->priv
;
12976 if (priv
->special_cells
!= special_cells
)
12978 priv
->special_cells
= special_cells
;
12979 gtk_widget_queue_draw (GTK_WIDGET (tree_view
));
12980 g_object_notify (G_OBJECT (tree_view
), "special-cells");
12985 pspp_sheet_view_get_fixed_height (const PsppSheetView
*tree_view
)
12987 /* XXX (re)calculate fixed_height if necessary */
12988 return tree_view
->priv
->fixed_height
;
12992 pspp_sheet_view_set_fixed_height (PsppSheetView
*tree_view
,
12995 g_return_if_fail (fixed_height
> 0);
12997 if (tree_view
->priv
->fixed_height
!= fixed_height
)
12999 tree_view
->priv
->fixed_height
= fixed_height
;
13000 g_object_notify (G_OBJECT (tree_view
), "fixed-height");
13002 if (!tree_view
->priv
->fixed_height_set
)
13004 tree_view
->priv
->fixed_height_set
= TRUE
;
13005 g_object_notify (G_OBJECT (tree_view
), "fixed-height-set");
13010 * pspp_sheet_view_set_tooltip_row:
13011 * @tree_view: a #PsppSheetView
13012 * @tooltip: a #GtkTooltip
13013 * @path: a #GtkTreePath
13015 * Sets the tip area of @tooltip to be the area covered by the row at @path.
13016 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13017 * See also gtk_tooltip_set_tip_area().
13022 pspp_sheet_view_set_tooltip_row (PsppSheetView
*tree_view
,
13023 GtkTooltip
*tooltip
,
13026 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
13027 g_return_if_fail (GTK_IS_TOOLTIP (tooltip
));
13029 pspp_sheet_view_set_tooltip_cell (tree_view
, tooltip
, path
, NULL
, NULL
);
13033 * pspp_sheet_view_set_tooltip_cell:
13034 * @tree_view: a #PsppSheetView
13035 * @tooltip: a #GtkTooltip
13036 * @path: (allow-none): a #GtkTreePath or %NULL
13037 * @column: (allow-none): a #PsppSheetViewColumn or %NULL
13038 * @cell: (allow-none): a #GtkCellRenderer or %NULL
13040 * Sets the tip area of @tooltip to the area @path, @column and @cell have
13041 * in common. For example if @path is %NULL and @column is set, the tip
13042 * area will be set to the full area covered by @column. See also
13043 * gtk_tooltip_set_tip_area().
13045 * See also pspp_sheet_view_set_tooltip_column() for a simpler alternative.
13050 pspp_sheet_view_set_tooltip_cell (PsppSheetView
*tree_view
,
13051 GtkTooltip
*tooltip
,
13053 PsppSheetViewColumn
*column
,
13054 GtkCellRenderer
*cell
)
13058 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
13059 g_return_if_fail (GTK_IS_TOOLTIP (tooltip
));
13060 g_return_if_fail (column
== NULL
|| PSPP_IS_SHEET_VIEW_COLUMN (column
));
13061 g_return_if_fail (cell
== NULL
|| GTK_IS_CELL_RENDERER (cell
));
13063 /* Determine x values. */
13064 if (column
&& cell
)
13069 pspp_sheet_view_get_cell_area (tree_view
, path
, column
, &tmp
);
13070 pspp_sheet_view_column_cell_get_position (column
, cell
, &start
, &width
);
13072 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view
,
13075 rect
.width
= width
;
13081 pspp_sheet_view_get_background_area (tree_view
, NULL
, column
, &tmp
);
13082 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view
,
13085 rect
.width
= tmp
.width
;
13089 GtkAllocation allocation
;
13090 gtk_widget_get_allocation (GTK_WIDGET (tree_view
), &allocation
);
13092 rect
.width
= allocation
.width
;
13095 /* Determine y values. */
13100 pspp_sheet_view_get_background_area (tree_view
, path
, NULL
, &tmp
);
13101 pspp_sheet_view_convert_bin_window_to_widget_coords (tree_view
,
13104 rect
.height
= tmp
.height
;
13109 rect
.height
= gtk_adjustment_get_page_size (tree_view
->priv
->vadjustment
);
13112 gtk_tooltip_set_tip_area (tooltip
, &rect
);
13116 * pspp_sheet_view_get_tooltip_context:
13117 * @tree_view: a #PsppSheetView
13118 * @x: the x coordinate (relative to widget coordinates)
13119 * @y: the y coordinate (relative to widget coordinates)
13120 * @keyboard_tip: whether this is a keyboard tooltip or not
13121 * @model: (allow-none): a pointer to receive a #GtkTreeModel or %NULL
13122 * @path: (allow-none): a pointer to receive a #GtkTreePath or %NULL
13123 * @iter: (allow-none): a pointer to receive a #GtkTreeIter or %NULL
13125 * This function is supposed to be used in a #GtkWidget::query-tooltip
13126 * signal handler for #PsppSheetView. The @x, @y and @keyboard_tip values
13127 * which are received in the signal handler, should be passed to this
13128 * function without modification.
13130 * The return value indicates whether there is a tree view row at the given
13131 * coordinates (%TRUE) or not (%FALSE) for mouse tooltips. For keyboard
13132 * tooltips the row returned will be the cursor row. When %TRUE, then any of
13133 * @model, @path and @iter which have been provided will be set to point to
13134 * that row and the corresponding model. @x and @y will always be converted
13135 * to be relative to @tree_view's bin_window if @keyboard_tooltip is %FALSE.
13137 * Return value: whether or not the given tooltip context points to a row.
13142 pspp_sheet_view_get_tooltip_context (PsppSheetView
*tree_view
,
13145 gboolean keyboard_tip
,
13146 GtkTreeModel
**model
,
13147 GtkTreePath
**path
,
13150 GtkTreePath
*tmppath
= NULL
;
13152 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), FALSE
);
13153 g_return_val_if_fail (x
!= NULL
, FALSE
);
13154 g_return_val_if_fail (y
!= NULL
, FALSE
);
13158 pspp_sheet_view_get_cursor (tree_view
, &tmppath
, NULL
);
13165 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view
, *x
, *y
,
13168 if (!pspp_sheet_view_get_path_at_pos (tree_view
, *x
, *y
,
13169 &tmppath
, NULL
, NULL
, NULL
))
13174 *model
= pspp_sheet_view_get_model (tree_view
);
13177 gtk_tree_model_get_iter (pspp_sheet_view_get_model (tree_view
),
13183 gtk_tree_path_free (tmppath
);
13189 pspp_sheet_view_set_tooltip_query_cb (GtkWidget
*widget
,
13192 gboolean keyboard_tip
,
13193 GtkTooltip
*tooltip
,
13196 GValue value
= { 0, };
13197 GValue transformed
= { 0, };
13200 GtkTreeModel
*model
;
13201 PsppSheetView
*tree_view
= PSPP_SHEET_VIEW (widget
);
13203 if (!pspp_sheet_view_get_tooltip_context (PSPP_SHEET_VIEW (widget
),
13206 &model
, &path
, &iter
))
13209 gtk_tree_model_get_value (model
, &iter
,
13210 tree_view
->priv
->tooltip_column
, &value
);
13212 g_value_init (&transformed
, G_TYPE_STRING
);
13214 if (!g_value_transform (&value
, &transformed
))
13216 g_value_unset (&value
);
13217 gtk_tree_path_free (path
);
13222 g_value_unset (&value
);
13224 if (!g_value_get_string (&transformed
))
13226 g_value_unset (&transformed
);
13227 gtk_tree_path_free (path
);
13232 gtk_tooltip_set_markup (tooltip
, g_value_get_string (&transformed
));
13233 pspp_sheet_view_set_tooltip_row (tree_view
, tooltip
, path
);
13235 gtk_tree_path_free (path
);
13236 g_value_unset (&transformed
);
13242 * pspp_sheet_view_set_tooltip_column:
13243 * @tree_view: a #PsppSheetView
13244 * @column: an integer, which is a valid column number for @tree_view's model
13246 * If you only plan to have simple (text-only) tooltips on full rows, you
13247 * can use this function to have #PsppSheetView handle these automatically
13248 * for you. @column should be set to the column in @tree_view's model
13249 * containing the tooltip texts, or -1 to disable this feature.
13251 * When enabled, #GtkWidget::has-tooltip will be set to %TRUE and
13252 * @tree_view will connect a #GtkWidget::query-tooltip signal handler.
13254 * Note that the signal handler sets the text with gtk_tooltip_set_markup(),
13255 * so &, <, etc have to be escaped in the text.
13260 pspp_sheet_view_set_tooltip_column (PsppSheetView
*tree_view
,
13263 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view
));
13265 if (column
== tree_view
->priv
->tooltip_column
)
13270 g_signal_handlers_disconnect_by_func (tree_view
,
13271 pspp_sheet_view_set_tooltip_query_cb
,
13273 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view
), FALSE
);
13277 if (tree_view
->priv
->tooltip_column
== -1)
13279 g_signal_connect (tree_view
, "query-tooltip",
13280 G_CALLBACK (pspp_sheet_view_set_tooltip_query_cb
), NULL
);
13281 gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view
), TRUE
);
13285 tree_view
->priv
->tooltip_column
= column
;
13286 g_object_notify (G_OBJECT (tree_view
), "tooltip-column");
13290 * pspp_sheet_view_get_tooltip_column:
13291 * @tree_view: a #PsppSheetView
13293 * Returns the column of @tree_view's model which is being used for
13294 * displaying tooltips on @tree_view's rows.
13296 * Return value: the index of the tooltip column that is currently being
13297 * used, or -1 if this is disabled.
13302 pspp_sheet_view_get_tooltip_column (PsppSheetView
*tree_view
)
13304 g_return_val_if_fail (PSPP_IS_SHEET_VIEW (tree_view
), 0);
13306 return tree_view
->priv
->tooltip_column
;
13310 _gtk_boolean_handled_accumulator (GSignalInvocationHint
*ihint
,
13311 GValue
*return_accu
,
13312 const GValue
*handler_return
,
13315 gboolean continue_emission
;
13316 gboolean signal_handled
;
13318 signal_handled
= g_value_get_boolean (handler_return
);
13319 g_value_set_boolean (return_accu
, signal_handled
);
13320 continue_emission
= !signal_handled
;
13322 return continue_emission
;
13327 pspp_sheet_view_grid_lines_get_type (void)
13329 static GType etype
= 0;
13330 if (G_UNLIKELY(etype
== 0)) {
13331 static const GEnumValue values
[] = {
13332 { PSPP_SHEET_VIEW_GRID_LINES_NONE
, "PSPP_SHEET_VIEW_GRID_LINES_NONE", "none" },
13333 { PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL
, "PSPP_SHEET_VIEW_GRID_LINES_HORIZONTAL", "horizontal" },
13334 { PSPP_SHEET_VIEW_GRID_LINES_VERTICAL
, "PSPP_SHEET_VIEW_GRID_LINES_VERTICAL", "vertical" },
13335 { PSPP_SHEET_VIEW_GRID_LINES_BOTH
, "PSPP_SHEET_VIEW_GRID_LINES_BOTH", "both" },
13338 etype
= g_enum_register_static (g_intern_static_string ("PsppSheetViewGridLines"), values
);
13344 pspp_sheet_view_special_cells_get_type (void)
13346 static GType etype
= 0;
13347 if (G_UNLIKELY(etype
== 0)) {
13348 static const GEnumValue values
[] = {
13349 { PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT
, "PSPP_SHEET_VIEW_SPECIAL_CELLS_DETECT", "detect" },
13350 { PSPP_SHEET_VIEW_SPECIAL_CELLS_YES
, "PSPP_SHEET_VIEW_SPECIAL_CELLS_YES", "yes" },
13351 { PSPP_SHEET_VIEW_SPECIAL_CELLS_NO
, "PSPP_SHEET_VIEW_SPECIAL_CELLS_NO", "no" },
13354 etype
= g_enum_register_static (g_intern_static_string ("PsppSheetViewSpecialCells"), values
);