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);
1058 psppire_var_sheet_variables_column_clicked (PsppSheetViewColumn *column,
1059 PsppireVarSheet *var_sheet)
1061 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1062 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1064 pspp_sheet_selection_select_all (selection);
1067 static PsppSheetViewColumn *
1068 make_row_number_column (PsppireVarSheet *var_sheet)
1070 PsppSheetViewColumn *column;
1071 GtkCellRenderer *renderer;
1073 renderer = psppire_cell_renderer_button_new ();
1074 g_object_set (renderer, "xalign", 1.0, NULL);
1075 g_signal_connect (renderer, "double-clicked",
1076 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1079 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1081 pspp_sheet_view_column_set_clickable (column, TRUE);
1082 pspp_sheet_view_column_set_cell_data_func (
1083 column, renderer, render_row_number_cell, var_sheet, NULL);
1084 pspp_sheet_view_column_set_fixed_width (column, 50);
1085 g_signal_connect (column, "clicked",
1086 G_CALLBACK (psppire_var_sheet_variables_column_clicked),
1093 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1095 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1096 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1097 PsppireDict *dict = var_sheet->dict;
1098 const struct range_set_node *node;
1099 struct range_set *selected;
1101 selected = pspp_sheet_selection_get_range_set (selection);
1102 for (node = range_set_last (selected); node != NULL;
1103 node = range_set_prev (selected, node))
1107 for (i = 1; i <= range_set_node_get_width (node); i++)
1109 unsigned long row = range_set_node_get_end (node) - i;
1110 if (row >= 0 && row < psppire_dict_get_var_cnt (dict))
1111 psppire_dict_delete_variables (dict, row, 1);
1114 range_set_destroy (selected);
1118 on_selection_changed (PsppSheetSelection *selection,
1119 gpointer user_data UNUSED)
1121 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1122 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1123 gint n_selected_rows;
1124 gboolean may_delete;
1128 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1130 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1131 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1132 && n_selected_rows > 0));
1134 switch (n_selected_rows)
1141 /* The row used for inserting new variables cannot be deleted. */
1142 path = gtk_tree_path_new_from_indices (
1143 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1144 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1145 gtk_tree_path_free (path);
1152 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1153 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1157 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1159 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1160 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1161 PsppireDict *dict = var_sheet->dict;
1162 struct range_set *selected;
1165 selected = pspp_sheet_selection_get_range_set (selection);
1166 row = range_set_scan (selected, 0);
1167 range_set_destroy (selected);
1169 if (row <= psppire_dict_get_var_cnt (dict))
1172 if (psppire_dict_generate_name (dict, name, sizeof name))
1173 psppire_dict_insert_variable (dict, row, name);
1178 psppire_var_sheet_init (PsppireVarSheet *obj)
1180 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1181 PsppSheetViewColumn *column;
1186 obj->format_use = FMT_FOR_OUTPUT;
1187 obj->may_create_vars = TRUE;
1188 obj->may_delete_vars = TRUE;
1190 obj->scroll_to_bottom_signal = 0;
1192 obj->container = NULL;
1193 obj->dispose_has_run = FALSE;
1196 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1198 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1199 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1200 g_signal_connect (list->data, "editing-started",
1201 G_CALLBACK (on_name_column_editing_started), NULL);
1204 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1205 add_popup_menu (obj, column, on_type_click);
1207 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1209 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1211 add_text_column (obj, VS_LABEL, _("Label"), 20);
1213 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1214 add_popup_menu (obj, column, on_value_labels_click);
1216 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1217 add_popup_menu (obj, column, on_missing_values_click);
1219 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1221 add_combo_column (obj, VS_ALIGN, _("Align"), 6,
1222 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1223 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1224 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1227 add_combo_column (obj, VS_MEASURE, _("Measure"), 10,
1228 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1229 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1230 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1233 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1234 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1235 PSPP_SHEET_SELECTION_MULTIPLE);
1237 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1238 g_signal_connect (obj, "query-tooltip",
1239 G_CALLBACK (on_query_var_tooltip), NULL);
1240 g_signal_connect (obj, "button-press-event",
1241 G_CALLBACK (on_button_pressed), NULL);
1242 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1244 obj->builder = builder_new ("var-sheet.ui");
1246 action = get_action_assert (obj->builder, "edit_clear-variables");
1247 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1249 gtk_action_set_sensitive (action, FALSE);
1250 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1251 "changed", G_CALLBACK (on_selection_changed), NULL);
1253 action = get_action_assert (obj->builder, "edit_insert-variable");
1254 gtk_action_set_sensitive (action, FALSE);
1255 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1260 psppire_var_sheet_new (void)
1262 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1266 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1268 return var_sheet->dict;
1272 refresh_model (PsppireVarSheet *var_sheet)
1274 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1276 if (var_sheet->dict != NULL)
1278 PsppireEmptyListStore *store;
1281 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1282 + var_sheet->may_create_vars);
1283 store = psppire_empty_list_store_new (n_rows);
1284 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1285 GTK_TREE_MODEL (store));
1286 g_object_unref (store);
1291 on_var_changed (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1293 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 psppire_empty_list_store_row_changed (store, row);
1305 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1307 PsppireEmptyListStore *store;
1310 g_return_if_fail (dict == var_sheet->dict);
1312 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1313 PSPP_SHEET_VIEW (var_sheet)));
1314 g_return_if_fail (store != NULL);
1316 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1317 + var_sheet->may_create_vars);
1318 psppire_empty_list_store_set_n_rows (store, n_rows);
1319 psppire_empty_list_store_row_inserted (store, row);
1323 on_var_deleted (PsppireDict *dict,
1324 const struct variable *var, int dict_idx, int case_idx,
1325 PsppireVarSheet *var_sheet)
1327 PsppireEmptyListStore *store;
1330 g_return_if_fail (dict == var_sheet->dict);
1332 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1333 PSPP_SHEET_VIEW (var_sheet)));
1334 g_return_if_fail (store != NULL);
1336 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1337 + var_sheet->may_create_vars);
1338 psppire_empty_list_store_set_n_rows (store, n_rows);
1339 psppire_empty_list_store_row_deleted (store, dict_idx);
1343 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1345 g_return_if_fail (dict == var_sheet->dict);
1346 refresh_model (var_sheet);
1350 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1353 if (var_sheet->dict != NULL)
1357 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1359 if (var_sheet->dict_signals[i])
1360 g_signal_handler_disconnect (var_sheet->dict,
1361 var_sheet->dict_signals[i]);
1363 var_sheet->dict_signals[i] = 0;
1366 g_object_unref (var_sheet->dict);
1369 var_sheet->dict = dict;
1373 g_object_ref (dict);
1375 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1376 = g_signal_connect (dict, "backend-changed",
1377 G_CALLBACK (on_backend_changed), var_sheet);
1379 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED]
1380 = g_signal_connect (dict, "variable-changed",
1381 G_CALLBACK (on_var_changed), var_sheet);
1383 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1384 = g_signal_connect (dict, "variable-inserted",
1385 G_CALLBACK (on_var_inserted), var_sheet);
1387 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1388 = g_signal_connect (dict, "variable-deleted",
1389 G_CALLBACK (on_var_deleted), var_sheet);
1392 refresh_model (var_sheet);
1396 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1398 return var_sheet->may_create_vars;
1402 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1403 gboolean may_create_vars)
1405 if (var_sheet->may_create_vars != may_create_vars)
1407 PsppireEmptyListStore *store;
1410 var_sheet->may_create_vars = may_create_vars;
1412 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1413 PSPP_SHEET_VIEW (var_sheet)));
1414 g_return_if_fail (store != NULL);
1416 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1417 + var_sheet->may_create_vars);
1418 psppire_empty_list_store_set_n_rows (store, n_rows);
1420 if (may_create_vars)
1421 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1423 psppire_empty_list_store_row_deleted (store, n_rows);
1425 on_selection_changed (pspp_sheet_view_get_selection (
1426 PSPP_SHEET_VIEW (var_sheet)), NULL);
1431 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1433 return var_sheet->may_delete_vars;
1437 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1438 gboolean may_delete_vars)
1440 if (var_sheet->may_delete_vars != may_delete_vars)
1442 var_sheet->may_delete_vars = may_delete_vars;
1443 on_selection_changed (pspp_sheet_view_get_selection (
1444 PSPP_SHEET_VIEW (var_sheet)), NULL);
1449 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1451 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1454 path = gtk_tree_path_new_from_indices (dict_index, -1);
1455 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1456 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1457 gtk_tree_path_free (path);
1461 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1463 if (var_sheet->uim == NULL)
1465 var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1467 GTK_TYPE_UI_MANAGER));
1468 g_object_ref (var_sheet->uim);
1471 return var_sheet->uim;