1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2011, 2012 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/>. */
17 /* gtktreeviewcolumn.c
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"
45 #include "ui/gui/psppire-marshal.h"
47 #define P_(STRING) STRING
48 #define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
49 #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
79 typedef struct _PsppSheetViewColumnCellInfo PsppSheetViewColumnCellInfo;
80 struct _PsppSheetViewColumnCellInfo
82 GtkCellRenderer *cell;
84 PsppSheetCellDataFunc func;
86 GDestroyNotify destroy;
92 guint in_editing_mode : 1;
96 static void pspp_sheet_view_column_cell_layout_init (GtkCellLayoutIface *iface);
99 static void pspp_sheet_view_column_set_property (GObject *object,
103 static void pspp_sheet_view_column_get_property (GObject *object,
107 static void pspp_sheet_view_column_finalize (GObject *object);
109 /* GtkCellLayout implementation */
110 static void pspp_sheet_view_column_cell_layout_pack_start (GtkCellLayout *cell_layout,
111 GtkCellRenderer *cell,
113 static void pspp_sheet_view_column_cell_layout_pack_end (GtkCellLayout *cell_layout,
114 GtkCellRenderer *cell,
116 static void pspp_sheet_view_column_cell_layout_clear (GtkCellLayout *cell_layout);
117 static void pspp_sheet_view_column_cell_layout_add_attribute (GtkCellLayout *cell_layout,
118 GtkCellRenderer *cell,
119 const gchar *attribute,
121 static void pspp_sheet_view_column_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout,
122 GtkCellRenderer *cell,
123 GtkCellLayoutDataFunc func,
125 GDestroyNotify destroy);
126 static void pspp_sheet_view_column_cell_layout_clear_attributes (GtkCellLayout *cell_layout,
127 GtkCellRenderer *cell);
128 static void pspp_sheet_view_column_cell_layout_reorder (GtkCellLayout *cell_layout,
129 GtkCellRenderer *cell,
131 static GList *pspp_sheet_view_column_cell_layout_get_cells (GtkCellLayout *cell_layout);
133 /* Button handling code */
134 static void pspp_sheet_view_column_create_button (PsppSheetViewColumn *tree_column);
135 static void pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column);
137 /* Button signal handlers */
138 static gint pspp_sheet_view_column_button_event (GtkWidget *widget,
141 static void pspp_sheet_view_column_button_clicked (GtkWidget *widget,
143 static gboolean pspp_sheet_view_column_mnemonic_activate (GtkWidget *widget,
144 gboolean group_cycling,
147 /* Property handlers */
148 static void pspp_sheet_view_model_sort_column_changed (GtkTreeSortable *sortable,
149 PsppSheetViewColumn *tree_column);
151 /* Internal functions */
152 static void pspp_sheet_view_column_sort (PsppSheetViewColumn *tree_column,
154 static void pspp_sheet_view_column_setup_sort_column_id_callback (PsppSheetViewColumn *tree_column);
155 static void pspp_sheet_view_column_set_attributesv (PsppSheetViewColumn *tree_column,
156 GtkCellRenderer *cell_renderer,
158 static PsppSheetViewColumnCellInfo *pspp_sheet_view_column_get_cell_info (PsppSheetViewColumn *tree_column,
159 GtkCellRenderer *cell_renderer);
161 /* cell list manipulation */
162 static GList *pspp_sheet_view_column_cell_first (PsppSheetViewColumn *tree_column);
163 static GList *pspp_sheet_view_column_cell_last (PsppSheetViewColumn *tree_column);
164 static GList *pspp_sheet_view_column_cell_next (PsppSheetViewColumn *tree_column,
166 static GList *pspp_sheet_view_column_cell_prev (PsppSheetViewColumn *tree_column,
168 static void pspp_sheet_view_column_clear_attributes_by_info (PsppSheetViewColumn *tree_column,
169 PsppSheetViewColumnCellInfo *info);
170 /* GtkBuildable implementation */
171 static void pspp_sheet_view_column_buildable_init (GtkBuildableIface *iface);
173 static guint tree_column_signals[LAST_SIGNAL] = { 0 };
175 G_DEFINE_TYPE_WITH_CODE (PsppSheetViewColumn, pspp_sheet_view_column, GTK_TYPE_OBJECT,
176 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
177 pspp_sheet_view_column_cell_layout_init)
178 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
179 pspp_sheet_view_column_buildable_init))
183 pspp_sheet_view_column_class_init (PsppSheetViewColumnClass *class)
185 GObjectClass *object_class;
187 object_class = (GObjectClass*) class;
189 class->clicked = NULL;
191 object_class->finalize = pspp_sheet_view_column_finalize;
192 object_class->set_property = pspp_sheet_view_column_set_property;
193 object_class->get_property = pspp_sheet_view_column_get_property;
195 tree_column_signals[CLICKED] =
196 g_signal_new ("clicked",
197 G_OBJECT_CLASS_TYPE (object_class),
199 G_STRUCT_OFFSET (PsppSheetViewColumnClass, clicked),
201 g_cclosure_marshal_VOID__VOID,
204 g_object_class_install_property (object_class,
206 g_param_spec_boolean ("visible",
208 P_("Whether to display the column"),
210 GTK_PARAM_READWRITE));
212 g_object_class_install_property (object_class,
214 g_param_spec_boolean ("resizable",
216 P_("Column is user-resizable"),
218 GTK_PARAM_READWRITE));
220 g_object_class_install_property (object_class,
222 g_param_spec_int ("width",
224 P_("Current width of the column"),
228 GTK_PARAM_READABLE));
229 g_object_class_install_property (object_class,
231 g_param_spec_int ("spacing",
233 P_("Space which is inserted between cells"),
237 GTK_PARAM_READWRITE));
238 g_object_class_install_property (object_class,
240 g_param_spec_enum ("sizing",
242 P_("Resize mode of the column"),
243 PSPP_TYPE_SHEET_VIEW_COLUMN_SIZING,
244 PSPP_SHEET_VIEW_COLUMN_GROW_ONLY,
245 GTK_PARAM_READWRITE));
247 g_object_class_install_property (object_class,
249 g_param_spec_int ("fixed-width",
251 P_("Current fixed width of the column"),
255 GTK_PARAM_READWRITE));
257 g_object_class_install_property (object_class,
259 g_param_spec_int ("min-width",
261 P_("Minimum allowed width of the column"),
265 GTK_PARAM_READWRITE));
267 g_object_class_install_property (object_class,
269 g_param_spec_int ("max-width",
271 P_("Maximum allowed width of the column"),
275 GTK_PARAM_READWRITE));
277 g_object_class_install_property (object_class,
279 g_param_spec_string ("title",
281 P_("Title to appear in column header"),
283 GTK_PARAM_READWRITE));
285 g_object_class_install_property (object_class,
287 g_param_spec_boolean ("expand",
289 P_("Column gets share of extra width allocated to the widget"),
291 GTK_PARAM_READWRITE));
293 g_object_class_install_property (object_class,
295 g_param_spec_boolean ("clickable",
297 P_("Whether the header can be clicked"),
299 GTK_PARAM_READWRITE));
302 g_object_class_install_property (object_class,
304 g_param_spec_object ("widget",
306 P_("Widget to put in column header button instead of column title"),
308 GTK_PARAM_READWRITE));
310 g_object_class_install_property (object_class,
312 g_param_spec_float ("alignment",
314 P_("X Alignment of the column header text or widget"),
318 GTK_PARAM_READWRITE));
320 g_object_class_install_property (object_class,
322 g_param_spec_boolean ("reorderable",
324 P_("Whether the column can be reordered around the headers"),
326 GTK_PARAM_READWRITE));
328 g_object_class_install_property (object_class,
330 g_param_spec_boolean ("sort-indicator",
331 P_("Sort indicator"),
332 P_("Whether to show a sort indicator"),
334 GTK_PARAM_READWRITE));
336 g_object_class_install_property (object_class,
338 g_param_spec_enum ("sort-order",
340 P_("Sort direction the sort indicator should indicate"),
343 GTK_PARAM_READWRITE));
346 * PsppSheetViewColumn:sort-column-id:
348 * Logical sort column ID this column sorts on when selected for sorting. Setting the sort column ID makes the column header
349 * clickable. Set to %-1 to make the column unsortable.
353 g_object_class_install_property (object_class,
355 g_param_spec_int ("sort-column-id",
356 P_("Sort column ID"),
357 P_("Logical sort column ID this column sorts on when selected for sorting"),
361 GTK_PARAM_READWRITE));
365 pspp_sheet_view_column_buildable_init (GtkBuildableIface *iface)
367 iface->add_child = _gtk_cell_layout_buildable_add_child;
368 iface->custom_tag_start = _gtk_cell_layout_buildable_custom_tag_start;
369 iface->custom_tag_end = _gtk_cell_layout_buildable_custom_tag_end;
373 pspp_sheet_view_column_cell_layout_init (GtkCellLayoutIface *iface)
375 iface->pack_start = pspp_sheet_view_column_cell_layout_pack_start;
376 iface->pack_end = pspp_sheet_view_column_cell_layout_pack_end;
377 iface->clear = pspp_sheet_view_column_cell_layout_clear;
378 iface->add_attribute = pspp_sheet_view_column_cell_layout_add_attribute;
379 iface->set_cell_data_func = pspp_sheet_view_column_cell_layout_set_cell_data_func;
380 iface->clear_attributes = pspp_sheet_view_column_cell_layout_clear_attributes;
381 iface->reorder = pspp_sheet_view_column_cell_layout_reorder;
382 iface->get_cells = pspp_sheet_view_column_cell_layout_get_cells;
386 pspp_sheet_view_column_init (PsppSheetViewColumn *tree_column)
388 tree_column->button = NULL;
389 tree_column->xalign = 0.0;
390 tree_column->width = 0;
391 tree_column->spacing = 0;
392 tree_column->requested_width = -1;
393 tree_column->min_width = -1;
394 tree_column->max_width = -1;
395 tree_column->resized_width = 0;
396 tree_column->column_type = PSPP_SHEET_VIEW_COLUMN_GROW_ONLY;
397 tree_column->visible = TRUE;
398 tree_column->resizable = FALSE;
399 tree_column->expand = FALSE;
400 tree_column->clickable = FALSE;
401 tree_column->dirty = TRUE;
402 tree_column->sort_order = GTK_SORT_ASCENDING;
403 tree_column->show_sort_indicator = FALSE;
404 tree_column->property_changed_signal = 0;
405 tree_column->sort_clicked_signal = 0;
406 tree_column->sort_column_changed_signal = 0;
407 tree_column->sort_column_id = -1;
408 tree_column->reorderable = FALSE;
409 tree_column->maybe_reordered = FALSE;
410 tree_column->fixed_width = 1;
411 tree_column->use_resized_width = FALSE;
412 tree_column->title = g_strdup ("");
416 pspp_sheet_view_column_finalize (GObject *object)
418 PsppSheetViewColumn *tree_column = (PsppSheetViewColumn *) object;
421 for (list = tree_column->cell_list; list; list = list->next)
423 PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
427 GDestroyNotify d = info->destroy;
429 info->destroy = NULL;
432 pspp_sheet_view_column_clear_attributes_by_info (tree_column, info);
433 g_object_unref (info->cell);
437 g_free (tree_column->title);
438 g_list_free (tree_column->cell_list);
440 if (tree_column->child)
441 g_object_unref (tree_column->child);
443 G_OBJECT_CLASS (pspp_sheet_view_column_parent_class)->finalize (object);
447 pspp_sheet_view_column_set_property (GObject *object,
452 PsppSheetViewColumn *tree_column;
454 tree_column = PSPP_SHEET_VIEW_COLUMN (object);
459 pspp_sheet_view_column_set_visible (tree_column,
460 g_value_get_boolean (value));
464 pspp_sheet_view_column_set_resizable (tree_column,
465 g_value_get_boolean (value));
469 pspp_sheet_view_column_set_sizing (tree_column,
470 g_value_get_enum (value));
473 case PROP_FIXED_WIDTH:
474 pspp_sheet_view_column_set_fixed_width (tree_column,
475 g_value_get_int (value));
479 pspp_sheet_view_column_set_min_width (tree_column,
480 g_value_get_int (value));
484 pspp_sheet_view_column_set_max_width (tree_column,
485 g_value_get_int (value));
489 pspp_sheet_view_column_set_spacing (tree_column,
490 g_value_get_int (value));
494 pspp_sheet_view_column_set_title (tree_column,
495 g_value_get_string (value));
499 pspp_sheet_view_column_set_expand (tree_column,
500 g_value_get_boolean (value));
504 pspp_sheet_view_column_set_clickable (tree_column,
505 g_value_get_boolean (value));
509 pspp_sheet_view_column_set_widget (tree_column,
510 (GtkWidget*) g_value_get_object (value));
514 pspp_sheet_view_column_set_alignment (tree_column,
515 g_value_get_float (value));
518 case PROP_REORDERABLE:
519 pspp_sheet_view_column_set_reorderable (tree_column,
520 g_value_get_boolean (value));
523 case PROP_SORT_INDICATOR:
524 pspp_sheet_view_column_set_sort_indicator (tree_column,
525 g_value_get_boolean (value));
528 case PROP_SORT_ORDER:
529 pspp_sheet_view_column_set_sort_order (tree_column,
530 g_value_get_enum (value));
533 case PROP_SORT_COLUMN_ID:
534 pspp_sheet_view_column_set_sort_column_id (tree_column,
535 g_value_get_int (value));
539 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
545 pspp_sheet_view_column_get_property (GObject *object,
550 PsppSheetViewColumn *tree_column;
552 tree_column = PSPP_SHEET_VIEW_COLUMN (object);
557 g_value_set_boolean (value,
558 pspp_sheet_view_column_get_visible (tree_column));
562 g_value_set_boolean (value,
563 pspp_sheet_view_column_get_resizable (tree_column));
567 g_value_set_int (value,
568 pspp_sheet_view_column_get_width (tree_column));
572 g_value_set_int (value,
573 pspp_sheet_view_column_get_spacing (tree_column));
577 g_value_set_enum (value,
578 pspp_sheet_view_column_get_sizing (tree_column));
581 case PROP_FIXED_WIDTH:
582 g_value_set_int (value,
583 pspp_sheet_view_column_get_fixed_width (tree_column));
587 g_value_set_int (value,
588 pspp_sheet_view_column_get_min_width (tree_column));
592 g_value_set_int (value,
593 pspp_sheet_view_column_get_max_width (tree_column));
597 g_value_set_string (value,
598 pspp_sheet_view_column_get_title (tree_column));
602 g_value_set_boolean (value,
603 pspp_sheet_view_column_get_expand (tree_column));
607 g_value_set_boolean (value,
608 pspp_sheet_view_column_get_clickable (tree_column));
612 g_value_set_object (value,
613 (GObject*) pspp_sheet_view_column_get_widget (tree_column));
617 g_value_set_float (value,
618 pspp_sheet_view_column_get_alignment (tree_column));
621 case PROP_REORDERABLE:
622 g_value_set_boolean (value,
623 pspp_sheet_view_column_get_reorderable (tree_column));
626 case PROP_SORT_INDICATOR:
627 g_value_set_boolean (value,
628 pspp_sheet_view_column_get_sort_indicator (tree_column));
631 case PROP_SORT_ORDER:
632 g_value_set_enum (value,
633 pspp_sheet_view_column_get_sort_order (tree_column));
636 case PROP_SORT_COLUMN_ID:
637 g_value_set_int (value,
638 pspp_sheet_view_column_get_sort_column_id (tree_column));
642 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
647 /* Implementation of GtkCellLayout interface
651 pspp_sheet_view_column_cell_layout_pack_start (GtkCellLayout *cell_layout,
652 GtkCellRenderer *cell,
655 PsppSheetViewColumn *column;
656 PsppSheetViewColumnCellInfo *cell_info;
658 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout));
659 column = PSPP_SHEET_VIEW_COLUMN (cell_layout);
660 g_return_if_fail (! pspp_sheet_view_column_get_cell_info (column, cell));
662 g_object_ref_sink (cell);
664 cell_info = g_new0 (PsppSheetViewColumnCellInfo, 1);
665 cell_info->cell = cell;
666 cell_info->expand = expand ? TRUE : FALSE;
667 cell_info->pack = GTK_PACK_START;
668 cell_info->has_focus = 0;
669 cell_info->attributes = NULL;
671 column->cell_list = g_list_append (column->cell_list, cell_info);
675 pspp_sheet_view_column_cell_layout_pack_end (GtkCellLayout *cell_layout,
676 GtkCellRenderer *cell,
679 PsppSheetViewColumn *column;
680 PsppSheetViewColumnCellInfo *cell_info;
682 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout));
683 column = PSPP_SHEET_VIEW_COLUMN (cell_layout);
684 g_return_if_fail (! pspp_sheet_view_column_get_cell_info (column, cell));
686 g_object_ref_sink (cell);
688 cell_info = g_new0 (PsppSheetViewColumnCellInfo, 1);
689 cell_info->cell = cell;
690 cell_info->expand = expand ? TRUE : FALSE;
691 cell_info->pack = GTK_PACK_END;
692 cell_info->has_focus = 0;
693 cell_info->attributes = NULL;
695 column->cell_list = g_list_append (column->cell_list, cell_info);
699 pspp_sheet_view_column_cell_layout_clear (GtkCellLayout *cell_layout)
701 PsppSheetViewColumn *column;
703 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout));
704 column = PSPP_SHEET_VIEW_COLUMN (cell_layout);
706 while (column->cell_list)
708 PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *)column->cell_list->data;
710 pspp_sheet_view_column_cell_layout_clear_attributes (cell_layout, info->cell);
711 g_object_unref (info->cell);
713 column->cell_list = g_list_delete_link (column->cell_list,
719 pspp_sheet_view_column_cell_layout_add_attribute (GtkCellLayout *cell_layout,
720 GtkCellRenderer *cell,
721 const gchar *attribute,
724 PsppSheetViewColumn *tree_column;
725 PsppSheetViewColumnCellInfo *info;
727 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout));
728 tree_column = PSPP_SHEET_VIEW_COLUMN (cell_layout);
730 info = pspp_sheet_view_column_get_cell_info (tree_column, cell);
731 g_return_if_fail (info != NULL);
733 info->attributes = g_slist_prepend (info->attributes, GINT_TO_POINTER (column));
734 info->attributes = g_slist_prepend (info->attributes, g_strdup (attribute));
736 if (tree_column->tree_view)
737 _pspp_sheet_view_column_cell_set_dirty (tree_column, TRUE);
741 pspp_sheet_view_column_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout,
742 GtkCellRenderer *cell,
743 GtkCellLayoutDataFunc func,
745 GDestroyNotify destroy)
747 PsppSheetViewColumn *column;
748 PsppSheetViewColumnCellInfo *info;
750 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout));
751 column = PSPP_SHEET_VIEW_COLUMN (cell_layout);
753 info = pspp_sheet_view_column_get_cell_info (column, cell);
754 g_return_if_fail (info != NULL);
758 GDestroyNotify d = info->destroy;
760 info->destroy = NULL;
764 info->func = (PsppSheetCellDataFunc)func;
765 info->func_data = func_data;
766 info->destroy = destroy;
768 if (column->tree_view)
769 _pspp_sheet_view_column_cell_set_dirty (column, TRUE);
773 pspp_sheet_view_column_cell_layout_clear_attributes (GtkCellLayout *cell_layout,
774 GtkCellRenderer *cell_renderer)
776 PsppSheetViewColumn *column;
777 PsppSheetViewColumnCellInfo *info;
779 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout));
780 column = PSPP_SHEET_VIEW_COLUMN (cell_layout);
782 info = pspp_sheet_view_column_get_cell_info (column, cell_renderer);
784 pspp_sheet_view_column_clear_attributes_by_info (column, info);
788 pspp_sheet_view_column_cell_layout_reorder (GtkCellLayout *cell_layout,
789 GtkCellRenderer *cell,
793 PsppSheetViewColumn *column;
794 PsppSheetViewColumnCellInfo *info;
796 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (cell_layout));
797 column = PSPP_SHEET_VIEW_COLUMN (cell_layout);
799 info = pspp_sheet_view_column_get_cell_info (column, cell);
801 g_return_if_fail (info != NULL);
802 g_return_if_fail (position >= 0);
804 link = g_list_find (column->cell_list, info);
806 g_return_if_fail (link != NULL);
808 column->cell_list = g_list_delete_link (column->cell_list, link);
809 column->cell_list = g_list_insert (column->cell_list, info, position);
811 if (column->tree_view)
812 gtk_widget_queue_draw (column->tree_view);
816 pspp_sheet_view_column_clear_attributes_by_info (PsppSheetViewColumn *tree_column,
817 PsppSheetViewColumnCellInfo *info)
821 list = info->attributes;
823 while (list && list->next)
826 list = list->next->next;
828 g_slist_free (info->attributes);
829 info->attributes = NULL;
831 if (tree_column->tree_view)
832 _pspp_sheet_view_column_cell_set_dirty (tree_column, TRUE);
838 /* Button handling code
841 pspp_sheet_view_column_create_button (PsppSheetViewColumn *tree_column)
843 PsppSheetView *tree_view;
847 tree_view = (PsppSheetView *) tree_column->tree_view;
849 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
850 g_return_if_fail (tree_column->button == NULL);
852 gtk_widget_push_composite_child ();
853 tree_column->button = gtk_button_new ();
854 gtk_widget_add_events (tree_column->button, GDK_POINTER_MOTION_MASK);
855 gtk_widget_pop_composite_child ();
857 /* make sure we own a reference to it as well. */
858 if (tree_view->priv->header_window)
859 gtk_widget_set_parent_window (tree_column->button, tree_view->priv->header_window);
860 gtk_widget_set_parent (tree_column->button, GTK_WIDGET (tree_view));
862 g_signal_connect (tree_column->button, "event",
863 G_CALLBACK (pspp_sheet_view_column_button_event),
865 g_signal_connect (tree_column->button, "clicked",
866 G_CALLBACK (pspp_sheet_view_column_button_clicked),
869 tree_column->alignment = gtk_alignment_new (tree_column->xalign, 0.5, 0.0, 0.0);
871 hbox = gtk_hbox_new (FALSE, 2);
872 tree_column->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_IN);
874 if (tree_column->child)
875 child = tree_column->child;
878 child = gtk_label_new (tree_column->title);
879 gtk_widget_show (child);
882 g_signal_connect (child, "mnemonic-activate",
883 G_CALLBACK (pspp_sheet_view_column_mnemonic_activate),
886 if (tree_column->xalign <= 0.5)
887 gtk_box_pack_end (GTK_BOX (hbox), tree_column->arrow, FALSE, FALSE, 0);
889 gtk_box_pack_start (GTK_BOX (hbox), tree_column->arrow, FALSE, FALSE, 0);
891 gtk_box_pack_start (GTK_BOX (hbox), tree_column->alignment, TRUE, TRUE, 0);
893 gtk_container_add (GTK_CONTAINER (tree_column->alignment), child);
894 gtk_container_add (GTK_CONTAINER (tree_column->button), hbox);
896 gtk_widget_show (hbox);
897 gtk_widget_show (tree_column->alignment);
898 pspp_sheet_view_column_update_button (tree_column);
902 pspp_sheet_view_column_update_button (PsppSheetViewColumn *tree_column)
904 gint sort_column_id = -1;
906 GtkWidget *alignment;
908 GtkWidget *current_child;
909 GtkArrowType arrow_type = GTK_ARROW_NONE;
912 if (tree_column->tree_view)
913 model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (tree_column->tree_view));
917 /* Create a button if necessary */
918 if (tree_column->visible &&
919 tree_column->button == NULL &&
920 tree_column->tree_view &&
921 gtk_widget_get_realized (tree_column->tree_view))
922 pspp_sheet_view_column_create_button (tree_column);
924 if (! tree_column->button)
927 hbox = GTK_BIN (tree_column->button)->child;
928 alignment = tree_column->alignment;
929 arrow = tree_column->arrow;
930 current_child = GTK_BIN (alignment)->child;
932 /* Set up the actual button */
933 gtk_alignment_set (GTK_ALIGNMENT (alignment), tree_column->xalign,
936 if (tree_column->child)
938 if (current_child != tree_column->child)
940 gtk_container_remove (GTK_CONTAINER (alignment),
942 gtk_container_add (GTK_CONTAINER (alignment),
948 if (current_child == NULL)
950 current_child = gtk_label_new (NULL);
951 gtk_widget_show (current_child);
952 gtk_container_add (GTK_CONTAINER (alignment),
956 g_return_if_fail (GTK_IS_LABEL (current_child));
958 if (tree_column->title)
959 gtk_label_set_text_with_mnemonic (GTK_LABEL (current_child),
962 gtk_label_set_text_with_mnemonic (GTK_LABEL (current_child),
966 if (GTK_IS_TREE_SORTABLE (model))
967 gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model),
971 if (tree_column->show_sort_indicator)
973 gboolean alternative;
975 g_object_get (gtk_widget_get_settings (tree_column->tree_view),
976 "gtk-alternative-sort-arrows", &alternative,
979 switch (tree_column->sort_order)
981 case GTK_SORT_ASCENDING:
982 arrow_type = alternative ? GTK_ARROW_UP : GTK_ARROW_DOWN;
985 case GTK_SORT_DESCENDING:
986 arrow_type = alternative ? GTK_ARROW_DOWN : GTK_ARROW_UP;
990 g_warning (G_STRLOC": bad sort order");
995 gtk_arrow_set (GTK_ARROW (arrow),
999 /* Put arrow on the right if the text is left-or-center justified, and on the
1000 * left otherwise; do this by packing boxes, so flipping text direction will
1003 g_object_ref (arrow);
1004 gtk_container_remove (GTK_CONTAINER (hbox), arrow);
1006 if (tree_column->xalign <= 0.5)
1008 gtk_box_pack_end (GTK_BOX (hbox), arrow, FALSE, FALSE, 0);
1012 gtk_box_pack_start (GTK_BOX (hbox), arrow, FALSE, FALSE, 0);
1013 /* move it to the front */
1014 gtk_box_reorder_child (GTK_BOX (hbox), arrow, 0);
1016 g_object_unref (arrow);
1018 if (tree_column->show_sort_indicator
1019 || (GTK_IS_TREE_SORTABLE (model) && tree_column->sort_column_id >= 0))
1020 gtk_widget_show (arrow);
1022 gtk_widget_hide (arrow);
1024 /* It's always safe to hide the button. It isn't always safe to show it, as
1025 * if you show it before it's realized, it'll get the wrong window. */
1026 if (tree_column->button &&
1027 tree_column->tree_view != NULL &&
1028 gtk_widget_get_realized (tree_column->tree_view))
1030 if (tree_column->visible)
1032 gtk_widget_show_now (tree_column->button);
1033 if (tree_column->window)
1035 if (tree_column->resizable)
1037 gdk_window_show (tree_column->window);
1038 gdk_window_raise (tree_column->window);
1042 gdk_window_hide (tree_column->window);
1048 gtk_widget_hide (tree_column->button);
1049 if (tree_column->window)
1050 gdk_window_hide (tree_column->window);
1054 if (tree_column->reorderable || tree_column->clickable)
1056 gtk_widget_set_can_focus (tree_column->button, TRUE);
1060 gtk_widget_set_can_focus (tree_column->button, FALSE);
1061 if (gtk_widget_has_focus (tree_column->button))
1063 GtkWidget *toplevel = gtk_widget_get_toplevel (tree_column->tree_view);
1064 if (gtk_widget_is_toplevel (toplevel))
1066 gtk_window_set_focus (GTK_WINDOW (toplevel), NULL);
1070 /* Queue a resize on the assumption that we always want to catch all changes
1071 * and columns don't change all that often.
1073 if (gtk_widget_get_realized (tree_column->tree_view))
1074 gtk_widget_queue_resize (tree_column->tree_view);
1078 /* Button signal handlers
1082 pspp_sheet_view_column_button_event (GtkWidget *widget,
1086 PsppSheetViewColumn *column = (PsppSheetViewColumn *) data;
1088 g_return_val_if_fail (event != NULL, FALSE);
1090 if (event->type == GDK_BUTTON_PRESS &&
1091 column->reorderable &&
1092 ((GdkEventButton *)event)->button == 1)
1094 column->maybe_reordered = TRUE;
1095 gdk_window_get_pointer (GTK_BUTTON (widget)->event_window,
1099 gtk_widget_grab_focus (widget);
1102 if (event->type == GDK_BUTTON_RELEASE ||
1103 event->type == GDK_LEAVE_NOTIFY)
1104 column->maybe_reordered = FALSE;
1106 if (event->type == GDK_MOTION_NOTIFY &&
1107 column->maybe_reordered &&
1108 (gtk_drag_check_threshold (widget,
1111 (gint) ((GdkEventMotion *)event)->x,
1112 (gint) ((GdkEventMotion *)event)->y)))
1114 column->maybe_reordered = FALSE;
1115 _pspp_sheet_view_column_start_drag (PSPP_SHEET_VIEW (column->tree_view), column);
1118 if (column->clickable == FALSE)
1120 switch (event->type)
1122 case GDK_BUTTON_PRESS:
1123 case GDK_2BUTTON_PRESS:
1124 case GDK_3BUTTON_PRESS:
1125 case GDK_MOTION_NOTIFY:
1126 case GDK_BUTTON_RELEASE:
1127 case GDK_ENTER_NOTIFY:
1128 case GDK_LEAVE_NOTIFY:
1139 pspp_sheet_view_column_button_clicked (GtkWidget *widget, gpointer data)
1141 g_signal_emit_by_name (data, "clicked");
1145 pspp_sheet_view_column_mnemonic_activate (GtkWidget *widget,
1146 gboolean group_cycling,
1149 PsppSheetViewColumn *column = (PsppSheetViewColumn *)data;
1151 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (column), FALSE);
1153 PSPP_SHEET_VIEW (column->tree_view)->priv->focus_column = column;
1154 if (column->clickable)
1155 gtk_button_clicked (GTK_BUTTON (column->button));
1156 else if (gtk_widget_get_can_focus (column->button))
1157 gtk_widget_grab_focus (column->button);
1159 gtk_widget_grab_focus (column->tree_view);
1165 pspp_sheet_view_model_sort_column_changed (GtkTreeSortable *sortable,
1166 PsppSheetViewColumn *column)
1168 gint sort_column_id;
1171 if (gtk_tree_sortable_get_sort_column_id (sortable,
1175 if (sort_column_id == column->sort_column_id)
1177 pspp_sheet_view_column_set_sort_indicator (column, TRUE);
1178 pspp_sheet_view_column_set_sort_order (column, order);
1182 pspp_sheet_view_column_set_sort_indicator (column, FALSE);
1187 pspp_sheet_view_column_set_sort_indicator (column, FALSE);
1192 pspp_sheet_view_column_sort (PsppSheetViewColumn *tree_column,
1195 gint sort_column_id;
1197 gboolean has_sort_column;
1198 gboolean has_default_sort_func;
1200 g_return_if_fail (tree_column->tree_view != NULL);
1203 gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model),
1206 has_default_sort_func =
1207 gtk_tree_sortable_has_default_sort_func (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model));
1209 if (has_sort_column &&
1210 sort_column_id == tree_column->sort_column_id)
1212 if (order == GTK_SORT_ASCENDING)
1213 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model),
1214 tree_column->sort_column_id,
1215 GTK_SORT_DESCENDING);
1216 else if (order == GTK_SORT_DESCENDING && has_default_sort_func)
1217 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model),
1218 GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
1219 GTK_SORT_ASCENDING);
1221 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model),
1222 tree_column->sort_column_id,
1223 GTK_SORT_ASCENDING);
1227 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->model),
1228 tree_column->sort_column_id,
1229 GTK_SORT_ASCENDING);
1235 pspp_sheet_view_column_setup_sort_column_id_callback (PsppSheetViewColumn *tree_column)
1237 GtkTreeModel *model;
1239 if (tree_column->tree_view == NULL)
1242 model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (tree_column->tree_view));
1247 if (GTK_IS_TREE_SORTABLE (model) &&
1248 tree_column->sort_column_id != -1)
1250 gint real_sort_column_id;
1251 GtkSortType real_order;
1253 if (tree_column->sort_column_changed_signal == 0)
1254 tree_column->sort_column_changed_signal =
1255 g_signal_connect (model, "sort-column-changed",
1256 G_CALLBACK (pspp_sheet_view_model_sort_column_changed),
1259 if (gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model),
1260 &real_sort_column_id,
1262 (real_sort_column_id == tree_column->sort_column_id))
1264 pspp_sheet_view_column_set_sort_indicator (tree_column, TRUE);
1265 pspp_sheet_view_column_set_sort_order (tree_column, real_order);
1269 pspp_sheet_view_column_set_sort_indicator (tree_column, FALSE);
1275 /* Exported Private Functions.
1276 * These should only be called by gtktreeview.c or gtktreeviewcolumn.c
1280 _pspp_sheet_view_column_realize_button (PsppSheetViewColumn *column)
1282 PsppSheetView *tree_view;
1284 guint attributes_mask;
1287 tree_view = (PsppSheetView *)column->tree_view;
1288 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_view)) == GTK_TEXT_DIR_RTL);
1290 g_return_if_fail (PSPP_IS_SHEET_VIEW (tree_view));
1291 g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (tree_view)));
1292 g_return_if_fail (tree_view->priv->header_window != NULL);
1293 g_return_if_fail (column->button != NULL);
1295 gtk_widget_set_parent_window (column->button, tree_view->priv->header_window);
1297 if (column->visible)
1298 gtk_widget_show (column->button);
1300 attr.window_type = GDK_WINDOW_CHILD;
1301 attr.wclass = GDK_INPUT_ONLY;
1302 attr.visual = gtk_widget_get_visual (GTK_WIDGET (tree_view));
1303 attr.colormap = gtk_widget_get_colormap (GTK_WIDGET (tree_view));
1304 attr.event_mask = gtk_widget_get_events (GTK_WIDGET (tree_view)) |
1305 (GDK_BUTTON_PRESS_MASK |
1306 GDK_BUTTON_RELEASE_MASK |
1307 GDK_POINTER_MOTION_MASK |
1308 GDK_POINTER_MOTION_HINT_MASK |
1309 GDK_KEY_PRESS_MASK);
1310 attributes_mask = GDK_WA_CURSOR | GDK_WA_X | GDK_WA_Y;
1311 attr.cursor = gdk_cursor_new_for_display (gdk_drawable_get_display (tree_view->priv->header_window),
1312 GDK_SB_H_DOUBLE_ARROW);
1314 attr.width = TREE_VIEW_DRAG_WIDTH;
1315 attr.height = tree_view->priv->header_height;
1317 attr.x = (column->button->allocation.x + (rtl ? 0 : column->button->allocation.width)) - TREE_VIEW_DRAG_WIDTH / 2;
1318 column->window = gdk_window_new (tree_view->priv->header_window,
1319 &attr, attributes_mask);
1320 gdk_window_set_user_data (column->window, tree_view);
1322 pspp_sheet_view_column_update_button (column);
1324 gdk_cursor_unref (attr.cursor);
1328 _pspp_sheet_view_column_unrealize_button (PsppSheetViewColumn *column)
1330 g_return_if_fail (column != NULL);
1331 g_return_if_fail (column->window != NULL);
1333 gdk_window_set_user_data (column->window, NULL);
1334 gdk_window_destroy (column->window);
1335 column->window = NULL;
1339 _pspp_sheet_view_column_unset_model (PsppSheetViewColumn *column,
1340 GtkTreeModel *old_model)
1342 if (column->sort_column_changed_signal)
1344 g_signal_handler_disconnect (old_model,
1345 column->sort_column_changed_signal);
1346 column->sort_column_changed_signal = 0;
1348 pspp_sheet_view_column_set_sort_indicator (column, FALSE);
1352 _pspp_sheet_view_column_set_tree_view (PsppSheetViewColumn *column,
1353 PsppSheetView *tree_view)
1355 g_assert (column->tree_view == NULL);
1357 column->tree_view = GTK_WIDGET (tree_view);
1358 pspp_sheet_view_column_create_button (column);
1360 column->property_changed_signal =
1361 g_signal_connect_swapped (tree_view,
1363 G_CALLBACK (pspp_sheet_view_column_setup_sort_column_id_callback),
1366 pspp_sheet_view_column_setup_sort_column_id_callback (column);
1370 _pspp_sheet_view_column_unset_tree_view (PsppSheetViewColumn *column)
1372 if (column->tree_view && column->button)
1374 gtk_container_remove (GTK_CONTAINER (column->tree_view), column->button);
1376 if (column->property_changed_signal)
1378 g_signal_handler_disconnect (column->tree_view, column->property_changed_signal);
1379 column->property_changed_signal = 0;
1382 if (column->sort_column_changed_signal)
1384 g_signal_handler_disconnect (pspp_sheet_view_get_model (PSPP_SHEET_VIEW (column->tree_view)),
1385 column->sort_column_changed_signal);
1386 column->sort_column_changed_signal = 0;
1389 column->tree_view = NULL;
1390 column->button = NULL;
1394 _pspp_sheet_view_column_has_editable_cell (PsppSheetViewColumn *column)
1398 for (list = column->cell_list; list; list = list->next)
1399 if (((PsppSheetViewColumnCellInfo *)list->data)->cell->mode ==
1400 GTK_CELL_RENDERER_MODE_EDITABLE)
1406 /* gets cell being edited */
1408 _pspp_sheet_view_column_get_edited_cell (PsppSheetViewColumn *column)
1412 for (list = column->cell_list; list; list = list->next)
1413 if (((PsppSheetViewColumnCellInfo *)list->data)->in_editing_mode)
1414 return ((PsppSheetViewColumnCellInfo *)list->data)->cell;
1420 _pspp_sheet_view_column_count_special_cells (PsppSheetViewColumn *column)
1425 for (list = column->cell_list; list; list = list->next)
1427 PsppSheetViewColumnCellInfo *cellinfo = list->data;
1429 if ((cellinfo->cell->mode == GTK_CELL_RENDERER_MODE_EDITABLE ||
1430 cellinfo->cell->mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE) &&
1431 cellinfo->cell->visible)
1439 _pspp_sheet_view_column_get_cell_at_pos (PsppSheetViewColumn *column,
1445 list = pspp_sheet_view_column_cell_first (column);
1446 for (; list; list = pspp_sheet_view_column_cell_next (column, list))
1448 PsppSheetViewColumnCellInfo *cellinfo = list->data;
1449 if (current_x <= x && x <= current_x + cellinfo->real_width)
1450 return cellinfo->cell;
1451 current_x += cellinfo->real_width;
1457 /* Public Functions */
1461 * pspp_sheet_view_column_new:
1463 * Creates a new #PsppSheetViewColumn.
1465 * Return value: A newly created #PsppSheetViewColumn.
1467 PsppSheetViewColumn *
1468 pspp_sheet_view_column_new (void)
1470 PsppSheetViewColumn *tree_column;
1472 tree_column = g_object_new (PSPP_TYPE_SHEET_VIEW_COLUMN, NULL);
1478 * pspp_sheet_view_column_new_with_attributes:
1479 * @title: The title to set the header to.
1480 * @cell: The #GtkCellRenderer.
1481 * @Varargs: A %NULL-terminated list of attributes.
1483 * Creates a new #PsppSheetViewColumn with a number of default values. This is
1484 * equivalent to calling pspp_sheet_view_column_set_title(),
1485 * pspp_sheet_view_column_pack_start(), and
1486 * pspp_sheet_view_column_set_attributes() on the newly created #PsppSheetViewColumn.
1488 * Here's a simple example:
1490 * enum { TEXT_COLUMN, COLOR_COLUMN, N_COLUMNS };
1493 * PsppSheetViewColumn *column;
1494 * GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
1496 * column = pspp_sheet_view_column_new_with_attributes ("Title",
1498 * "text", TEXT_COLUMN,
1499 * "foreground", COLOR_COLUMN,
1504 * Return value: A newly created #PsppSheetViewColumn.
1506 PsppSheetViewColumn *
1507 pspp_sheet_view_column_new_with_attributes (const gchar *title,
1508 GtkCellRenderer *cell,
1511 PsppSheetViewColumn *retval;
1514 retval = pspp_sheet_view_column_new ();
1516 pspp_sheet_view_column_set_title (retval, title);
1517 pspp_sheet_view_column_pack_start (retval, cell, TRUE);
1519 va_start (args, cell);
1520 pspp_sheet_view_column_set_attributesv (retval, cell, args);
1526 static PsppSheetViewColumnCellInfo *
1527 pspp_sheet_view_column_get_cell_info (PsppSheetViewColumn *tree_column,
1528 GtkCellRenderer *cell_renderer)
1531 for (list = tree_column->cell_list; list; list = list->next)
1532 if (((PsppSheetViewColumnCellInfo *)list->data)->cell == cell_renderer)
1533 return (PsppSheetViewColumnCellInfo *) list->data;
1539 * pspp_sheet_view_column_pack_start:
1540 * @tree_column: A #PsppSheetViewColumn.
1541 * @cell: The #GtkCellRenderer.
1542 * @expand: %TRUE if @cell is to be given extra space allocated to @tree_column.
1544 * Packs the @cell into the beginning of the column. If @expand is %FALSE, then
1545 * the @cell is allocated no more space than it needs. Any unused space is divided
1546 * evenly between cells for which @expand is %TRUE.
1549 pspp_sheet_view_column_pack_start (PsppSheetViewColumn *tree_column,
1550 GtkCellRenderer *cell,
1553 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (tree_column), cell, expand);
1557 * pspp_sheet_view_column_pack_end:
1558 * @tree_column: A #PsppSheetViewColumn.
1559 * @cell: The #GtkCellRenderer.
1560 * @expand: %TRUE if @cell is to be given extra space allocated to @tree_column.
1562 * Adds the @cell to end of the column. If @expand is %FALSE, then the @cell
1563 * is allocated no more space than it needs. Any unused space is divided
1564 * evenly between cells for which @expand is %TRUE.
1567 pspp_sheet_view_column_pack_end (PsppSheetViewColumn *tree_column,
1568 GtkCellRenderer *cell,
1571 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (tree_column), cell, expand);
1575 * pspp_sheet_view_column_clear:
1576 * @tree_column: A #PsppSheetViewColumn
1578 * Unsets all the mappings on all renderers on the @tree_column.
1581 pspp_sheet_view_column_clear (PsppSheetViewColumn *tree_column)
1583 gtk_cell_layout_clear (GTK_CELL_LAYOUT (tree_column));
1587 pspp_sheet_view_column_cell_layout_get_cells (GtkCellLayout *layout)
1589 PsppSheetViewColumn *tree_column = PSPP_SHEET_VIEW_COLUMN (layout);
1590 GList *retval = NULL, *list;
1592 g_return_val_if_fail (tree_column != NULL, NULL);
1594 for (list = tree_column->cell_list; list; list = list->next)
1596 PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *)list->data;
1598 retval = g_list_append (retval, info->cell);
1605 * pspp_sheet_view_column_get_cell_renderers:
1606 * @tree_column: A #PsppSheetViewColumn
1608 * Returns a newly-allocated #GList of all the cell renderers in the column,
1609 * in no particular order. The list must be freed with g_list_free().
1611 * Return value: A list of #GtkCellRenderers
1613 * Deprecated: 2.18: use gtk_cell_layout_get_cells() instead.
1616 pspp_sheet_view_column_get_cell_renderers (PsppSheetViewColumn *tree_column)
1618 return pspp_sheet_view_column_cell_layout_get_cells (GTK_CELL_LAYOUT (tree_column));
1622 * pspp_sheet_view_column_add_attribute:
1623 * @tree_column: A #PsppSheetViewColumn.
1624 * @cell_renderer: the #GtkCellRenderer to set attributes on
1625 * @attribute: An attribute on the renderer
1626 * @column: The column position on the model to get the attribute from.
1628 * Adds an attribute mapping to the list in @tree_column. The @column is the
1629 * column of the model to get a value from, and the @attribute is the
1630 * parameter on @cell_renderer to be set from the value. So for example
1631 * if column 2 of the model contains strings, you could have the
1632 * "text" attribute of a #GtkCellRendererText get its values from
1636 pspp_sheet_view_column_add_attribute (PsppSheetViewColumn *tree_column,
1637 GtkCellRenderer *cell_renderer,
1638 const gchar *attribute,
1641 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (tree_column),
1642 cell_renderer, attribute, column);
1646 pspp_sheet_view_column_set_attributesv (PsppSheetViewColumn *tree_column,
1647 GtkCellRenderer *cell_renderer,
1653 attribute = va_arg (args, gchar *);
1655 pspp_sheet_view_column_clear_attributes (tree_column, cell_renderer);
1657 while (attribute != NULL)
1659 column = va_arg (args, gint);
1660 pspp_sheet_view_column_add_attribute (tree_column, cell_renderer, attribute, column);
1661 attribute = va_arg (args, gchar *);
1666 * pspp_sheet_view_column_set_attributes:
1667 * @tree_column: A #PsppSheetViewColumn.
1668 * @cell_renderer: the #GtkCellRenderer we're setting the attributes of
1669 * @Varargs: A %NULL-terminated list of attributes.
1671 * Sets the attributes in the list as the attributes of @tree_column.
1672 * The attributes should be in attribute/column order, as in
1673 * pspp_sheet_view_column_add_attribute(). All existing attributes
1674 * are removed, and replaced with the new attributes.
1677 pspp_sheet_view_column_set_attributes (PsppSheetViewColumn *tree_column,
1678 GtkCellRenderer *cell_renderer,
1683 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
1684 g_return_if_fail (GTK_IS_CELL_RENDERER (cell_renderer));
1685 g_return_if_fail (pspp_sheet_view_column_get_cell_info (tree_column, cell_renderer));
1687 va_start (args, cell_renderer);
1688 pspp_sheet_view_column_set_attributesv (tree_column, cell_renderer, args);
1694 * pspp_sheet_view_column_set_cell_data_func:
1695 * @tree_column: A #PsppSheetViewColumn
1696 * @cell_renderer: A #GtkCellRenderer
1697 * @func: The #PsppSheetViewColumnFunc to use.
1698 * @func_data: The user data for @func.
1699 * @destroy: The destroy notification for @func_data
1701 * Sets the #PsppSheetViewColumnFunc to use for the column. This
1702 * function is used instead of the standard attributes mapping for
1703 * setting the column value, and should set the value of @tree_column's
1704 * cell renderer as appropriate. @func may be %NULL to remove an
1708 pspp_sheet_view_column_set_cell_data_func (PsppSheetViewColumn *tree_column,
1709 GtkCellRenderer *cell_renderer,
1710 PsppSheetCellDataFunc func,
1712 GDestroyNotify destroy)
1714 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (tree_column),
1716 (GtkCellLayoutDataFunc)func,
1717 func_data, destroy);
1722 * pspp_sheet_view_column_clear_attributes:
1723 * @tree_column: a #PsppSheetViewColumn
1724 * @cell_renderer: a #GtkCellRenderer to clear the attribute mapping on.
1726 * Clears all existing attributes previously set with
1727 * pspp_sheet_view_column_set_attributes().
1730 pspp_sheet_view_column_clear_attributes (PsppSheetViewColumn *tree_column,
1731 GtkCellRenderer *cell_renderer)
1733 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (tree_column),
1738 * pspp_sheet_view_column_set_spacing:
1739 * @tree_column: A #PsppSheetViewColumn.
1740 * @spacing: distance between cell renderers in pixels.
1742 * Sets the spacing field of @tree_column, which is the number of pixels to
1743 * place between cell renderers packed into it.
1746 pspp_sheet_view_column_set_spacing (PsppSheetViewColumn *tree_column,
1749 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
1750 g_return_if_fail (spacing >= 0);
1752 if (tree_column->spacing == spacing)
1755 tree_column->spacing = spacing;
1756 if (tree_column->tree_view)
1757 _pspp_sheet_view_column_cell_set_dirty (tree_column, TRUE);
1761 * pspp_sheet_view_column_get_spacing:
1762 * @tree_column: A #PsppSheetViewColumn.
1764 * Returns the spacing of @tree_column.
1766 * Return value: the spacing of @tree_column.
1769 pspp_sheet_view_column_get_spacing (PsppSheetViewColumn *tree_column)
1771 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
1773 return tree_column->spacing;
1776 /* Options for manipulating the columns */
1779 * pspp_sheet_view_column_set_visible:
1780 * @tree_column: A #PsppSheetViewColumn.
1781 * @visible: %TRUE if the @tree_column is visible.
1783 * Sets the visibility of @tree_column.
1786 pspp_sheet_view_column_set_visible (PsppSheetViewColumn *tree_column,
1789 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
1791 visible = !! visible;
1793 if (tree_column->visible == visible)
1796 tree_column->visible = visible;
1798 if (tree_column->visible)
1799 _pspp_sheet_view_column_cell_set_dirty (tree_column, TRUE);
1801 pspp_sheet_view_column_update_button (tree_column);
1802 g_object_notify (G_OBJECT (tree_column), "visible");
1806 * pspp_sheet_view_column_get_visible:
1807 * @tree_column: A #PsppSheetViewColumn.
1809 * Returns %TRUE if @tree_column is visible.
1811 * Return value: whether the column is visible or not. If it is visible, then
1812 * the tree will show the column.
1815 pspp_sheet_view_column_get_visible (PsppSheetViewColumn *tree_column)
1817 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
1819 return tree_column->visible;
1823 * pspp_sheet_view_column_set_resizable:
1824 * @tree_column: A #PsppSheetViewColumn
1825 * @resizable: %TRUE, if the column can be resized
1827 * If @resizable is %TRUE, then the user can explicitly resize the column by
1828 * grabbing the outer edge of the column button. If resizable is %TRUE and
1829 * sizing mode of the column is #PSPP_SHEET_VIEW_COLUMN_AUTOSIZE, then the sizing
1830 * mode is changed to #PSPP_SHEET_VIEW_COLUMN_GROW_ONLY.
1833 pspp_sheet_view_column_set_resizable (PsppSheetViewColumn *tree_column,
1836 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
1838 resizable = !! resizable;
1840 if (tree_column->resizable == resizable)
1843 tree_column->resizable = resizable;
1845 if (resizable && tree_column->column_type == PSPP_SHEET_VIEW_COLUMN_AUTOSIZE)
1846 pspp_sheet_view_column_set_sizing (tree_column, PSPP_SHEET_VIEW_COLUMN_GROW_ONLY);
1848 pspp_sheet_view_column_update_button (tree_column);
1850 g_object_notify (G_OBJECT (tree_column), "resizable");
1854 * pspp_sheet_view_column_get_resizable:
1855 * @tree_column: A #PsppSheetViewColumn
1857 * Returns %TRUE if the @tree_column can be resized by the end user.
1859 * Return value: %TRUE, if the @tree_column can be resized.
1862 pspp_sheet_view_column_get_resizable (PsppSheetViewColumn *tree_column)
1864 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
1866 return tree_column->resizable;
1871 * pspp_sheet_view_column_set_sizing:
1872 * @tree_column: A #PsppSheetViewColumn.
1873 * @type: The #PsppSheetViewColumnSizing.
1875 * Sets the growth behavior of @tree_column to @type.
1878 pspp_sheet_view_column_set_sizing (PsppSheetViewColumn *tree_column,
1879 PsppSheetViewColumnSizing type)
1881 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
1883 if (type == tree_column->column_type)
1886 if (type == PSPP_SHEET_VIEW_COLUMN_AUTOSIZE)
1887 pspp_sheet_view_column_set_resizable (tree_column, FALSE);
1890 /* I was clearly on crack when I wrote this. I'm not sure what's supposed to
1891 * be below so I'll leave it until I figure it out.
1893 if (tree_column->column_type == PSPP_SHEET_VIEW_COLUMN_AUTOSIZE &&
1894 tree_column->requested_width != -1)
1896 pspp_sheet_view_column_set_sizing (tree_column, tree_column->requested_width);
1899 tree_column->column_type = type;
1901 pspp_sheet_view_column_update_button (tree_column);
1903 g_object_notify (G_OBJECT (tree_column), "sizing");
1907 * pspp_sheet_view_column_get_sizing:
1908 * @tree_column: A #PsppSheetViewColumn.
1910 * Returns the current type of @tree_column.
1912 * Return value: The type of @tree_column.
1914 PsppSheetViewColumnSizing
1915 pspp_sheet_view_column_get_sizing (PsppSheetViewColumn *tree_column)
1917 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
1919 return tree_column->column_type;
1923 * pspp_sheet_view_column_get_width:
1924 * @tree_column: A #PsppSheetViewColumn.
1926 * Returns the current size of @tree_column in pixels.
1928 * Return value: The current width of @tree_column.
1931 pspp_sheet_view_column_get_width (PsppSheetViewColumn *tree_column)
1933 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
1935 return tree_column->width;
1939 * pspp_sheet_view_column_set_fixed_width:
1940 * @tree_column: A #PsppSheetViewColumn.
1941 * @fixed_width: The size to set @tree_column to. Must be greater than 0.
1943 * Sets the size of the column in pixels. This is meaningful only if the sizing
1944 * type is #PSPP_SHEET_VIEW_COLUMN_FIXED. The size of the column is clamped to
1945 * the min/max width for the column. Please note that the min/max width of the
1946 * column doesn't actually affect the "fixed_width" property of the widget, just
1947 * the actual size when displayed.
1950 pspp_sheet_view_column_set_fixed_width (PsppSheetViewColumn *tree_column,
1953 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
1954 g_return_if_fail (fixed_width > 0);
1956 tree_column->fixed_width = fixed_width;
1957 tree_column->use_resized_width = FALSE;
1959 if (tree_column->tree_view &&
1960 gtk_widget_get_realized (tree_column->tree_view) &&
1961 tree_column->column_type == PSPP_SHEET_VIEW_COLUMN_FIXED)
1963 gtk_widget_queue_resize (tree_column->tree_view);
1966 g_object_notify (G_OBJECT (tree_column), "fixed-width");
1970 * pspp_sheet_view_column_get_fixed_width:
1971 * @tree_column: a #PsppSheetViewColumn
1973 * Gets the fixed width of the column. This value is only meaning may not be
1974 * the actual width of the column on the screen, just what is requested.
1976 * Return value: the fixed width of the column
1979 pspp_sheet_view_column_get_fixed_width (PsppSheetViewColumn *tree_column)
1981 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
1983 return tree_column->fixed_width;
1987 * pspp_sheet_view_column_set_min_width:
1988 * @tree_column: A #PsppSheetViewColumn.
1989 * @min_width: The minimum width of the column in pixels, or -1.
1991 * Sets the minimum width of the @tree_column. If @min_width is -1, then the
1992 * minimum width is unset.
1995 pspp_sheet_view_column_set_min_width (PsppSheetViewColumn *tree_column,
1998 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
1999 g_return_if_fail (min_width >= -1);
2001 if (min_width == tree_column->min_width)
2004 if (tree_column->visible &&
2005 tree_column->tree_view != NULL &&
2006 gtk_widget_get_realized (tree_column->tree_view))
2008 if (min_width > tree_column->width)
2009 gtk_widget_queue_resize (tree_column->tree_view);
2012 tree_column->min_width = min_width;
2013 g_object_freeze_notify (G_OBJECT (tree_column));
2014 if (tree_column->max_width != -1 && tree_column->max_width < min_width)
2016 tree_column->max_width = min_width;
2017 g_object_notify (G_OBJECT (tree_column), "max-width");
2019 g_object_notify (G_OBJECT (tree_column), "min-width");
2020 g_object_thaw_notify (G_OBJECT (tree_column));
2022 if (tree_column->column_type == PSPP_SHEET_VIEW_COLUMN_AUTOSIZE)
2023 _pspp_sheet_view_column_autosize (PSPP_SHEET_VIEW (tree_column->tree_view),
2028 * pspp_sheet_view_column_get_min_width:
2029 * @tree_column: A #PsppSheetViewColumn.
2031 * Returns the minimum width in pixels of the @tree_column, or -1 if no minimum
2034 * Return value: The minimum width of the @tree_column.
2037 pspp_sheet_view_column_get_min_width (PsppSheetViewColumn *tree_column)
2039 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), -1);
2041 return tree_column->min_width;
2045 * pspp_sheet_view_column_set_max_width:
2046 * @tree_column: A #PsppSheetViewColumn.
2047 * @max_width: The maximum width of the column in pixels, or -1.
2049 * Sets the maximum width of the @tree_column. If @max_width is -1, then the
2050 * maximum width is unset. Note, the column can actually be wider than max
2051 * width if it's the last column in a view. In this case, the column expands to
2052 * fill any extra space.
2055 pspp_sheet_view_column_set_max_width (PsppSheetViewColumn *tree_column,
2058 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2059 g_return_if_fail (max_width >= -1);
2061 if (max_width == tree_column->max_width)
2064 if (tree_column->visible &&
2065 tree_column->tree_view != NULL &&
2066 gtk_widget_get_realized (tree_column->tree_view))
2068 if (max_width != -1 && max_width < tree_column->width)
2069 gtk_widget_queue_resize (tree_column->tree_view);
2072 tree_column->max_width = max_width;
2073 g_object_freeze_notify (G_OBJECT (tree_column));
2074 if (max_width != -1 && max_width < tree_column->min_width)
2076 tree_column->min_width = max_width;
2077 g_object_notify (G_OBJECT (tree_column), "min-width");
2079 g_object_notify (G_OBJECT (tree_column), "max-width");
2080 g_object_thaw_notify (G_OBJECT (tree_column));
2082 if (tree_column->column_type == PSPP_SHEET_VIEW_COLUMN_AUTOSIZE)
2083 _pspp_sheet_view_column_autosize (PSPP_SHEET_VIEW (tree_column->tree_view),
2088 * pspp_sheet_view_column_get_max_width:
2089 * @tree_column: A #PsppSheetViewColumn.
2091 * Returns the maximum width in pixels of the @tree_column, or -1 if no maximum
2094 * Return value: The maximum width of the @tree_column.
2097 pspp_sheet_view_column_get_max_width (PsppSheetViewColumn *tree_column)
2099 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), -1);
2101 return tree_column->max_width;
2105 * pspp_sheet_view_column_clicked:
2106 * @tree_column: a #PsppSheetViewColumn
2108 * Emits the "clicked" signal on the column. This function will only work if
2109 * @tree_column is clickable.
2112 pspp_sheet_view_column_clicked (PsppSheetViewColumn *tree_column)
2114 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2116 if (tree_column->visible &&
2117 tree_column->button &&
2118 tree_column->clickable)
2119 gtk_button_clicked (GTK_BUTTON (tree_column->button));
2123 * pspp_sheet_view_column_set_title:
2124 * @tree_column: A #PsppSheetViewColumn.
2125 * @title: The title of the @tree_column.
2127 * Sets the title of the @tree_column. If a custom widget has been set, then
2128 * this value is ignored.
2131 pspp_sheet_view_column_set_title (PsppSheetViewColumn *tree_column,
2136 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2138 new_title = g_strdup (title);
2139 g_free (tree_column->title);
2140 tree_column->title = new_title;
2142 pspp_sheet_view_column_update_button (tree_column);
2143 g_object_notify (G_OBJECT (tree_column), "title");
2147 * pspp_sheet_view_column_get_title:
2148 * @tree_column: A #PsppSheetViewColumn.
2150 * Returns the title of the widget.
2152 * Return value: the title of the column. This string should not be
2153 * modified or freed.
2155 G_CONST_RETURN gchar *
2156 pspp_sheet_view_column_get_title (PsppSheetViewColumn *tree_column)
2158 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), NULL);
2160 return tree_column->title;
2164 * pspp_sheet_view_column_set_expand:
2165 * @tree_column: A #PsppSheetViewColumn
2166 * @expand: %TRUE if the column should take available extra space, %FALSE if not
2168 * Sets the column to take available extra space. This space is shared equally
2169 * amongst all columns that have the expand set to %TRUE. If no column has this
2170 * option set, then the last column gets all extra space. By default, every
2171 * column is created with this %FALSE.
2176 pspp_sheet_view_column_set_expand (PsppSheetViewColumn *tree_column,
2179 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2181 expand = expand?TRUE:FALSE;
2182 if (tree_column->expand == expand)
2184 tree_column->expand = expand;
2186 if (tree_column->visible &&
2187 tree_column->tree_view != NULL &&
2188 gtk_widget_get_realized (tree_column->tree_view))
2190 /* We want to continue using the original width of the
2191 * column that includes additional space added by the user
2192 * resizing the columns and possibly extra (expanded) space, which
2193 * are not included in the resized width.
2195 tree_column->use_resized_width = FALSE;
2197 gtk_widget_queue_resize (tree_column->tree_view);
2200 g_object_notify (G_OBJECT (tree_column), "expand");
2204 * pspp_sheet_view_column_get_expand:
2205 * @tree_column: a #PsppSheetViewColumn
2207 * Return %TRUE if the column expands to take any available space.
2209 * Return value: %TRUE, if the column expands
2214 pspp_sheet_view_column_get_expand (PsppSheetViewColumn *tree_column)
2216 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2218 return tree_column->expand;
2222 * pspp_sheet_view_column_set_clickable:
2223 * @tree_column: A #PsppSheetViewColumn.
2224 * @clickable: %TRUE if the header is active.
2226 * Sets the header to be active if @active is %TRUE. When the header is active,
2227 * then it can take keyboard focus, and can be clicked.
2230 pspp_sheet_view_column_set_clickable (PsppSheetViewColumn *tree_column,
2233 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2235 clickable = !! clickable;
2236 if (tree_column->clickable == clickable)
2239 tree_column->clickable = clickable;
2240 pspp_sheet_view_column_update_button (tree_column);
2241 g_object_notify (G_OBJECT (tree_column), "clickable");
2245 * pspp_sheet_view_column_get_clickable:
2246 * @tree_column: a #PsppSheetViewColumn
2248 * Returns %TRUE if the user can click on the header for the column.
2250 * Return value: %TRUE if user can click the column header.
2253 pspp_sheet_view_column_get_clickable (PsppSheetViewColumn *tree_column)
2255 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2257 return tree_column->clickable;
2261 * pspp_sheet_view_column_set_widget:
2262 * @tree_column: A #PsppSheetViewColumn.
2263 * @widget: (allow-none): A child #GtkWidget, or %NULL.
2265 * Sets the widget in the header to be @widget. If widget is %NULL, then the
2266 * header button is set with a #GtkLabel set to the title of @tree_column.
2269 pspp_sheet_view_column_set_widget (PsppSheetViewColumn *tree_column,
2272 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2273 g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
2276 g_object_ref_sink (widget);
2278 if (tree_column->child)
2279 g_object_unref (tree_column->child);
2281 tree_column->child = widget;
2282 pspp_sheet_view_column_update_button (tree_column);
2283 g_object_notify (G_OBJECT (tree_column), "widget");
2287 * pspp_sheet_view_column_get_widget:
2288 * @tree_column: A #PsppSheetViewColumn.
2290 * Returns the #GtkWidget in the button on the column header. If a custom
2291 * widget has not been set then %NULL is returned.
2293 * Return value: The #GtkWidget in the column header, or %NULL
2296 pspp_sheet_view_column_get_widget (PsppSheetViewColumn *tree_column)
2298 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), NULL);
2300 return tree_column->child;
2304 * pspp_sheet_view_column_set_alignment:
2305 * @tree_column: A #PsppSheetViewColumn.
2306 * @xalign: The alignment, which is between [0.0 and 1.0] inclusive.
2308 * Sets the alignment of the title or custom widget inside the column header.
2309 * The alignment determines its location inside the button -- 0.0 for left, 0.5
2310 * for center, 1.0 for right.
2313 pspp_sheet_view_column_set_alignment (PsppSheetViewColumn *tree_column,
2316 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2318 xalign = CLAMP (xalign, 0.0, 1.0);
2320 if (tree_column->xalign == xalign)
2323 tree_column->xalign = xalign;
2324 pspp_sheet_view_column_update_button (tree_column);
2325 g_object_notify (G_OBJECT (tree_column), "alignment");
2329 * pspp_sheet_view_column_get_alignment:
2330 * @tree_column: A #PsppSheetViewColumn.
2332 * Returns the current x alignment of @tree_column. This value can range
2333 * between 0.0 and 1.0.
2335 * Return value: The current alignent of @tree_column.
2338 pspp_sheet_view_column_get_alignment (PsppSheetViewColumn *tree_column)
2340 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0.5);
2342 return tree_column->xalign;
2346 * pspp_sheet_view_column_set_reorderable:
2347 * @tree_column: A #PsppSheetViewColumn
2348 * @reorderable: %TRUE, if the column can be reordered.
2350 * If @reorderable is %TRUE, then the column can be reordered by the end user
2351 * dragging the header.
2354 pspp_sheet_view_column_set_reorderable (PsppSheetViewColumn *tree_column,
2355 gboolean reorderable)
2357 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2360 pspp_sheet_view_column_set_clickable (tree_column, TRUE);*/
2362 if (tree_column->reorderable == (reorderable?TRUE:FALSE))
2365 tree_column->reorderable = (reorderable?TRUE:FALSE);
2366 pspp_sheet_view_column_update_button (tree_column);
2367 g_object_notify (G_OBJECT (tree_column), "reorderable");
2371 * pspp_sheet_view_column_get_reorderable:
2372 * @tree_column: A #PsppSheetViewColumn
2374 * Returns %TRUE if the @tree_column can be reordered by the user.
2376 * Return value: %TRUE if the @tree_column can be reordered by the user.
2379 pspp_sheet_view_column_get_reorderable (PsppSheetViewColumn *tree_column)
2381 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2383 return tree_column->reorderable;
2388 * pspp_sheet_view_column_set_sort_column_id:
2389 * @tree_column: a #PsppSheetViewColumn
2390 * @sort_column_id: The @sort_column_id of the model to sort on.
2392 * Sets the logical @sort_column_id that this column sorts on when this column
2393 * is selected for sorting. Doing so makes the column header clickable.
2396 pspp_sheet_view_column_set_sort_column_id (PsppSheetViewColumn *tree_column,
2397 gint sort_column_id)
2399 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2400 g_return_if_fail (sort_column_id >= -1);
2402 if (tree_column->sort_column_id == sort_column_id)
2405 tree_column->sort_column_id = sort_column_id;
2407 /* Handle unsetting the id */
2408 if (sort_column_id == -1)
2410 GtkTreeModel *model = pspp_sheet_view_get_model (PSPP_SHEET_VIEW (tree_column->tree_view));
2412 if (tree_column->sort_clicked_signal)
2414 g_signal_handler_disconnect (tree_column, tree_column->sort_clicked_signal);
2415 tree_column->sort_clicked_signal = 0;
2418 if (tree_column->sort_column_changed_signal)
2420 g_signal_handler_disconnect (model, tree_column->sort_column_changed_signal);
2421 tree_column->sort_column_changed_signal = 0;
2424 pspp_sheet_view_column_set_sort_order (tree_column, GTK_SORT_ASCENDING);
2425 pspp_sheet_view_column_set_sort_indicator (tree_column, FALSE);
2426 pspp_sheet_view_column_set_clickable (tree_column, FALSE);
2427 g_object_notify (G_OBJECT (tree_column), "sort-column-id");
2431 pspp_sheet_view_column_set_clickable (tree_column, TRUE);
2433 if (! tree_column->sort_clicked_signal)
2434 tree_column->sort_clicked_signal = g_signal_connect (tree_column,
2436 G_CALLBACK (pspp_sheet_view_column_sort),
2439 pspp_sheet_view_column_setup_sort_column_id_callback (tree_column);
2440 g_object_notify (G_OBJECT (tree_column), "sort-column-id");
2444 * pspp_sheet_view_column_get_sort_column_id:
2445 * @tree_column: a #PsppSheetViewColumn
2447 * Gets the logical @sort_column_id that the model sorts on when this
2448 * column is selected for sorting.
2449 * See pspp_sheet_view_column_set_sort_column_id().
2451 * Return value: the current @sort_column_id for this column, or -1 if
2452 * this column can't be used for sorting.
2455 pspp_sheet_view_column_get_sort_column_id (PsppSheetViewColumn *tree_column)
2457 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
2459 return tree_column->sort_column_id;
2463 * pspp_sheet_view_column_set_sort_indicator:
2464 * @tree_column: a #PsppSheetViewColumn
2465 * @setting: %TRUE to display an indicator that the column is sorted
2467 * Call this function with a @setting of %TRUE to display an arrow in
2468 * the header button indicating the column is sorted. Call
2469 * pspp_sheet_view_column_set_sort_order() to change the direction of
2474 pspp_sheet_view_column_set_sort_indicator (PsppSheetViewColumn *tree_column,
2477 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2479 setting = setting != FALSE;
2481 if (setting == tree_column->show_sort_indicator)
2484 tree_column->show_sort_indicator = setting;
2485 pspp_sheet_view_column_update_button (tree_column);
2486 g_object_notify (G_OBJECT (tree_column), "sort-indicator");
2490 * pspp_sheet_view_column_get_sort_indicator:
2491 * @tree_column: a #PsppSheetViewColumn
2493 * Gets the value set by pspp_sheet_view_column_set_sort_indicator().
2495 * Return value: whether the sort indicator arrow is displayed
2498 pspp_sheet_view_column_get_sort_indicator (PsppSheetViewColumn *tree_column)
2500 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
2502 return tree_column->show_sort_indicator;
2506 * pspp_sheet_view_column_set_sort_order:
2507 * @tree_column: a #PsppSheetViewColumn
2508 * @order: sort order that the sort indicator should indicate
2510 * Changes the appearance of the sort indicator.
2512 * This <emphasis>does not</emphasis> actually sort the model. Use
2513 * pspp_sheet_view_column_set_sort_column_id() if you want automatic sorting
2514 * support. This function is primarily for custom sorting behavior, and should
2515 * be used in conjunction with gtk_tree_sortable_set_sort_column() to do
2516 * that. For custom models, the mechanism will vary.
2518 * The sort indicator changes direction to indicate normal sort or reverse sort.
2519 * Note that you must have the sort indicator enabled to see anything when
2520 * calling this function; see pspp_sheet_view_column_set_sort_indicator().
2523 pspp_sheet_view_column_set_sort_order (PsppSheetViewColumn *tree_column,
2526 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2528 if (order == tree_column->sort_order)
2531 tree_column->sort_order = order;
2532 pspp_sheet_view_column_update_button (tree_column);
2533 g_object_notify (G_OBJECT (tree_column), "sort-order");
2537 * pspp_sheet_view_column_get_sort_order:
2538 * @tree_column: a #PsppSheetViewColumn
2540 * Gets the value set by pspp_sheet_view_column_set_sort_order().
2542 * Return value: the sort order the sort indicator is indicating
2545 pspp_sheet_view_column_get_sort_order (PsppSheetViewColumn *tree_column)
2547 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), 0);
2549 return tree_column->sort_order;
2553 * pspp_sheet_view_column_cell_set_cell_data:
2554 * @tree_column: A #PsppSheetViewColumn.
2555 * @tree_model: The #GtkTreeModel to to get the cell renderers attributes from.
2556 * @iter: The #GtkTreeIter to to get the cell renderer's attributes from.
2557 * @is_expander: %TRUE, if the row has children
2558 * @is_expanded: %TRUE, if the row has visible children
2560 * Sets the cell renderer based on the @tree_model and @iter. That is, for
2561 * every attribute mapping in @tree_column, it will get a value from the set
2562 * column on the @iter, and use that value to set the attribute on the cell
2563 * renderer. This is used primarily by the #PsppSheetView.
2566 pspp_sheet_view_column_cell_set_cell_data (PsppSheetViewColumn *tree_column,
2567 GtkTreeModel *tree_model,
2569 gboolean is_expander,
2570 gboolean is_expanded)
2573 GValue value = { 0, };
2576 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2578 if (tree_model == NULL)
2581 for (cell_list = tree_column->cell_list; cell_list; cell_list = cell_list->next)
2583 PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) cell_list->data;
2584 GObject *cell = (GObject *) info->cell;
2586 list = info->attributes;
2588 g_object_freeze_notify (cell);
2590 if (info->cell->is_expander != is_expander)
2591 g_object_set (cell, "is-expander", is_expander, NULL);
2593 if (info->cell->is_expanded != is_expanded)
2594 g_object_set (cell, "is-expanded", is_expanded, NULL);
2596 while (list && list->next)
2598 gtk_tree_model_get_value (tree_model, iter,
2599 GPOINTER_TO_INT (list->next->data),
2601 g_object_set_property (cell, (gchar *) list->data, &value);
2602 g_value_unset (&value);
2603 list = list->next->next;
2607 (* info->func) (tree_column, info->cell, tree_model, iter, info->func_data);
2608 g_object_thaw_notify (G_OBJECT (info->cell));
2614 * pspp_sheet_view_column_cell_get_size:
2615 * @tree_column: A #PsppSheetViewColumn.
2616 * @cell_area: (allow-none): The area a cell in the column will be allocated, or %NULL
2617 * @x_offset: (allow-none): location to return x offset of a cell relative to @cell_area, or %NULL
2618 * @y_offset: (allow-none): location to return y offset of a cell relative to @cell_area, or %NULL
2619 * @width: (allow-none): location to return width needed to render a cell, or %NULL
2620 * @height: (allow-none): location to return height needed to render a cell, or %NULL
2622 * Obtains the width and height needed to render the column. This is used
2623 * primarily by the #PsppSheetView.
2626 pspp_sheet_view_column_cell_get_size (PsppSheetViewColumn *tree_column,
2627 const GdkRectangle *cell_area,
2634 gboolean first_cell = TRUE;
2635 gint focus_line_width;
2637 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
2644 gtk_widget_style_get (tree_column->tree_view, "focus-line-width", &focus_line_width, NULL);
2646 for (list = tree_column->cell_list; list; list = list->next)
2648 PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
2650 gint new_height = 0;
2652 g_object_get (info->cell, "visible", &visible, NULL);
2654 if (visible == FALSE)
2657 if (first_cell == FALSE && width)
2658 *width += tree_column->spacing;
2660 gtk_cell_renderer_get_size (info->cell,
2661 tree_column->tree_view,
2669 * height = MAX (*height, new_height + focus_line_width * 2);
2670 info->requested_width = MAX (info->requested_width, new_width + focus_line_width * 2);
2672 * width += info->requested_width;
2677 /* rendering, event handling and rendering focus are somewhat complicated, and
2678 * quite a bit of code. Rather than duplicate them, we put them together to
2679 * keep the code in one place.
2681 * To better understand what's going on, check out
2682 * docs/tree-column-sizing.png
2691 pspp_sheet_view_column_cell_process_action (PsppSheetViewColumn *tree_column,
2693 const GdkRectangle *background_area,
2694 const GdkRectangle *cell_area,
2697 const GdkRectangle *expose_area, /* RENDER */
2698 GdkRectangle *focus_rectangle, /* FOCUS */
2699 GtkCellEditable **editable_widget, /* EVENT */
2700 GdkEvent *event, /* EVENT */
2701 gchar *path_string) /* EVENT */
2704 GdkRectangle real_cell_area;
2705 GdkRectangle real_background_area;
2706 GdkRectangle real_expose_area = *cell_area;
2708 gint expand_cell_count = 0;
2709 gint full_requested_width = 0;
2711 gint min_x, min_y, max_x, max_y;
2712 gint focus_line_width;
2714 gint horizontal_separator;
2715 gboolean cursor_row = FALSE;
2716 gboolean first_cell = TRUE;
2718 /* If we have rtl text, we need to transform our areas */
2719 GdkRectangle rtl_cell_area;
2720 GdkRectangle rtl_background_area;
2727 rtl = (gtk_widget_get_direction (GTK_WIDGET (tree_column->tree_view)) == GTK_TEXT_DIR_RTL);
2728 special_cells = _pspp_sheet_view_column_count_special_cells (tree_column);
2730 if (special_cells > 1 && action == CELL_ACTION_FOCUS)
2732 PsppSheetViewColumnCellInfo *info = NULL;
2733 gboolean found_has_focus = FALSE;
2735 /* one should have focus */
2736 for (list = tree_column->cell_list; list; list = list->next)
2739 if (info && info->has_focus)
2741 found_has_focus = TRUE;
2746 if (!found_has_focus)
2748 /* give the first one focus */
2749 info = pspp_sheet_view_column_cell_first (tree_column)->data;
2750 info->has_focus = TRUE;
2754 cursor_row = flags & GTK_CELL_RENDERER_FOCUSED;
2756 gtk_widget_style_get (GTK_WIDGET (tree_column->tree_view),
2757 "focus-line-width", &focus_line_width,
2758 "horizontal-separator", &horizontal_separator,
2761 real_cell_area = *cell_area;
2762 real_background_area = *background_area;
2765 real_cell_area.x += focus_line_width;
2766 real_cell_area.y += focus_line_width;
2767 real_cell_area.height -= 2 * focus_line_width;
2770 depth = real_background_area.width - real_cell_area.width;
2772 depth = real_cell_area.x - real_background_area.x;
2774 /* Find out how much extra space we have to allocate */
2775 for (list = tree_column->cell_list; list; list = list->next)
2777 PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *)list->data;
2779 if (! info->cell->visible)
2782 if (info->expand == TRUE)
2783 expand_cell_count ++;
2784 full_requested_width += info->requested_width;
2787 full_requested_width += tree_column->spacing;
2792 extra_space = cell_area->width - full_requested_width;
2793 if (extra_space < 0)
2795 else if (extra_space > 0 && expand_cell_count > 0)
2796 extra_space /= expand_cell_count;
2798 /* iterate list for GTK_PACK_START cells */
2799 for (list = tree_column->cell_list; list; list = list->next)
2801 PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
2803 if (info->pack == GTK_PACK_END)
2806 if (! info->cell->visible)
2809 if ((info->has_focus || special_cells == 1) && cursor_row)
2810 flags |= GTK_CELL_RENDERER_FOCUSED;
2812 flags &= ~GTK_CELL_RENDERER_FOCUSED;
2814 info->real_width = info->requested_width + (info->expand?extra_space:0);
2816 /* We constrain ourselves to only the width available */
2817 if (real_cell_area.x - focus_line_width + info->real_width > cell_area->x + cell_area->width)
2819 info->real_width = cell_area->x + cell_area->width - real_cell_area.x;
2822 if (real_cell_area.x > cell_area->x + cell_area->width)
2825 real_cell_area.width = info->real_width;
2826 real_cell_area.width -= 2 * focus_line_width;
2830 real_background_area.width = info->real_width + depth;
2834 /* fill the rest of background for the last cell */
2835 real_background_area.width = background_area->x + background_area->width - real_background_area.x;
2838 rtl_cell_area = real_cell_area;
2839 rtl_background_area = real_background_area;
2843 rtl_cell_area.x = cell_area->x + cell_area->width - (real_cell_area.x - cell_area->x) - real_cell_area.width;
2844 rtl_background_area.x = background_area->x + background_area->width - (real_background_area.x - background_area->x) - real_background_area.width;
2848 if (action == CELL_ACTION_RENDER)
2850 gtk_cell_renderer_render (info->cell,
2852 tree_column->tree_view,
2853 &rtl_background_area,
2859 else if (action == CELL_ACTION_FOCUS)
2861 gint x_offset, y_offset, width, height;
2863 gtk_cell_renderer_get_size (info->cell,
2864 tree_column->tree_view,
2866 &x_offset, &y_offset,
2869 if (special_cells > 1)
2871 if (info->has_focus)
2873 min_x = rtl_cell_area.x + x_offset;
2874 max_x = min_x + width;
2875 min_y = rtl_cell_area.y + y_offset;
2876 max_y = min_y + height;
2881 if (min_x > (rtl_cell_area.x + x_offset))
2882 min_x = rtl_cell_area.x + x_offset;
2883 if (max_x < rtl_cell_area.x + x_offset + width)
2884 max_x = rtl_cell_area.x + x_offset + width;
2885 if (min_y > (rtl_cell_area.y + y_offset))
2886 min_y = rtl_cell_area.y + y_offset;
2887 if (max_y < rtl_cell_area.y + y_offset + height)
2888 max_y = rtl_cell_area.y + y_offset + height;
2892 else if (action == CELL_ACTION_EVENT)
2894 gboolean try_event = FALSE;
2898 if (special_cells == 1)
2900 /* only 1 activatable cell -> whole column can activate */
2901 if (cell_area->x <= ((GdkEventButton *)event)->x &&
2902 cell_area->x + cell_area->width > ((GdkEventButton *)event)->x)
2905 else if (rtl_cell_area.x <= ((GdkEventButton *)event)->x &&
2906 rtl_cell_area.x + rtl_cell_area.width > ((GdkEventButton *)event)->x)
2907 /* only activate cell if the user clicked on an individual
2912 else if (special_cells > 1 && info->has_focus)
2914 else if (special_cells == 1)
2919 gboolean visible, mode;
2921 g_object_get (info->cell,
2922 "visible", &visible,
2925 if (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
2927 if (gtk_cell_renderer_activate (info->cell,
2929 tree_column->tree_view,
2931 &rtl_background_area,
2935 flags &= ~GTK_CELL_RENDERER_FOCUSED;
2939 else if (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE)
2942 gtk_cell_renderer_start_editing (info->cell,
2944 tree_column->tree_view,
2946 &rtl_background_area,
2950 if (*editable_widget != NULL)
2952 g_return_val_if_fail (GTK_IS_CELL_EDITABLE (*editable_widget), FALSE);
2953 info->in_editing_mode = TRUE;
2954 pspp_sheet_view_column_focus_cell (tree_column, info->cell);
2956 flags &= ~GTK_CELL_RENDERER_FOCUSED;
2964 flags &= ~GTK_CELL_RENDERER_FOCUSED;
2966 real_cell_area.x += (real_cell_area.width + 2 * focus_line_width + tree_column->spacing);
2967 real_background_area.x += real_background_area.width + tree_column->spacing;
2969 /* Only needed for first cell */
2973 /* iterate list for PACK_END cells */
2974 for (list = g_list_last (tree_column->cell_list); list; list = list->prev)
2976 PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
2978 if (info->pack == GTK_PACK_START)
2981 if (! info->cell->visible)
2984 if ((info->has_focus || special_cells == 1) && cursor_row)
2985 flags |= GTK_CELL_RENDERER_FOCUSED;
2987 flags &= ~GTK_CELL_RENDERER_FOCUSED;
2989 info->real_width = info->requested_width + (info->expand?extra_space:0);
2991 /* We constrain ourselves to only the width available */
2992 if (real_cell_area.x - focus_line_width + info->real_width > cell_area->x + cell_area->width)
2994 info->real_width = cell_area->x + cell_area->width - real_cell_area.x;
2997 if (real_cell_area.x > cell_area->x + cell_area->width)
3000 real_cell_area.width = info->real_width;
3001 real_cell_area.width -= 2 * focus_line_width;
3002 real_background_area.width = info->real_width + depth;
3004 rtl_cell_area = real_cell_area;
3005 rtl_background_area = real_background_area;
3008 rtl_cell_area.x = cell_area->x + cell_area->width - (real_cell_area.x - cell_area->x) - real_cell_area.width;
3009 rtl_background_area.x = background_area->x + background_area->width - (real_background_area.x - background_area->x) - real_background_area.width;
3013 if (action == CELL_ACTION_RENDER)
3015 gtk_cell_renderer_render (info->cell,
3017 tree_column->tree_view,
3018 &rtl_background_area,
3024 else if (action == CELL_ACTION_FOCUS)
3026 gint x_offset, y_offset, width, height;
3028 gtk_cell_renderer_get_size (info->cell,
3029 tree_column->tree_view,
3031 &x_offset, &y_offset,
3034 if (special_cells > 1)
3036 if (info->has_focus)
3038 min_x = rtl_cell_area.x + x_offset;
3039 max_x = min_x + width;
3040 min_y = rtl_cell_area.y + y_offset;
3041 max_y = min_y + height;
3046 if (min_x > (rtl_cell_area.x + x_offset))
3047 min_x = rtl_cell_area.x + x_offset;
3048 if (max_x < rtl_cell_area.x + x_offset + width)
3049 max_x = rtl_cell_area.x + x_offset + width;
3050 if (min_y > (rtl_cell_area.y + y_offset))
3051 min_y = rtl_cell_area.y + y_offset;
3052 if (max_y < rtl_cell_area.y + y_offset + height)
3053 max_y = rtl_cell_area.y + y_offset + height;
3057 else if (action == CELL_ACTION_EVENT)
3059 gboolean try_event = FALSE;
3063 if (special_cells == 1)
3065 /* only 1 activatable cell -> whole column can activate */
3066 if (cell_area->x <= ((GdkEventButton *)event)->x &&
3067 cell_area->x + cell_area->width > ((GdkEventButton *)event)->x)
3070 else if (rtl_cell_area.x <= ((GdkEventButton *)event)->x &&
3071 rtl_cell_area.x + rtl_cell_area.width > ((GdkEventButton *)event)->x)
3072 /* only activate cell if the user clicked on an individual
3077 else if (special_cells > 1 && info->has_focus)
3079 else if (special_cells == 1)
3084 gboolean visible, mode;
3086 g_object_get (info->cell,
3087 "visible", &visible,
3090 if (visible && mode == GTK_CELL_RENDERER_MODE_ACTIVATABLE)
3092 if (gtk_cell_renderer_activate (info->cell,
3094 tree_column->tree_view,
3096 &rtl_background_area,
3100 flags &= ~GTK_CELL_RENDERER_FOCUSED;
3104 else if (visible && mode == GTK_CELL_RENDERER_MODE_EDITABLE)
3107 gtk_cell_renderer_start_editing (info->cell,
3109 tree_column->tree_view,
3111 &rtl_background_area,
3115 if (*editable_widget != NULL)
3117 g_return_val_if_fail (GTK_IS_CELL_EDITABLE (*editable_widget), FALSE);
3118 info->in_editing_mode = TRUE;
3119 pspp_sheet_view_column_focus_cell (tree_column, info->cell);
3121 flags &= ~GTK_CELL_RENDERER_FOCUSED;
3128 flags &= ~GTK_CELL_RENDERER_FOCUSED;
3130 real_cell_area.x += (real_cell_area.width + 2 * focus_line_width + tree_column->spacing);
3131 real_background_area.x += (real_background_area.width + tree_column->spacing);
3133 /* Only needed for first cell */
3137 /* fill focus_rectangle when required */
3138 if (action == CELL_ACTION_FOCUS)
3140 if (min_x >= max_x || min_y >= max_y)
3142 *focus_rectangle = *cell_area;
3143 /* don't change the focus_rectangle, just draw it nicely inside
3148 focus_rectangle->x = min_x - focus_line_width;
3149 focus_rectangle->y = min_y - focus_line_width;
3150 focus_rectangle->width = (max_x - min_x) + 2 * focus_line_width;
3151 focus_rectangle->height = (max_y - min_y) + 2 * focus_line_width;
3159 * pspp_sheet_view_column_cell_render:
3160 * @tree_column: A #PsppSheetViewColumn.
3161 * @window: a #GdkDrawable to draw to
3162 * @background_area: entire cell area (including tree expanders and maybe padding on the sides)
3163 * @cell_area: area normally rendered by a cell renderer
3164 * @expose_area: area that actually needs updating
3165 * @flags: flags that affect rendering
3167 * Renders the cell contained by #tree_column. This is used primarily by the
3171 _pspp_sheet_view_column_cell_render (PsppSheetViewColumn *tree_column,
3173 const GdkRectangle *background_area,
3174 const GdkRectangle *cell_area,
3175 const GdkRectangle *expose_area,
3178 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
3179 g_return_if_fail (background_area != NULL);
3180 g_return_if_fail (cell_area != NULL);
3181 g_return_if_fail (expose_area != NULL);
3183 pspp_sheet_view_column_cell_process_action (tree_column,
3190 NULL, NULL, NULL, NULL);
3194 _pspp_sheet_view_column_cell_event (PsppSheetViewColumn *tree_column,
3195 GtkCellEditable **editable_widget,
3198 const GdkRectangle *background_area,
3199 const GdkRectangle *cell_area,
3202 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
3204 return pspp_sheet_view_column_cell_process_action (tree_column,
3217 _pspp_sheet_view_column_get_focus_area (PsppSheetViewColumn *tree_column,
3218 const GdkRectangle *background_area,
3219 const GdkRectangle *cell_area,
3220 GdkRectangle *focus_area)
3222 pspp_sheet_view_column_cell_process_action (tree_column,
3234 /* cell list manipulation */
3236 pspp_sheet_view_column_cell_first (PsppSheetViewColumn *tree_column)
3238 GList *list = tree_column->cell_list;
3240 /* first GTK_PACK_START cell we find */
3241 for ( ; list; list = list->next)
3243 PsppSheetViewColumnCellInfo *info = list->data;
3244 if (info->pack == GTK_PACK_START)
3248 /* hmm, else the *last* GTK_PACK_END cell */
3249 list = g_list_last (tree_column->cell_list);
3251 for ( ; list; list = list->prev)
3253 PsppSheetViewColumnCellInfo *info = list->data;
3254 if (info->pack == GTK_PACK_END)
3262 pspp_sheet_view_column_cell_last (PsppSheetViewColumn *tree_column)
3264 GList *list = tree_column->cell_list;
3266 /* *first* GTK_PACK_END cell we find */
3267 for ( ; list ; list = list->next)
3269 PsppSheetViewColumnCellInfo *info = list->data;
3270 if (info->pack == GTK_PACK_END)
3274 /* hmm, else the last GTK_PACK_START cell */
3275 list = g_list_last (tree_column->cell_list);
3277 for ( ; list; list = list->prev)
3279 PsppSheetViewColumnCellInfo *info = list->data;
3280 if (info->pack == GTK_PACK_START)
3288 pspp_sheet_view_column_cell_next (PsppSheetViewColumn *tree_column,
3292 PsppSheetViewColumnCellInfo *info = current->data;
3294 if (info->pack == GTK_PACK_START)
3296 for (list = current->next; list; list = list->next)
3298 PsppSheetViewColumnCellInfo *inf = list->data;
3299 if (inf->pack == GTK_PACK_START)
3303 /* out of GTK_PACK_START cells, get *last* GTK_PACK_END one */
3304 list = g_list_last (tree_column->cell_list);
3305 for (; list; list = list->prev)
3307 PsppSheetViewColumnCellInfo *inf = list->data;
3308 if (inf->pack == GTK_PACK_END)
3313 for (list = current->prev; list; list = list->prev)
3315 PsppSheetViewColumnCellInfo *inf = list->data;
3316 if (inf->pack == GTK_PACK_END)
3324 pspp_sheet_view_column_cell_prev (PsppSheetViewColumn *tree_column,
3328 PsppSheetViewColumnCellInfo *info = current->data;
3330 if (info->pack == GTK_PACK_END)
3332 for (list = current->next; list; list = list->next)
3334 PsppSheetViewColumnCellInfo *inf = list->data;
3335 if (inf->pack == GTK_PACK_END)
3339 /* out of GTK_PACK_END, get last GTK_PACK_START one */
3340 list = g_list_last (tree_column->cell_list);
3341 for ( ; list; list = list->prev)
3343 PsppSheetViewColumnCellInfo *inf = list->data;
3344 if (inf->pack == GTK_PACK_START)
3349 for (list = current->prev; list; list = list->prev)
3351 PsppSheetViewColumnCellInfo *inf = list->data;
3352 if (inf->pack == GTK_PACK_START)
3360 _pspp_sheet_view_column_cell_focus (PsppSheetViewColumn *tree_column,
3368 count = _pspp_sheet_view_column_count_special_cells (tree_column);
3369 rtl = gtk_widget_get_direction (GTK_WIDGET (tree_column->tree_view)) == GTK_TEXT_DIR_RTL;
3371 /* if we are the current focus column and have multiple editable cells,
3372 * try to select the next one, else move the focus to the next column
3374 if (PSPP_SHEET_VIEW (tree_column->tree_view)->priv->focus_column == tree_column)
3379 GList *list = tree_column->cell_list;
3380 PsppSheetViewColumnCellInfo *info = NULL;
3382 /* find current focussed cell */
3383 for ( ; list; list = list->next)
3386 if (info->has_focus)
3390 /* not a focussed cell in the focus column? */
3391 if (!list || !info || !info->has_focus)
3396 prev = pspp_sheet_view_column_cell_next (tree_column, list);
3397 next = pspp_sheet_view_column_cell_prev (tree_column, list);
3401 next = pspp_sheet_view_column_cell_next (tree_column, list);
3402 prev = pspp_sheet_view_column_cell_prev (tree_column, list);
3405 info->has_focus = FALSE;
3406 if (direction > 0 && next)
3409 info->has_focus = TRUE;
3412 else if (direction > 0 && !next && !right)
3414 /* keep focus on last cell */
3416 info = pspp_sheet_view_column_cell_first (tree_column)->data;
3418 info = pspp_sheet_view_column_cell_last (tree_column)->data;
3420 info->has_focus = TRUE;
3423 else if (direction < 0 && prev)
3426 info->has_focus = TRUE;
3429 else if (direction < 0 && !prev && !left)
3431 /* keep focus on first cell */
3433 info = pspp_sheet_view_column_cell_last (tree_column)->data;
3435 info = pspp_sheet_view_column_cell_first (tree_column)->data;
3437 info->has_focus = TRUE;
3444 /* we get focus, if we have multiple editable cells, give the correct one
3449 GList *list = tree_column->cell_list;
3451 /* clear focus first */
3452 for ( ; list ; list = list->next)
3454 PsppSheetViewColumnCellInfo *info = list->data;
3455 if (info->has_focus)
3456 info->has_focus = FALSE;
3463 list = pspp_sheet_view_column_cell_last (tree_column);
3464 else if (direction < 0)
3465 list = pspp_sheet_view_column_cell_first (tree_column);
3470 list = pspp_sheet_view_column_cell_first (tree_column);
3471 else if (direction < 0)
3472 list = pspp_sheet_view_column_cell_last (tree_column);
3476 ((PsppSheetViewColumnCellInfo *) list->data)->has_focus = TRUE;
3483 _pspp_sheet_view_column_cell_draw_focus (PsppSheetViewColumn *tree_column,
3485 const GdkRectangle *background_area,
3486 const GdkRectangle *cell_area,
3487 const GdkRectangle *expose_area,
3490 gint focus_line_width;
3491 GtkStateType cell_state;
3493 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
3494 gtk_widget_style_get (GTK_WIDGET (tree_column->tree_view),
3495 "focus-line-width", &focus_line_width, NULL);
3496 if (tree_column->editable_widget)
3498 /* This function is only called on the editable row when editing.
3501 gtk_paint_focus (tree_column->tree_view->style,
3503 gtk_widget_get_state (tree_column->tree_view),
3505 tree_column->tree_view,
3507 cell_area->x - focus_line_width,
3508 cell_area->y - focus_line_width,
3509 cell_area->width + 2 * focus_line_width,
3510 cell_area->height + 2 * focus_line_width);
3515 GdkRectangle focus_rectangle;
3516 pspp_sheet_view_column_cell_process_action (tree_column,
3526 cell_state = flags & GTK_CELL_RENDERER_SELECTED ? GTK_STATE_SELECTED :
3527 (flags & GTK_CELL_RENDERER_PRELIT ? GTK_STATE_PRELIGHT :
3528 (flags & GTK_CELL_RENDERER_INSENSITIVE ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL));
3529 gtk_paint_focus (tree_column->tree_view->style,
3533 tree_column->tree_view,
3537 focus_rectangle.width,
3538 focus_rectangle.height);
3543 * pspp_sheet_view_column_cell_is_visible:
3544 * @tree_column: A #PsppSheetViewColumn
3546 * Returns %TRUE if any of the cells packed into the @tree_column are visible.
3547 * For this to be meaningful, you must first initialize the cells with
3548 * pspp_sheet_view_column_cell_set_cell_data()
3550 * Return value: %TRUE, if any of the cells packed into the @tree_column are currently visible
3553 pspp_sheet_view_column_cell_is_visible (PsppSheetViewColumn *tree_column)
3557 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), FALSE);
3559 for (list = tree_column->cell_list; list; list = list->next)
3561 PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
3563 if (info->cell->visible)
3571 * pspp_sheet_view_column_focus_cell:
3572 * @tree_column: A #PsppSheetViewColumn
3573 * @cell: A #GtkCellRenderer
3575 * Sets the current keyboard focus to be at @cell, if the column contains
3576 * 2 or more editable and activatable cells.
3581 pspp_sheet_view_column_focus_cell (PsppSheetViewColumn *tree_column,
3582 GtkCellRenderer *cell)
3585 gboolean found_cell = FALSE;
3587 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
3588 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
3590 if (_pspp_sheet_view_column_count_special_cells (tree_column) < 2)
3593 for (list = tree_column->cell_list; list; list = list->next)
3595 PsppSheetViewColumnCellInfo *info = list->data;
3597 if (info->cell == cell)
3599 info->has_focus = TRUE;
3607 for (list = tree_column->cell_list; list; list = list->next)
3609 PsppSheetViewColumnCellInfo *info = list->data;
3611 if (info->cell != cell)
3612 info->has_focus = FALSE;
3615 /* FIXME: redraw? */
3620 _pspp_sheet_view_column_cell_set_dirty (PsppSheetViewColumn *tree_column,
3621 gboolean install_handler)
3625 for (list = tree_column->cell_list; list; list = list->next)
3627 PsppSheetViewColumnCellInfo *info = (PsppSheetViewColumnCellInfo *) list->data;
3629 info->requested_width = 0;
3631 tree_column->dirty = TRUE;
3632 tree_column->requested_width = -1;
3633 tree_column->width = 0;
3635 if (tree_column->tree_view &&
3636 gtk_widget_get_realized (tree_column->tree_view))
3638 if (install_handler)
3639 _pspp_sheet_view_install_mark_rows_col_dirty (PSPP_SHEET_VIEW (tree_column->tree_view));
3641 PSPP_SHEET_VIEW (tree_column->tree_view)->priv->mark_rows_col_dirty = TRUE;
3642 gtk_widget_queue_resize (tree_column->tree_view);
3647 _pspp_sheet_view_column_start_editing (PsppSheetViewColumn *tree_column,
3648 GtkCellEditable *cell_editable)
3650 g_return_if_fail (tree_column->editable_widget == NULL);
3652 tree_column->editable_widget = cell_editable;
3656 _pspp_sheet_view_column_stop_editing (PsppSheetViewColumn *tree_column)
3660 g_return_if_fail (tree_column->editable_widget != NULL);
3662 tree_column->editable_widget = NULL;
3663 for (list = tree_column->cell_list; list; list = list->next)
3664 ((PsppSheetViewColumnCellInfo *)list->data)->in_editing_mode = FALSE;
3668 _pspp_sheet_view_column_get_neighbor_sizes (PsppSheetViewColumn *column,
3669 GtkCellRenderer *cell,
3674 PsppSheetViewColumnCellInfo *info;
3680 list = pspp_sheet_view_column_cell_first (column);
3684 info = (PsppSheetViewColumnCellInfo *)list->data;
3686 list = pspp_sheet_view_column_cell_next (column, list);
3688 if (info->cell == cell)
3691 if (info->cell->visible)
3692 l += info->real_width + column->spacing;
3697 info = (PsppSheetViewColumnCellInfo *)list->data;
3699 list = pspp_sheet_view_column_cell_next (column, list);
3701 if (info->cell->visible)
3702 r += info->real_width + column->spacing;
3705 rtl = (gtk_widget_get_direction (GTK_WIDGET (column->tree_view)) == GTK_TEXT_DIR_RTL);
3707 *left = rtl ? r : l;
3710 *right = rtl ? l : r;
3714 * pspp_sheet_view_column_cell_get_position:
3715 * @tree_column: a #PsppSheetViewColumn
3716 * @cell_renderer: a #GtkCellRenderer
3717 * @start_pos: return location for the horizontal position of @cell within
3718 * @tree_column, may be %NULL
3719 * @width: return location for the width of @cell, may be %NULL
3721 * Obtains the horizontal position and size of a cell in a column. If the
3722 * cell is not found in the column, @start_pos and @width are not changed and
3723 * %FALSE is returned.
3725 * Return value: %TRUE if @cell belongs to @tree_column.
3728 pspp_sheet_view_column_cell_get_position (PsppSheetViewColumn *tree_column,
3729 GtkCellRenderer *cell_renderer,
3735 gboolean found_cell = FALSE;
3736 PsppSheetViewColumnCellInfo *cellinfo = NULL;
3738 list = pspp_sheet_view_column_cell_first (tree_column);
3739 for (; list; list = pspp_sheet_view_column_cell_next (tree_column, list))
3741 cellinfo = list->data;
3742 if (cellinfo->cell == cell_renderer)
3748 if (cellinfo->cell->visible)
3749 current_x += cellinfo->real_width;
3755 *start_pos = current_x;
3757 *width = cellinfo->real_width;
3764 * pspp_sheet_view_column_queue_resize:
3765 * @tree_column: A #PsppSheetViewColumn
3767 * Flags the column, and the cell renderers added to this column, to have
3768 * their sizes renegotiated.
3773 pspp_sheet_view_column_queue_resize (PsppSheetViewColumn *tree_column)
3775 g_return_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column));
3777 if (tree_column->tree_view)
3778 _pspp_sheet_view_column_cell_set_dirty (tree_column, TRUE);
3782 * pspp_sheet_view_column_get_tree_view:
3783 * @tree_column: A #PsppSheetViewColumn
3785 * Returns the #PsppSheetView wherein @tree_column has been inserted. If
3786 * @column is currently not inserted in any tree view, %NULL is
3789 * Return value: The tree view wherein @column has been inserted if any,
3795 pspp_sheet_view_column_get_tree_view (PsppSheetViewColumn *tree_column)
3797 g_return_val_if_fail (PSPP_IS_SHEET_VIEW_COLUMN (tree_column), NULL);
3799 return tree_column->tree_view;
3803 GtkCellLayout *cell_layout;
3804 GtkCellRenderer *renderer;
3806 } AttributesSubParserData;
3809 attributes_start_element (GMarkupParseContext *context,
3810 const gchar *element_name,
3811 const gchar **names,
3812 const gchar **values,
3816 AttributesSubParserData *parser_data = (AttributesSubParserData*)user_data;
3819 if (strcmp (element_name, "attribute") == 0)
3821 for (i = 0; names[i]; i++)
3822 if (strcmp (names[i], "name") == 0)
3823 parser_data->attr_name = g_strdup (values[i]);
3825 else if (strcmp (element_name, "attributes") == 0)
3828 g_warning ("Unsupported tag for GtkCellLayout: %s\n", element_name);
3832 attributes_text_element (GMarkupParseContext *context,
3838 AttributesSubParserData *parser_data = (AttributesSubParserData*)user_data;
3843 if (!parser_data->attr_name)
3847 string = g_strndup (text, text_len);
3848 l = strtol (string, &endptr, 0);
3849 if (errno || endptr == string)
3853 GTK_BUILDER_ERROR_INVALID_VALUE,
3854 "Could not parse integer `%s'",
3861 gtk_cell_layout_add_attribute (parser_data->cell_layout,
3862 parser_data->renderer,
3863 parser_data->attr_name, l);
3864 g_free (parser_data->attr_name);
3865 parser_data->attr_name = NULL;
3868 static const GMarkupParser attributes_parser =
3870 attributes_start_element,
3872 attributes_text_element,
3876 _gtk_cell_layout_buildable_custom_tag_start (GtkBuildable *buildable,
3877 GtkBuilder *builder,
3879 const gchar *tagname,
3880 GMarkupParser *parser,
3883 AttributesSubParserData *parser_data;
3888 if (strcmp (tagname, "attributes") == 0)
3890 parser_data = g_slice_new0 (AttributesSubParserData);
3891 parser_data->cell_layout = GTK_CELL_LAYOUT (buildable);
3892 parser_data->renderer = GTK_CELL_RENDERER (child);
3893 parser_data->attr_name = NULL;
3895 *parser = attributes_parser;
3896 *data = parser_data;
3904 _gtk_cell_layout_buildable_custom_tag_end (GtkBuildable *buildable,
3905 GtkBuilder *builder,
3907 const gchar *tagname,
3910 AttributesSubParserData *parser_data;
3912 parser_data = (AttributesSubParserData*)data;
3913 g_assert (!parser_data->attr_name);
3914 g_slice_free (AttributesSubParserData, parser_data);
3918 _gtk_cell_layout_buildable_add_child (GtkBuildable *buildable,
3919 GtkBuilder *builder,
3923 GtkCellLayoutIface *iface;
3925 g_return_if_fail (GTK_IS_CELL_LAYOUT (buildable));
3926 g_return_if_fail (GTK_IS_CELL_RENDERER (child));
3928 iface = GTK_CELL_LAYOUT_GET_IFACE (buildable);
3929 g_return_if_fail (iface->pack_start != NULL);
3930 iface->pack_start (GTK_CELL_LAYOUT (buildable), GTK_CELL_RENDERER (child), FALSE);
3934 pspp_sheet_view_column_sizing_get_type (void)
3936 static GType etype = 0;
3937 if (G_UNLIKELY(etype == 0)) {
3938 static const GEnumValue values[] = {
3939 { PSPP_SHEET_VIEW_COLUMN_GROW_ONLY, "PSPP_SHEET_VIEW_COLUMN_GROW_ONLY", "grow-only" },
3940 { PSPP_SHEET_VIEW_COLUMN_AUTOSIZE, "PSPP_SHEET_VIEW_COLUMN_AUTOSIZE", "autosize" },
3941 { PSPP_SHEET_VIEW_COLUMN_FIXED, "PSPP_SHEET_VIEW_COLUMN_FIXED", "fixed" },
3944 etype = g_enum_register_static (g_intern_static_string ("PsppSheetViewColumnSizing"), values);