1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 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/>. */
19 #include "ui/gui/psppire-var-sheet.h"
21 #include "data/format.h"
22 #include "data/value-labels.h"
23 #include "libpspp/range-set.h"
24 #include "ui/gui/builder-wrapper.h"
25 #include "ui/gui/helper.h"
26 #include "ui/gui/missing-val-dialog.h"
27 #include "ui/gui/pspp-sheet-selection.h"
28 #include "ui/gui/psppire-cell-renderer-button.h"
29 #include "ui/gui/psppire-data-editor.h"
30 #include "ui/gui/psppire-data-window.h"
31 #include "ui/gui/psppire-dialog-action-var-info.h"
32 #include "ui/gui/psppire-empty-list-store.h"
33 #include "ui/gui/psppire-marshal.h"
34 #include "ui/gui/val-labs-dialog.h"
35 #include "ui/gui/var-type-dialog.h"
36 #include "ui/gui/var-display.h"
37 #include "ui/gui/var-type-dialog.h"
39 #include "gl/intprops.h"
42 #define _(msgid) gettext (msgid)
43 #define N_(msgid) msgid
59 G_DEFINE_TYPE (PsppireVarSheet, psppire_var_sheet, PSPP_TYPE_SHEET_VIEW);
62 set_spin_cell (GtkCellRenderer *cell, int value, int min, int max, int step)
64 char text[INT_BUFSIZE_BOUND (int)];
65 GtkAdjustment *adjust;
68 adjust = GTK_ADJUSTMENT (gtk_adjustment_new (value, min, max,
73 sprintf (text, "%d", value);
82 error_dialog (GtkWindow *w, gchar *primary_text, gchar *secondary_text)
85 gtk_message_dialog_new (w,
86 GTK_DIALOG_DESTROY_WITH_PARENT,
88 GTK_BUTTONS_CLOSE, "%s", primary_text);
90 g_object_set (dialog, "icon-name", "psppicon", NULL);
92 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
93 "%s", secondary_text);
95 gtk_dialog_run (GTK_DIALOG (dialog));
97 gtk_widget_destroy (dialog);
101 on_name_column_editing_started (GtkCellRenderer *cell,
102 GtkCellEditable *editable,
106 PsppireVarSheet *var_sheet = g_object_get_data (G_OBJECT (cell), "var-sheet");
107 PsppireDict *dict = psppire_var_sheet_get_dictionary (var_sheet);
109 g_return_if_fail (var_sheet);
110 g_return_if_fail (dict);
112 if (GTK_IS_ENTRY (editable))
114 GtkEntry *entry = GTK_ENTRY (editable);
115 if (gtk_entry_get_text (entry)[0] == '\0')
118 if (psppire_dict_generate_name (dict, name, sizeof name))
119 gtk_entry_set_text (entry, name);
125 scroll_to_bottom (GtkWidget *widget,
126 GtkRequisition *requisition,
127 gpointer unused UNUSED)
129 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
130 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
131 GtkAdjustment *vadjust;
133 vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
134 gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
136 if (var_sheet->scroll_to_bottom_signal)
138 g_signal_handler_disconnect (var_sheet,
139 var_sheet->scroll_to_bottom_signal);
140 var_sheet->scroll_to_bottom_signal = 0;
145 on_var_column_edited (GtkCellRendererText *cell,
150 PsppireVarSheet *var_sheet = user_data;
151 GtkWindow *window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (var_sheet)));
152 struct dictionary *dict = var_sheet->dict->dict;
153 enum vs_column column_id;
154 struct variable *var;
159 path = gtk_tree_path_new_from_string (path_string);
160 row = gtk_tree_path_get_indices (path)[0];
161 gtk_tree_path_free (path);
163 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell),
166 var = psppire_dict_get_variable (var_sheet->dict, row);
169 g_return_if_fail (column_id == VS_NAME);
171 if (!dict_id_is_valid (dict, new_text, false))
172 error_dialog (window,
173 g_strdup (_("Cannot create variable.")),
174 g_strdup_printf (_("\"%s\" is not a valid variable "
175 "name."), new_text));
176 else if (dict_lookup_var (dict, new_text) != NULL)
177 error_dialog (window,
178 g_strdup (_("Cannot create variable.")),
179 g_strdup_printf (_("This dictionary already contains "
180 "a variable named \"%s\"."),
184 dict_create_var (var_sheet->dict->dict, new_text, 0);
185 if (!var_sheet->scroll_to_bottom_signal)
187 gtk_widget_queue_resize (GTK_WIDGET (var_sheet));
188 var_sheet->scroll_to_bottom_signal =
189 g_signal_connect (var_sheet, "size-request",
190 G_CALLBACK (scroll_to_bottom), NULL);
200 if (!dict_id_is_valid (dict, new_text, false))
201 error_dialog (window,
202 g_strdup (_("Cannot rename variable.")),
203 g_strdup_printf (_("\"%s\" is not a valid variable "
204 "name."), new_text));
205 else if (dict_lookup_var (dict, new_text) != NULL
206 && dict_lookup_var (dict, new_text) != var)
207 error_dialog (window,
208 g_strdup (_("Cannot rename variable.")),
209 g_strdup_printf (_("This dictionary already contains "
210 "a variable named \"%s\"."),
213 dict_rename_var (dict, var, new_text);
221 width = atoi (new_text);
224 struct fmt_spec format;
226 format = *var_get_print_format (var);
227 fmt_change_width (&format, width, var_sheet->format_use);
228 var_set_width (var, fmt_var_width (&format));
229 var_set_both_formats (var, &format);
234 decimals = atoi (new_text);
237 struct fmt_spec format;
239 format = *var_get_print_format (var);
240 fmt_change_decimals (&format, decimals, var_sheet->format_use);
241 var_set_print_format (var, &format);
246 var_set_label (var, new_text, false);
254 width = atoi (new_text);
255 if (width > 0 && width < 2 * MAX_STRING)
256 var_set_display_width (var, width);
260 if (!strcmp (new_text, alignment_to_string (ALIGN_LEFT)))
261 var_set_alignment (var, ALIGN_LEFT);
262 else if (!strcmp (new_text, alignment_to_string (ALIGN_CENTRE)))
263 var_set_alignment (var, ALIGN_CENTRE);
264 else if (!strcmp (new_text, alignment_to_string (ALIGN_RIGHT)))
265 var_set_alignment (var, ALIGN_RIGHT);
269 if (!strcmp (new_text, measure_to_string (MEASURE_NOMINAL)))
270 var_set_measure (var, MEASURE_NOMINAL);
271 else if (!strcmp (new_text, measure_to_string (MEASURE_ORDINAL)))
272 var_set_measure (var, MEASURE_ORDINAL);
273 else if (!strcmp (new_text, measure_to_string (MEASURE_SCALE)))
274 var_set_measure (var, MEASURE_SCALE);
280 render_popup_cell (PsppSheetViewColumn *tree_column,
281 GtkCellRenderer *cell,
286 PsppireVarSheet *var_sheet = user_data;
289 row = GPOINTER_TO_INT (iter->user_data);
291 "editable", row < psppire_dict_get_var_cnt (var_sheet->dict),
296 render_var_cell (PsppSheetViewColumn *tree_column,
297 GtkCellRenderer *cell,
302 PsppireVarSheet *var_sheet = user_data;
303 const struct fmt_spec *print;
304 enum vs_column column_id;
305 struct variable *var;
308 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
309 "column-number")) - 1;
310 row = GPOINTER_TO_INT (iter->user_data);
312 if (row >= psppire_dict_get_var_cnt (var_sheet->dict))
316 "editable", column_id == VS_NAME,
318 if (column_id == VS_WIDTH
319 || column_id == VS_DECIMALS
320 || column_id == VS_COLUMNS)
321 g_object_set (cell, "adjustment", NULL, NULL);
325 var = psppire_dict_get_variable (var_sheet->dict, row);
327 print = var_get_print_format (var);
332 "text", var_get_name (var),
339 "text", fmt_gui_name (print->type),
345 set_spin_cell (cell, print->w,
346 fmt_min_width (print->type, var_sheet->format_use),
347 fmt_max_width (print->type, var_sheet->format_use),
348 fmt_step_width (print->type));
352 if (fmt_takes_decimals (print->type))
354 int max_w = fmt_max_width (print->type, var_sheet->format_use);
355 int max_d = fmt_max_decimals (print->type, max_w,
356 var_sheet->format_use);
357 set_spin_cell (cell, print->d, 0, max_d, 1);
369 "text", var_has_label (var) ? var_get_label (var) : "",
375 g_object_set (cell, "editable", FALSE, NULL);
376 if ( ! var_has_value_labels (var))
377 g_object_set (cell, "text", _("None"), NULL);
380 const struct val_labs *vls = var_get_value_labels (var);
381 const struct val_lab **labels = val_labs_sorted (vls);
382 const struct val_lab *vl = labels[0];
383 gchar *vstr = value_to_text (vl->value, var);
384 char *text = xasprintf (_("{%s, %s}..."), vstr,
385 val_lab_get_escaped_label (vl));
388 g_object_set (cell, "text", text, NULL);
396 char *text = missing_values_to_string (var_sheet->dict, var, NULL);
406 set_spin_cell (cell, var_get_display_width (var), 1, 2 * MAX_STRING, 1);
411 "text", alignment_to_string (var_get_alignment (var)),
418 "text", measure_to_string (var_get_measure (var)),
425 static struct variable *
426 path_string_to_variable (PsppireVarSheet *var_sheet, gchar *path_string)
432 path = gtk_tree_path_new_from_string (path_string);
433 row = gtk_tree_path_get_indices (path)[0];
434 gtk_tree_path_free (path);
436 dict = psppire_var_sheet_get_dictionary (var_sheet);
437 g_return_val_if_fail (dict != NULL, NULL);
439 return psppire_dict_get_variable (dict, row);
443 on_type_click (PsppireCellRendererButton *cell,
445 PsppireVarSheet *var_sheet)
447 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
448 struct fmt_spec format;
449 struct variable *var;
451 var = path_string_to_variable (var_sheet, path);
452 g_return_if_fail (var != NULL);
454 format = *var_get_print_format (var);
455 psppire_var_type_dialog_run (GTK_WINDOW (toplevel), &format);
456 var_set_width (var, fmt_var_width (&format));
457 var_set_both_formats (var, &format);
461 on_value_labels_click (PsppireCellRendererButton *cell,
463 PsppireVarSheet *var_sheet)
465 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
466 struct val_labs *labels;
467 struct variable *var;
469 var = path_string_to_variable (var_sheet, path);
470 g_return_if_fail (var != NULL);
472 labels = psppire_val_labs_dialog_run (GTK_WINDOW (toplevel), var);
475 var_set_value_labels (var, labels);
476 val_labs_destroy (labels);
481 on_missing_values_click (PsppireCellRendererButton *cell,
483 PsppireVarSheet *var_sheet)
485 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
486 struct missing_values mv;
487 struct variable *var;
489 var = path_string_to_variable (var_sheet, path);
490 g_return_if_fail (var != NULL);
492 psppire_missing_val_dialog_run (GTK_WINDOW (toplevel), var, &mv);
493 var_set_missing_values (var, &mv);
498 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
502 g_object_set (G_OBJECT (renderer),
503 PSPPIRE_IS_CELL_RENDERER_BUTTON (renderer) ? "label" : "text",
504 string, (void *) NULL);
505 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
506 NULL, NULL, NULL, &width, NULL);
511 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
518 ds_put_byte_multiple (&s, '0', char_cnt);
519 ds_put_byte (&s, ' ');
520 width = get_string_width (treeview, renderer, ds_cstr (&s));
526 static PsppSheetViewColumn *
527 add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer,
528 enum vs_column column_id,
529 const char *title, int width)
531 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
532 int title_width, content_width;
533 PsppSheetViewColumn *column;
535 column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL);
536 g_object_set_data (G_OBJECT (column), "column-number",
537 GINT_TO_POINTER (column_id) + 1);
539 pspp_sheet_view_column_set_cell_data_func (
540 column, renderer, render_var_cell, var_sheet, NULL);
542 title_width = get_string_width (sheet_view, renderer, title);
543 content_width = get_monospace_width (sheet_view, renderer, width);
544 g_object_set_data (G_OBJECT (column), "content-width",
545 GINT_TO_POINTER (content_width));
547 pspp_sheet_view_column_set_fixed_width (column,
548 MAX (title_width, content_width));
549 pspp_sheet_view_column_set_resizable (column, TRUE);
551 pspp_sheet_view_append_column (sheet_view, column);
553 g_signal_connect (renderer, "edited",
554 G_CALLBACK (on_var_column_edited),
556 g_object_set_data (G_OBJECT (renderer), "column-id",
557 GINT_TO_POINTER (column_id));
558 g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet);
563 static PsppSheetViewColumn *
564 add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
565 const char *title, int width)
567 return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (),
568 column_id, title, width);
571 static PsppSheetViewColumn *
572 add_spin_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
573 const char *title, int width)
575 return add_var_sheet_column (var_sheet, gtk_cell_renderer_spin_new (),
576 column_id, title, width);
579 static PsppSheetViewColumn *
580 add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
581 const char *title, int width,
584 GtkCellRenderer *cell;
589 store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
590 va_start (args, width);
591 while ((name = va_arg (args, const char *)) != NULL)
593 int value = va_arg (args, int);
594 gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
601 cell = gtk_cell_renderer_combo_new ();
604 "model", GTK_TREE_MODEL (store),
608 return add_var_sheet_column (var_sheet, cell, column_id, title, width);
613 add_popup_menu (PsppireVarSheet *var_sheet,
614 PsppSheetViewColumn *column,
615 void (*on_click) (PsppireCellRendererButton *,
617 PsppireVarSheet *var_sheet))
619 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
620 const char *button_label = "...";
621 GtkCellRenderer *button_renderer;
624 button_renderer = psppire_cell_renderer_button_new ();
625 g_object_set (button_renderer,
626 "label", button_label,
629 g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click),
631 pspp_sheet_view_column_pack_start (column, button_renderer, FALSE);
632 pspp_sheet_view_column_set_cell_data_func (
633 column, button_renderer, render_popup_cell, var_sheet, NULL);
635 content_width = GPOINTER_TO_INT (g_object_get_data (
636 G_OBJECT (column), "content-width"));
637 content_width += get_string_width (sheet_view, button_renderer,
639 if (content_width > pspp_sheet_view_column_get_fixed_width (column))
640 pspp_sheet_view_column_set_fixed_width (column, content_width);
644 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
645 gint wx, gint wy, size_t *row, size_t *column)
647 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
651 PsppSheetViewColumn *tree_column;
652 GtkTreeModel *tree_model;
656 /* Check that WIDGET is really visible on the screen before we
657 do anything else. This is a bug fix for a sticky situation:
658 when text_data_import_assistant() returns, it frees the data
659 necessary to compose the tool tip message, but there may be
660 a tool tip under preparation at that point (even if there is
661 no visible tool tip) that will call back into us a little
662 bit later. Perhaps the correct solution to this problem is
663 to make the data related to the tool tips part of a GObject
664 that only gets destroyed when all references are released,
665 but this solution appears to be effective too. */
666 if (!gtk_widget_get_mapped (widget))
669 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
671 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
672 &path, &tree_column, NULL, NULL))
675 column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number");
676 if (column_ptr == NULL)
678 *column = GPOINTER_TO_INT (column_ptr) - 1;
680 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
683 tree_model = pspp_sheet_view_get_model (tree_view);
684 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
685 gtk_tree_path_free (path);
689 *row = GPOINTER_TO_INT (iter.user_data);
694 on_query_var_tooltip (GtkWidget *widget, gint wx, gint wy,
695 gboolean keyboard_mode UNUSED,
696 GtkTooltip *tooltip, gpointer *user_data UNUSED)
698 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
700 struct variable *var;
703 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
706 dict = psppire_var_sheet_get_dictionary (var_sheet);
707 g_return_val_if_fail (dict != NULL, FALSE);
709 if (row >= psppire_dict_get_var_cnt (dict))
711 gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a "
716 var = psppire_dict_get_variable (dict, row);
717 g_return_val_if_fail (var != NULL, FALSE);
723 char text[FMT_STRING_LEN_MAX + 1];
725 fmt_to_string (var_get_print_format (var), text);
726 gtk_tooltip_set_text (tooltip, text);
731 if (var_has_value_labels (var))
733 const struct val_labs *vls = var_get_value_labels (var);
734 const struct val_lab **labels = val_labs_sorted (vls);
739 for (i = 0; i < val_labs_count (vls); i++)
741 const struct val_lab *vl = labels[i];
744 if (i >= 10 || ds_length (&s) > 500)
746 ds_put_cstr (&s, "...");
750 vstr = value_to_text (vl->value, var);
751 ds_put_format (&s, _("{%s, %s}\n"), vstr,
752 val_lab_get_escaped_label (vl));
756 ds_chomp_byte (&s, '\n');
758 gtk_tooltip_set_text (tooltip, ds_cstr (&s));
770 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
772 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
775 menu = get_widget_assert (var_sheet->builder, "varsheet-variable-popup");
776 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
780 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
782 do_popup_menu (widget, 0, gtk_get_current_event_time ());
786 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
787 gpointer user_data UNUSED)
789 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
791 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
793 PsppSheetSelection *selection;
795 selection = pspp_sheet_view_get_selection (sheet_view);
796 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
800 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
801 &path, NULL, NULL, NULL))
803 pspp_sheet_selection_unselect_all (selection);
804 pspp_sheet_selection_select_path (selection, path);
805 gtk_tree_path_free (path);
809 do_popup_menu (widget, event->button, event->time);
817 psppire_fmt_use_get_type (void)
819 static GType etype = 0;
822 static const GEnumValue values[] =
824 { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
825 { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
829 etype = g_enum_register_static
830 (g_intern_static_string ("PsppireFmtUse"), values);
839 PROP_MAY_CREATE_VARS,
840 PROP_MAY_DELETE_VARS,
846 psppire_var_sheet_set_property (GObject *object,
851 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
855 case PROP_DICTIONARY:
856 psppire_var_sheet_set_dictionary (obj,
857 PSPPIRE_DICT (g_value_get_object (
861 case PROP_MAY_CREATE_VARS:
862 psppire_var_sheet_set_may_create_vars (obj,
863 g_value_get_boolean (value));
866 case PROP_MAY_DELETE_VARS:
867 psppire_var_sheet_set_may_delete_vars (obj,
868 g_value_get_boolean (value));
871 case PROP_FORMAT_TYPE:
872 obj->format_use = g_value_get_enum (value);
875 case PROP_UI_MANAGER:
877 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
883 psppire_var_sheet_get_property (GObject *object,
888 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
892 case PROP_DICTIONARY:
893 g_value_set_object (value,
894 G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
897 case PROP_MAY_CREATE_VARS:
898 g_value_set_boolean (value, obj->may_create_vars);
901 case PROP_MAY_DELETE_VARS:
902 g_value_set_boolean (value, obj->may_delete_vars);
905 case PROP_FORMAT_TYPE:
906 g_value_set_enum (value, obj->format_use);
909 case PROP_UI_MANAGER:
910 g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj));
914 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
920 psppire_var_sheet_dispose (GObject *obj)
922 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
925 if (var_sheet->dispose_has_run)
928 var_sheet->dispose_has_run = TRUE;
930 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
931 if ( var_sheet->dict_signals[i])
932 g_signal_handler_disconnect (var_sheet->dict,
933 var_sheet->dict_signals[i]);
936 g_object_unref (var_sheet->dict);
939 g_object_unref (var_sheet->uim);
941 /* These dialogs are not GObjects (although they should be!)
942 But for now, unreffing them only causes a GCritical Error
943 so comment them out for now. (and accept the memory leakage)
945 g_object_unref (var_sheet->val_labs_dialog);
946 g_object_unref (var_sheet->missing_val_dialog);
947 g_object_unref (var_sheet->var_type_dialog);
950 G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
954 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
956 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
959 gobject_class->set_property = psppire_var_sheet_set_property;
960 gobject_class->get_property = psppire_var_sheet_get_property;
961 gobject_class->dispose = psppire_var_sheet_dispose;
963 g_signal_new ("var-double-clicked",
964 G_OBJECT_CLASS_TYPE (gobject_class),
967 g_signal_accumulator_true_handled, NULL,
968 psppire_marshal_BOOLEAN__INT,
969 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
971 pspec = g_param_spec_object ("dictionary",
972 "Dictionary displayed by the sheet",
973 "The PsppireDict that the sheet displays "
974 "may allow the user to edit",
977 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
979 pspec = g_param_spec_boolean ("may-create-vars",
980 "May create variables",
981 "Whether the user may create more variables",
984 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
986 pspec = g_param_spec_boolean ("may-delete-vars",
987 "May delete variables",
988 "Whether the user may delete variables",
991 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
993 pspec = g_param_spec_enum ("format-use",
994 "Use of variable format",
995 ("Whether variables have input or output "
997 PSPPIRE_TYPE_FMT_USE,
1000 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
1002 pspec = g_param_spec_object ("ui-manager",
1004 "UI manager for the variable sheet. The client should merge this UI manager with the active UI manager to obtain variable sheet specific menu items and tool bar items.",
1005 GTK_TYPE_UI_MANAGER,
1007 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1011 render_row_number_cell (PsppSheetViewColumn *tree_column,
1012 GtkCellRenderer *cell,
1013 GtkTreeModel *model,
1017 PsppireVarSheet *var_sheet = user_data;
1018 GValue gvalue = { 0, };
1021 row = GPOINTER_TO_INT (iter->user_data);
1023 g_value_init (&gvalue, G_TYPE_INT);
1024 g_value_set_int (&gvalue, row + 1);
1025 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1026 g_value_unset (&gvalue);
1028 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1029 g_object_set (cell, "editable", TRUE, NULL);
1031 g_object_set (cell, "editable", FALSE, NULL);
1035 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1037 PsppireVarSheet *var_sheet)
1041 g_return_if_fail (var_sheet->dict != NULL);
1043 path = gtk_tree_path_new_from_string (path_string);
1044 if (gtk_tree_path_get_depth (path) == 1)
1046 gint *indices = gtk_tree_path_get_indices (path);
1047 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1050 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1051 indices[0], &handled);
1054 gtk_tree_path_free (path);
1057 static PsppSheetViewColumn *
1058 make_row_number_column (PsppireVarSheet *var_sheet)
1060 PsppSheetViewColumn *column;
1061 GtkCellRenderer *renderer;
1063 renderer = psppire_cell_renderer_button_new ();
1064 g_object_set (renderer, "xalign", 1.0, NULL);
1065 g_signal_connect (renderer, "double-clicked",
1066 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1069 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1071 pspp_sheet_view_column_set_cell_data_func (
1072 column, renderer, render_row_number_cell, var_sheet, NULL);
1073 pspp_sheet_view_column_set_fixed_width (column, 50);
1078 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1080 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1081 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1082 PsppireDict *dict = var_sheet->dict;
1083 const struct range_set_node *node;
1084 struct range_set *selected;
1086 selected = pspp_sheet_selection_get_range_set (selection);
1087 for (node = range_set_last (selected); node != NULL;
1088 node = range_set_prev (selected, node))
1092 for (i = 1; i <= range_set_node_get_width (node); i++)
1094 unsigned long row = range_set_node_get_end (node) - i;
1095 if (row >= 0 && row < psppire_dict_get_var_cnt (dict))
1096 psppire_dict_delete_variables (dict, row, 1);
1099 range_set_destroy (selected);
1103 on_selection_changed (PsppSheetSelection *selection,
1104 gpointer user_data UNUSED)
1106 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1107 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1108 gint n_selected_rows;
1109 gboolean may_delete;
1113 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1115 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1116 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1117 && n_selected_rows > 0));
1119 switch (n_selected_rows)
1126 /* The row used for inserting new variables cannot be deleted. */
1127 path = gtk_tree_path_new_from_indices (
1128 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1129 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1130 gtk_tree_path_free (path);
1137 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1138 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1142 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1144 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1145 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1146 PsppireDict *dict = var_sheet->dict;
1147 struct range_set *selected;
1150 selected = pspp_sheet_selection_get_range_set (selection);
1151 row = range_set_scan (selected, 0);
1152 range_set_destroy (selected);
1154 if (row <= psppire_dict_get_var_cnt (dict))
1157 if (psppire_dict_generate_name (dict, name, sizeof name))
1158 psppire_dict_insert_variable (dict, row, name);
1163 psppire_var_sheet_init (PsppireVarSheet *obj)
1165 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1166 PsppSheetViewColumn *column;
1171 obj->format_use = FMT_FOR_OUTPUT;
1172 obj->may_create_vars = TRUE;
1173 obj->may_delete_vars = TRUE;
1175 obj->scroll_to_bottom_signal = 0;
1177 obj->container = NULL;
1178 obj->dispose_has_run = FALSE;
1181 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1183 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1184 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1185 g_signal_connect (list->data, "editing-started",
1186 G_CALLBACK (on_name_column_editing_started), NULL);
1189 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1190 add_popup_menu (obj, column, on_type_click);
1192 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1194 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1196 add_text_column (obj, VS_LABEL, _("Label"), 20);
1198 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1199 add_popup_menu (obj, column, on_value_labels_click);
1201 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1202 add_popup_menu (obj, column, on_missing_values_click);
1204 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1206 add_combo_column (obj, VS_ALIGN, _("Align"), 6,
1207 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1208 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1209 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1212 add_combo_column (obj, VS_MEASURE, _("Measure"), 10,
1213 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1214 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1215 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1218 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1219 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1220 PSPP_SHEET_SELECTION_MULTIPLE);
1222 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1223 g_signal_connect (obj, "query-tooltip",
1224 G_CALLBACK (on_query_var_tooltip), NULL);
1225 g_signal_connect (obj, "button-press-event",
1226 G_CALLBACK (on_button_pressed), NULL);
1227 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1229 obj->builder = builder_new ("var-sheet.ui");
1231 action = get_action_assert (obj->builder, "edit_clear-variables");
1232 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1234 gtk_action_set_sensitive (action, FALSE);
1235 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1236 "changed", G_CALLBACK (on_selection_changed), NULL);
1238 action = get_action_assert (obj->builder, "edit_insert-variable");
1239 gtk_action_set_sensitive (action, FALSE);
1240 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1245 psppire_var_sheet_new (void)
1247 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1251 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1253 return var_sheet->dict;
1257 refresh_model (PsppireVarSheet *var_sheet)
1259 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1261 if (var_sheet->dict != NULL)
1263 PsppireEmptyListStore *store;
1266 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1267 + var_sheet->may_create_vars);
1268 store = psppire_empty_list_store_new (n_rows);
1269 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1270 GTK_TREE_MODEL (store));
1271 g_object_unref (store);
1276 on_var_changed (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1278 PsppireEmptyListStore *store;
1280 g_return_if_fail (dict == var_sheet->dict);
1282 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1283 PSPP_SHEET_VIEW (var_sheet)));
1284 g_return_if_fail (store != NULL);
1286 psppire_empty_list_store_row_changed (store, row);
1290 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1292 PsppireEmptyListStore *store;
1295 g_return_if_fail (dict == var_sheet->dict);
1297 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1298 PSPP_SHEET_VIEW (var_sheet)));
1299 g_return_if_fail (store != NULL);
1301 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1302 + var_sheet->may_create_vars);
1303 psppire_empty_list_store_set_n_rows (store, n_rows);
1304 psppire_empty_list_store_row_inserted (store, row);
1308 on_var_deleted (PsppireDict *dict,
1309 const struct variable *var, int dict_idx, int case_idx,
1310 PsppireVarSheet *var_sheet)
1312 PsppireEmptyListStore *store;
1315 g_return_if_fail (dict == var_sheet->dict);
1317 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1318 PSPP_SHEET_VIEW (var_sheet)));
1319 g_return_if_fail (store != NULL);
1321 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1322 + var_sheet->may_create_vars);
1323 psppire_empty_list_store_set_n_rows (store, n_rows);
1324 psppire_empty_list_store_row_deleted (store, dict_idx);
1328 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1330 g_return_if_fail (dict == var_sheet->dict);
1331 refresh_model (var_sheet);
1335 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1338 if (var_sheet->dict != NULL)
1342 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1344 if (var_sheet->dict_signals[i])
1345 g_signal_handler_disconnect (var_sheet->dict,
1346 var_sheet->dict_signals[i]);
1348 var_sheet->dict_signals[i] = 0;
1351 g_object_unref (var_sheet->dict);
1354 var_sheet->dict = dict;
1358 g_object_ref (dict);
1360 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1361 = g_signal_connect (dict, "backend-changed",
1362 G_CALLBACK (on_backend_changed), var_sheet);
1364 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED]
1365 = g_signal_connect (dict, "variable-changed",
1366 G_CALLBACK (on_var_changed), var_sheet);
1368 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1369 = g_signal_connect (dict, "variable-inserted",
1370 G_CALLBACK (on_var_inserted), var_sheet);
1372 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1373 = g_signal_connect (dict, "variable-deleted",
1374 G_CALLBACK (on_var_deleted), var_sheet);
1377 refresh_model (var_sheet);
1381 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1383 return var_sheet->may_create_vars;
1387 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1388 gboolean may_create_vars)
1390 if (var_sheet->may_create_vars != may_create_vars)
1392 PsppireEmptyListStore *store;
1395 var_sheet->may_create_vars = may_create_vars;
1397 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1398 PSPP_SHEET_VIEW (var_sheet)));
1399 g_return_if_fail (store != NULL);
1401 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1402 + var_sheet->may_create_vars);
1403 psppire_empty_list_store_set_n_rows (store, n_rows);
1405 if (may_create_vars)
1406 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1408 psppire_empty_list_store_row_deleted (store, n_rows);
1410 on_selection_changed (pspp_sheet_view_get_selection (
1411 PSPP_SHEET_VIEW (var_sheet)), NULL);
1416 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1418 return var_sheet->may_delete_vars;
1422 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1423 gboolean may_delete_vars)
1425 if (var_sheet->may_delete_vars != may_delete_vars)
1427 var_sheet->may_delete_vars = may_delete_vars;
1428 on_selection_changed (pspp_sheet_view_get_selection (
1429 PSPP_SHEET_VIEW (var_sheet)), NULL);
1434 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1436 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1439 path = gtk_tree_path_new_from_indices (dict_index, -1);
1440 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1441 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1442 gtk_tree_path_free (path);
1446 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1448 if (var_sheet->uim == NULL)
1450 var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1452 GTK_TYPE_UI_MANAGER));
1453 g_object_ref (var_sheet->uim);
1456 return var_sheet->uim;