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/>. */
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-display.h"
36 #include "ui/gui/var-type-dialog.h"
38 #include "gl/intprops.h"
41 #define _(msgid) gettext (msgid)
42 #define N_(msgid) msgid
58 G_DEFINE_TYPE (PsppireVarSheet, psppire_var_sheet, PSPP_TYPE_SHEET_VIEW);
61 set_spin_cell (GtkCellRenderer *cell, int value, int min, int max, int step)
63 char text[INT_BUFSIZE_BOUND (int)];
64 GtkAdjustment *adjust;
67 adjust = GTK_ADJUSTMENT (gtk_adjustment_new (value, min, max,
72 sprintf (text, "%d", value);
81 error_dialog (GtkWindow *w, gchar *primary_text, gchar *secondary_text)
84 gtk_message_dialog_new (w,
85 GTK_DIALOG_DESTROY_WITH_PARENT,
87 GTK_BUTTONS_CLOSE, "%s", primary_text);
89 g_object_set (dialog, "icon-name", "psppicon", NULL);
91 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
92 "%s", secondary_text);
94 gtk_dialog_run (GTK_DIALOG (dialog));
96 gtk_widget_destroy (dialog);
100 on_name_column_editing_started (GtkCellRenderer *cell,
101 GtkCellEditable *editable,
105 PsppireVarSheet *var_sheet = g_object_get_data (G_OBJECT (cell), "var-sheet");
106 PsppireDict *dict = psppire_var_sheet_get_dictionary (var_sheet);
108 g_return_if_fail (var_sheet);
109 g_return_if_fail (dict);
111 if (GTK_IS_ENTRY (editable))
113 GtkEntry *entry = GTK_ENTRY (editable);
114 if (gtk_entry_get_text (entry)[0] == '\0')
117 if (psppire_dict_generate_name (dict, name, sizeof name))
118 gtk_entry_set_text (entry, name);
124 scroll_to_bottom (GtkWidget *widget,
125 GtkRequisition *requisition,
126 gpointer unused UNUSED)
128 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
129 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
130 GtkAdjustment *vadjust;
132 vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
133 gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
135 if (var_sheet->scroll_to_bottom_signal)
137 g_signal_handler_disconnect (var_sheet,
138 var_sheet->scroll_to_bottom_signal);
139 var_sheet->scroll_to_bottom_signal = 0;
144 on_var_column_edited (GtkCellRendererText *cell,
149 PsppireVarSheet *var_sheet = user_data;
150 GtkWindow *window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (var_sheet)));
151 struct dictionary *dict = var_sheet->dict->dict;
152 enum vs_column column_id;
153 struct variable *var;
158 path = gtk_tree_path_new_from_string (path_string);
159 row = gtk_tree_path_get_indices (path)[0];
160 gtk_tree_path_free (path);
162 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell),
165 var = psppire_dict_get_variable (var_sheet->dict, row);
168 g_return_if_fail (column_id == VS_NAME);
170 if (!dict_id_is_valid (dict, new_text, false))
171 error_dialog (window,
172 g_strdup (_("Cannot create variable.")),
173 g_strdup_printf (_("\"%s\" is not a valid variable "
174 "name."), new_text));
175 else if (dict_lookup_var (dict, new_text) != NULL)
176 error_dialog (window,
177 g_strdup (_("Cannot create variable.")),
178 g_strdup_printf (_("This dictionary already contains "
179 "a variable named \"%s\"."),
183 dict_create_var (var_sheet->dict->dict, new_text, 0);
184 if (!var_sheet->scroll_to_bottom_signal)
186 gtk_widget_queue_resize (GTK_WIDGET (var_sheet));
187 var_sheet->scroll_to_bottom_signal =
188 g_signal_connect (var_sheet, "size-request",
189 G_CALLBACK (scroll_to_bottom), NULL);
199 if (!dict_id_is_valid (dict, new_text, false))
200 error_dialog (window,
201 g_strdup (_("Cannot rename variable.")),
202 g_strdup_printf (_("\"%s\" is not a valid variable "
203 "name."), new_text));
204 else if (dict_lookup_var (dict, new_text) != NULL
205 && dict_lookup_var (dict, new_text) != var)
206 error_dialog (window,
207 g_strdup (_("Cannot rename variable.")),
208 g_strdup_printf (_("This dictionary already contains "
209 "a variable named \"%s\"."),
212 dict_rename_var (dict, var, new_text);
220 width = atoi (new_text);
223 struct fmt_spec format;
225 format = *var_get_print_format (var);
226 fmt_change_width (&format, width, var_sheet->format_use);
227 var_set_print_format (var, &format);
228 var_set_width (var, fmt_var_width (&format));
233 decimals = atoi (new_text);
236 struct fmt_spec format;
238 format = *var_get_print_format (var);
239 fmt_change_decimals (&format, decimals, var_sheet->format_use);
240 var_set_print_format (var, &format);
245 var_set_label (var, new_text, false);
253 width = atoi (new_text);
254 if (width > 0 && width < 2 * MAX_STRING)
255 var_set_display_width (var, width);
259 if (!strcmp (new_text, alignment_to_string (ALIGN_LEFT)))
260 var_set_alignment (var, ALIGN_LEFT);
261 else if (!strcmp (new_text, alignment_to_string (ALIGN_CENTRE)))
262 var_set_alignment (var, ALIGN_CENTRE);
263 else if (!strcmp (new_text, alignment_to_string (ALIGN_RIGHT)))
264 var_set_alignment (var, ALIGN_RIGHT);
268 if (!strcmp (new_text, measure_to_string (MEASURE_NOMINAL)))
269 var_set_measure (var, MEASURE_NOMINAL);
270 else if (!strcmp (new_text, measure_to_string (MEASURE_ORDINAL)))
271 var_set_measure (var, MEASURE_ORDINAL);
272 else if (!strcmp (new_text, measure_to_string (MEASURE_SCALE)))
273 var_set_measure (var, MEASURE_SCALE);
279 render_popup_cell (PsppSheetViewColumn *tree_column,
280 GtkCellRenderer *cell,
285 PsppireVarSheet *var_sheet = user_data;
288 row = GPOINTER_TO_INT (iter->user_data);
290 "editable", row < psppire_dict_get_var_cnt (var_sheet->dict),
295 render_var_cell (PsppSheetViewColumn *tree_column,
296 GtkCellRenderer *cell,
301 PsppireVarSheet *var_sheet = user_data;
302 const struct fmt_spec *print;
303 enum vs_column column_id;
304 struct variable *var;
307 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
308 "column-number")) - 1;
309 row = GPOINTER_TO_INT (iter->user_data);
311 if (row >= psppire_dict_get_var_cnt (var_sheet->dict))
315 "editable", column_id == VS_NAME,
317 if (column_id == VS_WIDTH
318 || column_id == VS_DECIMALS
319 || column_id == VS_COLUMNS)
320 g_object_set (cell, "adjustment", NULL, NULL);
324 var = psppire_dict_get_variable (var_sheet->dict, row);
326 print = var_get_print_format (var);
331 "text", var_get_name (var),
338 "text", fmt_gui_name (print->type),
345 int step = fmt_step_width (print->type);
346 if (var_is_numeric (var))
347 set_spin_cell (cell, print->w,
348 fmt_min_width (print->type, var_sheet->format_use),
349 fmt_max_width (print->type, var_sheet->format_use),
352 set_spin_cell (cell, print->w, 0, 0, step);
357 if (fmt_takes_decimals (print->type))
359 int max_w = fmt_max_width (print->type, var_sheet->format_use);
360 int max_d = fmt_max_decimals (print->type, max_w,
361 var_sheet->format_use);
362 set_spin_cell (cell, print->d, 0, max_d, 1);
374 "text", var_has_label (var) ? var_get_label (var) : "",
380 g_object_set (cell, "editable", FALSE, NULL);
381 if ( ! var_has_value_labels (var))
382 g_object_set (cell, "text", _("None"), NULL);
385 const struct val_labs *vls = var_get_value_labels (var);
386 const struct val_lab **labels = val_labs_sorted (vls);
387 const struct val_lab *vl = labels[0];
388 gchar *vstr = value_to_text (vl->value, var);
389 char *text = xasprintf (_("{%s, %s}..."), vstr,
390 val_lab_get_escaped_label (vl));
393 g_object_set (cell, "text", text, NULL);
400 char *text = missing_values_to_string (var_sheet->dict, var, NULL);
410 set_spin_cell (cell, var_get_display_width (var), 1, 2 * MAX_STRING, 1);
415 "text", alignment_to_string (var_get_alignment (var)),
422 "text", measure_to_string (var_get_measure (var)),
429 static struct variable *
430 path_string_to_variable (PsppireVarSheet *var_sheet, gchar *path_string)
436 path = gtk_tree_path_new_from_string (path_string);
437 row = gtk_tree_path_get_indices (path)[0];
438 gtk_tree_path_free (path);
440 dict = psppire_var_sheet_get_dictionary (var_sheet);
441 g_return_val_if_fail (dict != NULL, NULL);
443 return psppire_dict_get_variable (dict, row);
447 on_type_click (PsppireCellRendererButton *cell,
449 PsppireVarSheet *var_sheet)
451 var_sheet->var_type_dialog->pv = path_string_to_variable (var_sheet, path);
452 var_type_dialog_show (var_sheet->var_type_dialog);
456 on_value_labels_click (PsppireCellRendererButton *cell,
458 PsppireVarSheet *var_sheet)
460 struct variable *var = path_string_to_variable (var_sheet, path);
461 val_labs_dialog_set_target_variable (var_sheet->val_labs_dialog, var);
462 val_labs_dialog_show (var_sheet->val_labs_dialog);
466 on_missing_values_click (PsppireCellRendererButton *cell,
468 PsppireVarSheet *var_sheet)
470 var_sheet->missing_val_dialog->pv = path_string_to_variable (var_sheet,
472 missing_val_dialog_show (var_sheet->missing_val_dialog);
476 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
480 g_object_set (G_OBJECT (renderer),
481 PSPPIRE_IS_CELL_RENDERER_BUTTON (renderer) ? "label" : "text",
482 string, (void *) NULL);
483 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
484 NULL, NULL, NULL, &width, NULL);
489 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
496 ds_put_byte_multiple (&s, '0', char_cnt);
497 ds_put_byte (&s, ' ');
498 width = get_string_width (treeview, renderer, ds_cstr (&s));
504 static PsppSheetViewColumn *
505 add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer,
506 enum vs_column column_id,
507 const char *title, int width)
509 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
510 int title_width, content_width;
511 PsppSheetViewColumn *column;
513 column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL);
514 g_object_set_data (G_OBJECT (column), "column-number",
515 GINT_TO_POINTER (column_id) + 1);
517 pspp_sheet_view_column_set_cell_data_func (
518 column, renderer, render_var_cell, var_sheet, NULL);
520 title_width = get_string_width (sheet_view, renderer, title);
521 content_width = get_monospace_width (sheet_view, renderer, width);
522 g_object_set_data (G_OBJECT (column), "content-width",
523 GINT_TO_POINTER (content_width));
525 pspp_sheet_view_column_set_fixed_width (column,
526 MAX (title_width, content_width));
527 pspp_sheet_view_column_set_resizable (column, TRUE);
529 pspp_sheet_view_append_column (sheet_view, column);
531 g_signal_connect (renderer, "edited",
532 G_CALLBACK (on_var_column_edited),
534 g_object_set_data (G_OBJECT (renderer), "column-id",
535 GINT_TO_POINTER (column_id));
536 g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet);
541 static PsppSheetViewColumn *
542 add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
543 const char *title, int width)
545 return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (),
546 column_id, title, width);
549 static PsppSheetViewColumn *
550 add_spin_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
551 const char *title, int width)
553 return add_var_sheet_column (var_sheet, gtk_cell_renderer_spin_new (),
554 column_id, title, width);
557 static PsppSheetViewColumn *
558 add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
559 const char *title, int width,
562 GtkCellRenderer *cell;
567 store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
568 va_start (args, width);
569 while ((name = va_arg (args, const char *)) != NULL)
571 int value = va_arg (args, int);
572 gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
579 cell = gtk_cell_renderer_combo_new ();
582 "model", GTK_TREE_MODEL (store),
586 return add_var_sheet_column (var_sheet, cell, column_id, title, width);
591 add_popup_menu (PsppireVarSheet *var_sheet,
592 PsppSheetViewColumn *column,
593 void (*on_click) (PsppireCellRendererButton *,
595 PsppireVarSheet *var_sheet))
597 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
598 const char *button_label = "...";
599 GtkCellRenderer *button_renderer;
602 button_renderer = psppire_cell_renderer_button_new ();
603 g_object_set (button_renderer,
604 "label", button_label,
607 g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click),
609 pspp_sheet_view_column_pack_start (column, button_renderer, FALSE);
610 pspp_sheet_view_column_set_cell_data_func (
611 column, button_renderer, render_popup_cell, var_sheet, NULL);
613 content_width = GPOINTER_TO_INT (g_object_get_data (
614 G_OBJECT (column), "content-width"));
615 content_width += get_string_width (sheet_view, button_renderer,
617 if (content_width > pspp_sheet_view_column_get_fixed_width (column))
618 pspp_sheet_view_column_set_fixed_width (column, content_width);
622 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
623 gint wx, gint wy, size_t *row, size_t *column)
625 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
629 PsppSheetViewColumn *tree_column;
630 GtkTreeModel *tree_model;
634 /* Check that WIDGET is really visible on the screen before we
635 do anything else. This is a bug fix for a sticky situation:
636 when text_data_import_assistant() returns, it frees the data
637 necessary to compose the tool tip message, but there may be
638 a tool tip under preparation at that point (even if there is
639 no visible tool tip) that will call back into us a little
640 bit later. Perhaps the correct solution to this problem is
641 to make the data related to the tool tips part of a GObject
642 that only gets destroyed when all references are released,
643 but this solution appears to be effective too. */
644 if (!gtk_widget_get_mapped (widget))
647 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
649 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
650 &path, &tree_column, NULL, NULL))
653 column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number");
654 if (column_ptr == NULL)
656 *column = GPOINTER_TO_INT (column_ptr) - 1;
658 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
661 tree_model = pspp_sheet_view_get_model (tree_view);
662 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
663 gtk_tree_path_free (path);
667 *row = GPOINTER_TO_INT (iter.user_data);
672 on_query_var_tooltip (GtkWidget *widget, gint wx, gint wy,
673 gboolean keyboard_mode UNUSED,
674 GtkTooltip *tooltip, gpointer *user_data UNUSED)
676 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
678 struct variable *var;
681 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
684 dict = psppire_var_sheet_get_dictionary (var_sheet);
685 g_return_val_if_fail (dict != NULL, FALSE);
687 if (row >= psppire_dict_get_var_cnt (dict))
689 gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a "
694 var = psppire_dict_get_variable (dict, row);
695 g_return_val_if_fail (var != NULL, FALSE);
701 char text[FMT_STRING_LEN_MAX + 1];
703 fmt_to_string (var_get_print_format (var), text);
704 gtk_tooltip_set_text (tooltip, text);
709 if (var_has_value_labels (var))
711 const struct val_labs *vls = var_get_value_labels (var);
712 const struct val_lab **labels = val_labs_sorted (vls);
717 for (i = 0; i < val_labs_count (vls); i++)
719 const struct val_lab *vl = labels[i];
722 if (i >= 10 || ds_length (&s) > 500)
724 ds_put_cstr (&s, "...");
728 vstr = value_to_text (vl->value, var);
729 ds_put_format (&s, _("{%s, %s}\n"), vstr,
730 val_lab_get_escaped_label (vl));
734 ds_chomp_byte (&s, '\n');
736 gtk_tooltip_set_text (tooltip, ds_cstr (&s));
747 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
749 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
752 menu = get_widget_assert (var_sheet->builder, "varsheet-variable-popup");
753 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
757 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
759 do_popup_menu (widget, 0, gtk_get_current_event_time ());
763 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
764 gpointer user_data UNUSED)
766 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
768 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
770 PsppSheetSelection *selection;
772 selection = pspp_sheet_view_get_selection (sheet_view);
773 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
777 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
778 &path, NULL, NULL, NULL))
780 pspp_sheet_selection_unselect_all (selection);
781 pspp_sheet_selection_select_path (selection, path);
782 gtk_tree_path_free (path);
786 do_popup_menu (widget, event->button, event->time);
794 psppire_fmt_use_get_type (void)
796 static GType etype = 0;
799 static const GEnumValue values[] =
801 { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
802 { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
806 etype = g_enum_register_static
807 (g_intern_static_string ("PsppireFmtUse"), values);
816 PROP_MAY_CREATE_VARS,
817 PROP_MAY_DELETE_VARS,
823 psppire_var_sheet_set_property (GObject *object,
828 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
832 case PROP_DICTIONARY:
833 psppire_var_sheet_set_dictionary (obj,
834 PSPPIRE_DICT (g_value_get_object (
838 case PROP_MAY_CREATE_VARS:
839 psppire_var_sheet_set_may_create_vars (obj,
840 g_value_get_boolean (value));
843 case PROP_MAY_DELETE_VARS:
844 psppire_var_sheet_set_may_delete_vars (obj,
845 g_value_get_boolean (value));
848 case PROP_FORMAT_TYPE:
849 obj->format_use = g_value_get_enum (value);
852 case PROP_UI_MANAGER:
854 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
860 psppire_var_sheet_get_property (GObject *object,
865 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
869 case PROP_DICTIONARY:
870 g_value_set_object (value,
871 G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
874 case PROP_MAY_CREATE_VARS:
875 g_value_set_boolean (value, obj->may_create_vars);
878 case PROP_MAY_DELETE_VARS:
879 g_value_set_boolean (value, obj->may_delete_vars);
882 case PROP_FORMAT_TYPE:
883 g_value_set_enum (value, obj->format_use);
886 case PROP_UI_MANAGER:
887 g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj));
891 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
897 psppire_var_sheet_realize (GtkWidget *w)
899 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (w);
902 GTK_WIDGET_CLASS (psppire_var_sheet_parent_class)->realize (w);
904 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (w));
905 var_sheet->val_labs_dialog = val_labs_dialog_create (toplevel);
906 var_sheet->missing_val_dialog = missing_val_dialog_create (toplevel);
907 var_sheet->var_type_dialog = var_type_dialog_create (toplevel);
911 psppire_var_sheet_dispose (GObject *obj)
913 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
916 if (var_sheet->dispose_has_run)
919 var_sheet->dispose_has_run = TRUE;
921 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
922 if ( var_sheet->dict_signals[i])
923 g_signal_handler_disconnect (var_sheet->dict,
924 var_sheet->dict_signals[i]);
927 g_object_unref (var_sheet->dict);
930 /* These dialogs are not GObjects (although they should be!)
931 But for now, unreffing them only causes a GCritical Error
932 so comment them out for now. (and accept the memory leakage)
934 g_object_unref (var_sheet->val_labs_dialog);
935 g_object_unref (var_sheet->missing_val_dialog);
936 g_object_unref (var_sheet->var_type_dialog);
939 G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
943 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
945 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
946 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
949 gobject_class->set_property = psppire_var_sheet_set_property;
950 gobject_class->get_property = psppire_var_sheet_get_property;
951 gobject_class->dispose = psppire_var_sheet_dispose;
953 widget_class->realize = psppire_var_sheet_realize;
955 g_signal_new ("var-double-clicked",
956 G_OBJECT_CLASS_TYPE (gobject_class),
959 g_signal_accumulator_true_handled, NULL,
960 psppire_marshal_BOOLEAN__INT,
961 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
963 pspec = g_param_spec_object ("dictionary",
964 "Dictionary displayed by the sheet",
965 "The PsppireDict that the sheet displays "
966 "may allow the user to edit",
969 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
971 pspec = g_param_spec_boolean ("may-create-vars",
972 "May create variables",
973 "Whether the user may create more variables",
976 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
978 pspec = g_param_spec_boolean ("may-delete-vars",
979 "May delete variables",
980 "Whether the user may delete variables",
983 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
985 pspec = g_param_spec_enum ("format-use",
986 "Use of variable format",
987 ("Whether variables have input or output "
989 PSPPIRE_TYPE_FMT_USE,
992 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
994 pspec = g_param_spec_object ("ui-manager",
996 "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.",
999 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1003 render_row_number_cell (PsppSheetViewColumn *tree_column,
1004 GtkCellRenderer *cell,
1005 GtkTreeModel *model,
1009 PsppireVarSheet *var_sheet = user_data;
1010 GValue gvalue = { 0, };
1013 row = GPOINTER_TO_INT (iter->user_data);
1015 g_value_init (&gvalue, G_TYPE_INT);
1016 g_value_set_int (&gvalue, row + 1);
1017 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1018 g_value_unset (&gvalue);
1020 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1021 g_object_set (cell, "editable", TRUE, NULL);
1023 g_object_set (cell, "editable", FALSE, NULL);
1027 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1029 PsppireVarSheet *var_sheet)
1033 g_return_if_fail (var_sheet->dict != NULL);
1035 path = gtk_tree_path_new_from_string (path_string);
1036 if (gtk_tree_path_get_depth (path) == 1)
1038 gint *indices = gtk_tree_path_get_indices (path);
1039 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1042 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1043 indices[0], &handled);
1046 gtk_tree_path_free (path);
1049 static PsppSheetViewColumn *
1050 make_row_number_column (PsppireVarSheet *var_sheet)
1052 PsppSheetViewColumn *column;
1053 GtkCellRenderer *renderer;
1055 renderer = psppire_cell_renderer_button_new ();
1056 g_object_set (renderer, "xalign", 1.0, NULL);
1057 g_signal_connect (renderer, "double-clicked",
1058 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1061 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1063 pspp_sheet_view_column_set_cell_data_func (
1064 column, renderer, render_row_number_cell, var_sheet, NULL);
1065 pspp_sheet_view_column_set_fixed_width (column, 50);
1070 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1072 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1073 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1074 PsppireDict *dict = var_sheet->dict;
1075 const struct range_set_node *node;
1076 struct range_set *selected;
1078 selected = pspp_sheet_selection_get_range_set (selection);
1079 for (node = range_set_last (selected); node != NULL;
1080 node = range_set_prev (selected, node))
1084 for (i = 1; i <= range_set_node_get_width (node); i++)
1086 unsigned long row = range_set_node_get_end (node) - i;
1087 if (row >= 0 && row < psppire_dict_get_var_cnt (dict))
1088 psppire_dict_delete_variables (dict, row, 1);
1091 range_set_destroy (selected);
1095 on_selection_changed (PsppSheetSelection *selection,
1096 gpointer user_data UNUSED)
1098 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1099 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1100 gint n_selected_rows;
1101 gboolean may_delete;
1105 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1107 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1108 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1109 && n_selected_rows > 0));
1111 switch (n_selected_rows)
1118 /* The row used for inserting new variables cannot be deleted. */
1119 path = gtk_tree_path_new_from_indices (
1120 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1121 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1122 gtk_tree_path_free (path);
1129 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1130 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1134 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1136 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1137 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1138 PsppireDict *dict = var_sheet->dict;
1139 struct range_set *selected;
1142 selected = pspp_sheet_selection_get_range_set (selection);
1143 row = range_set_scan (selected, 0);
1144 range_set_destroy (selected);
1146 if (row <= psppire_dict_get_var_cnt (dict))
1149 if (psppire_dict_generate_name (dict, name, sizeof name))
1150 psppire_dict_insert_variable (dict, row, name);
1155 psppire_var_sheet_init (PsppireVarSheet *obj)
1157 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1158 PsppSheetViewColumn *column;
1163 obj->format_use = PSPPIRE_TYPE_FMT_USE;
1164 obj->may_create_vars = TRUE;
1165 obj->may_delete_vars = TRUE;
1167 obj->scroll_to_bottom_signal = 0;
1169 obj->container = NULL;
1170 obj->dispose_has_run = FALSE;
1172 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1174 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1175 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1176 g_signal_connect (list->data, "editing-started",
1177 G_CALLBACK (on_name_column_editing_started), NULL);
1180 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1181 add_popup_menu (obj, column, on_type_click);
1183 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1185 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1187 add_text_column (obj, VS_LABEL, _("Label"), 20);
1189 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1190 add_popup_menu (obj, column, on_value_labels_click);
1192 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1193 add_popup_menu (obj, column, on_missing_values_click);
1195 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1197 add_combo_column (obj, VS_ALIGN, _("Align"), 6,
1198 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1199 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1200 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1203 add_combo_column (obj, VS_MEASURE, _("Measure"), 10,
1204 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1205 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1206 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1209 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1210 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1211 PSPP_SHEET_SELECTION_MULTIPLE);
1213 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1214 g_signal_connect (obj, "query-tooltip",
1215 G_CALLBACK (on_query_var_tooltip), NULL);
1216 g_signal_connect (obj, "button-press-event",
1217 G_CALLBACK (on_button_pressed), NULL);
1218 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1220 obj->builder = builder_new ("var-sheet.ui");
1222 action = get_action_assert (obj->builder, "edit_clear-variables");
1223 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1225 gtk_action_set_sensitive (action, FALSE);
1226 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1227 "changed", G_CALLBACK (on_selection_changed), NULL);
1229 action = get_action_assert (obj->builder, "edit_insert-variable");
1230 gtk_action_set_sensitive (action, FALSE);
1231 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1236 psppire_var_sheet_new (void)
1238 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1242 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1244 return var_sheet->dict;
1248 refresh_model (PsppireVarSheet *var_sheet)
1250 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1252 if (var_sheet->dict != NULL)
1254 PsppireEmptyListStore *store;
1257 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1258 + var_sheet->may_create_vars);
1259 store = psppire_empty_list_store_new (n_rows);
1260 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1261 GTK_TREE_MODEL (store));
1262 g_object_unref (store);
1267 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1269 PsppireEmptyListStore *store;
1272 g_return_if_fail (dict == var_sheet->dict);
1274 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1275 PSPP_SHEET_VIEW (var_sheet)));
1276 g_return_if_fail (store != NULL);
1278 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1279 + var_sheet->may_create_vars);
1280 psppire_empty_list_store_set_n_rows (store, n_rows);
1281 psppire_empty_list_store_row_inserted (store, row);
1285 on_var_deleted (PsppireDict *dict,
1286 const struct variable *var, int dict_idx, int case_idx,
1287 PsppireVarSheet *var_sheet)
1289 PsppireEmptyListStore *store;
1292 g_return_if_fail (dict == var_sheet->dict);
1294 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1295 PSPP_SHEET_VIEW (var_sheet)));
1296 g_return_if_fail (store != NULL);
1298 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1299 + var_sheet->may_create_vars);
1300 psppire_empty_list_store_set_n_rows (store, n_rows);
1301 psppire_empty_list_store_row_deleted (store, dict_idx);
1305 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1307 g_return_if_fail (dict == var_sheet->dict);
1308 refresh_model (var_sheet);
1312 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1315 if (var_sheet->dict != NULL)
1319 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1321 if (var_sheet->dict_signals[i])
1322 g_signal_handler_disconnect (var_sheet->dict,
1323 var_sheet->dict_signals[i]);
1325 var_sheet->dict_signals[i] = 0;
1328 g_object_unref (var_sheet->dict);
1331 var_sheet->dict = dict;
1335 g_object_ref (dict);
1337 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1338 = g_signal_connect (dict, "backend-changed",
1339 G_CALLBACK (on_backend_changed), var_sheet);
1341 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1342 = g_signal_connect (dict, "variable-inserted",
1343 G_CALLBACK (on_var_inserted), var_sheet);
1345 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1346 = g_signal_connect (dict, "variable-deleted",
1347 G_CALLBACK (on_var_deleted), var_sheet);
1350 refresh_model (var_sheet);
1354 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1356 return var_sheet->may_create_vars;
1360 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1361 gboolean may_create_vars)
1363 if (var_sheet->may_create_vars != may_create_vars)
1365 PsppireEmptyListStore *store;
1368 var_sheet->may_create_vars = may_create_vars;
1370 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1371 PSPP_SHEET_VIEW (var_sheet)));
1372 g_return_if_fail (store != NULL);
1374 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1375 + var_sheet->may_create_vars);
1376 psppire_empty_list_store_set_n_rows (store, n_rows);
1378 if (may_create_vars)
1379 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1381 psppire_empty_list_store_row_deleted (store, n_rows);
1383 on_selection_changed (pspp_sheet_view_get_selection (
1384 PSPP_SHEET_VIEW (var_sheet)), NULL);
1389 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1391 return var_sheet->may_delete_vars;
1395 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1396 gboolean may_delete_vars)
1398 if (var_sheet->may_delete_vars != may_delete_vars)
1400 var_sheet->may_delete_vars = may_delete_vars;
1401 on_selection_changed (pspp_sheet_view_get_selection (
1402 PSPP_SHEET_VIEW (var_sheet)), NULL);
1407 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1409 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1412 path = gtk_tree_path_new_from_indices (dict_index, -1);
1413 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1414 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1415 gtk_tree_path_free (path);
1419 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1421 return GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1423 GTK_TYPE_UI_MANAGER));