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_destroy (GtkObject *obj)
913 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
915 GTK_OBJECT_CLASS (psppire_var_sheet_parent_class)->destroy (obj);
917 psppire_var_sheet_set_dictionary (var_sheet, NULL);
919 if (var_sheet->val_labs_dialog)
921 g_object_unref (var_sheet->val_labs_dialog);
922 var_sheet->val_labs_dialog = NULL;
925 if (var_sheet->missing_val_dialog)
927 g_object_unref (var_sheet->missing_val_dialog);
928 var_sheet->missing_val_dialog = NULL;
931 if (var_sheet->var_type_dialog)
933 g_object_unref (var_sheet->var_type_dialog);
934 var_sheet->var_type_dialog = NULL;
939 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
941 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
942 GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
943 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
946 gobject_class->set_property = psppire_var_sheet_set_property;
947 gobject_class->get_property = psppire_var_sheet_get_property;
949 widget_class->realize = psppire_var_sheet_realize;
951 gtk_object_class->destroy = psppire_var_sheet_destroy;
953 g_signal_new ("var-double-clicked",
954 G_OBJECT_CLASS_TYPE (gobject_class),
957 g_signal_accumulator_true_handled, NULL,
958 psppire_marshal_BOOLEAN__INT,
959 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
961 pspec = g_param_spec_object ("dictionary",
962 "Dictionary displayed by the sheet",
963 "The PsppireDict that the sheet displays "
964 "may allow the user to edit",
967 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
969 pspec = g_param_spec_boolean ("may-create-vars",
970 "May create variables",
971 "Whether the user may create more variables",
974 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
976 pspec = g_param_spec_boolean ("may-delete-vars",
977 "May delete variables",
978 "Whether the user may delete variables",
981 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
983 pspec = g_param_spec_enum ("format-use",
984 "Use of variable format",
985 ("Whether variables have input or output "
987 PSPPIRE_TYPE_FMT_USE,
990 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
992 pspec = g_param_spec_object ("ui-manager",
994 "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.",
997 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1001 render_row_number_cell (PsppSheetViewColumn *tree_column,
1002 GtkCellRenderer *cell,
1003 GtkTreeModel *model,
1007 PsppireVarSheet *var_sheet = user_data;
1008 GValue gvalue = { 0, };
1011 row = GPOINTER_TO_INT (iter->user_data);
1013 g_value_init (&gvalue, G_TYPE_INT);
1014 g_value_set_int (&gvalue, row + 1);
1015 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1016 g_value_unset (&gvalue);
1018 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1019 g_object_set (cell, "editable", TRUE, NULL);
1021 g_object_set (cell, "editable", FALSE, NULL);
1025 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1027 PsppireVarSheet *var_sheet)
1031 g_return_if_fail (var_sheet->dict != NULL);
1033 path = gtk_tree_path_new_from_string (path_string);
1034 if (gtk_tree_path_get_depth (path) == 1)
1036 gint *indices = gtk_tree_path_get_indices (path);
1037 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1040 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1041 indices[0], &handled);
1044 gtk_tree_path_free (path);
1047 static PsppSheetViewColumn *
1048 make_row_number_column (PsppireVarSheet *var_sheet)
1050 PsppSheetViewColumn *column;
1051 GtkCellRenderer *renderer;
1053 renderer = psppire_cell_renderer_button_new ();
1054 g_object_set (renderer, "xalign", 1.0, NULL);
1055 g_signal_connect (renderer, "double-clicked",
1056 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1059 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1061 pspp_sheet_view_column_set_cell_data_func (
1062 column, renderer, render_row_number_cell, var_sheet, NULL);
1063 pspp_sheet_view_column_set_fixed_width (column, 50);
1068 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1070 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1071 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1072 PsppireDict *dict = var_sheet->dict;
1073 const struct range_set_node *node;
1074 struct range_set *selected;
1076 selected = pspp_sheet_selection_get_range_set (selection);
1077 for (node = range_set_last (selected); node != NULL;
1078 node = range_set_prev (selected, node))
1082 for (i = 1; i <= range_set_node_get_width (node); i++)
1084 unsigned long row = range_set_node_get_end (node) - i;
1085 if (row >= 0 && row < psppire_dict_get_var_cnt (dict))
1086 psppire_dict_delete_variables (dict, row, 1);
1089 range_set_destroy (selected);
1093 on_selection_changed (PsppSheetSelection *selection,
1094 gpointer user_data UNUSED)
1096 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1097 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1098 gint n_selected_rows;
1099 gboolean may_delete;
1103 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1105 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1106 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1107 && n_selected_rows > 0));
1109 switch (n_selected_rows)
1116 /* The row used for inserting new variables cannot be deleted. */
1117 path = gtk_tree_path_new_from_indices (
1118 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1119 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1120 gtk_tree_path_free (path);
1127 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1128 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1132 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1134 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1135 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1136 PsppireDict *dict = var_sheet->dict;
1137 struct range_set *selected;
1140 selected = pspp_sheet_selection_get_range_set (selection);
1141 row = range_set_scan (selected, 0);
1142 range_set_destroy (selected);
1144 if (row <= psppire_dict_get_var_cnt (dict))
1147 if (psppire_dict_generate_name (dict, name, sizeof name))
1148 psppire_dict_insert_variable (dict, row, name);
1153 psppire_var_sheet_init (PsppireVarSheet *obj)
1155 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1156 PsppSheetViewColumn *column;
1161 obj->format_use = PSPPIRE_TYPE_FMT_USE;
1162 obj->may_create_vars = TRUE;
1163 obj->may_delete_vars = TRUE;
1165 obj->scroll_to_bottom_signal = 0;
1167 obj->container = NULL;
1169 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1171 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1172 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1173 g_signal_connect (list->data, "editing-started",
1174 G_CALLBACK (on_name_column_editing_started), NULL);
1177 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1178 add_popup_menu (obj, column, on_type_click);
1180 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1182 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1184 add_text_column (obj, VS_LABEL, _("Label"), 20);
1186 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1187 add_popup_menu (obj, column, on_value_labels_click);
1189 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1190 add_popup_menu (obj, column, on_missing_values_click);
1192 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1194 add_combo_column (obj, VS_ALIGN, _("Align"), 6,
1195 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1196 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1197 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1200 add_combo_column (obj, VS_MEASURE, _("Measure"), 10,
1201 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1202 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1203 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1206 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1207 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1208 PSPP_SHEET_SELECTION_MULTIPLE);
1210 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1211 g_signal_connect (obj, "query-tooltip",
1212 G_CALLBACK (on_query_var_tooltip), NULL);
1213 g_signal_connect (obj, "button-press-event",
1214 G_CALLBACK (on_button_pressed), NULL);
1215 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1217 obj->builder = builder_new ("var-sheet.ui");
1219 action = get_action_assert (obj->builder, "edit_clear-variables");
1220 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1222 gtk_action_set_sensitive (action, FALSE);
1223 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1224 "changed", G_CALLBACK (on_selection_changed), NULL);
1226 action = get_action_assert (obj->builder, "edit_insert-variable");
1227 gtk_action_set_sensitive (action, FALSE);
1228 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1233 psppire_var_sheet_new (void)
1235 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1239 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1241 return var_sheet->dict;
1245 refresh_model (PsppireVarSheet *var_sheet)
1247 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1249 if (var_sheet->dict != NULL)
1251 PsppireEmptyListStore *store;
1254 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1255 + var_sheet->may_create_vars);
1256 store = psppire_empty_list_store_new (n_rows);
1257 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1258 GTK_TREE_MODEL (store));
1259 g_object_unref (store);
1264 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1266 PsppireEmptyListStore *store;
1269 g_return_if_fail (dict == var_sheet->dict);
1271 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1272 PSPP_SHEET_VIEW (var_sheet)));
1273 g_return_if_fail (store != NULL);
1275 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1276 + var_sheet->may_create_vars);
1277 psppire_empty_list_store_set_n_rows (store, n_rows);
1278 psppire_empty_list_store_row_inserted (store, row);
1282 on_var_deleted (PsppireDict *dict,
1283 const struct variable *var, int dict_idx, int case_idx,
1284 PsppireVarSheet *var_sheet)
1286 PsppireEmptyListStore *store;
1289 g_return_if_fail (dict == var_sheet->dict);
1291 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1292 PSPP_SHEET_VIEW (var_sheet)));
1293 g_return_if_fail (store != NULL);
1295 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1296 + var_sheet->may_create_vars);
1297 psppire_empty_list_store_set_n_rows (store, n_rows);
1298 psppire_empty_list_store_row_deleted (store, dict_idx);
1302 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1304 g_return_if_fail (dict == var_sheet->dict);
1305 refresh_model (var_sheet);
1309 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1319 if (var_sheet->dict != NULL)
1321 if (var_sheet->dict_signals)
1325 for (i = 0; i < N_SIGNALS; i++)
1326 g_signal_handler_disconnect (var_sheet->dict,
1327 var_sheet->dict_signals[i]);
1329 g_free (var_sheet->dict_signals);
1330 var_sheet->dict_signals = NULL;
1332 g_object_unref (var_sheet->dict);
1335 var_sheet->dict = dict;
1339 g_object_ref (dict);
1341 var_sheet->dict_signals = g_malloc0 (
1342 N_SIGNALS * sizeof *var_sheet->dict_signals);
1344 var_sheet->dict_signals[BACKEND_CHANGED]
1345 = g_signal_connect (dict, "backend-changed",
1346 G_CALLBACK (on_backend_changed), var_sheet);
1348 var_sheet->dict_signals[VARIABLE_DELETED]
1349 = g_signal_connect (dict, "variable-inserted",
1350 G_CALLBACK (on_var_inserted), var_sheet);
1352 var_sheet->dict_signals[VARIABLE_INSERTED]
1353 = g_signal_connect (dict, "variable-deleted",
1354 G_CALLBACK (on_var_deleted), var_sheet);
1356 refresh_model (var_sheet);
1360 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1362 return var_sheet->may_create_vars;
1366 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1367 gboolean may_create_vars)
1369 if (var_sheet->may_create_vars != may_create_vars)
1371 PsppireEmptyListStore *store;
1374 var_sheet->may_create_vars = may_create_vars;
1376 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1377 PSPP_SHEET_VIEW (var_sheet)));
1378 g_return_if_fail (store != NULL);
1380 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1381 + var_sheet->may_create_vars);
1382 psppire_empty_list_store_set_n_rows (store, n_rows);
1384 if (may_create_vars)
1385 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1387 psppire_empty_list_store_row_deleted (store, n_rows);
1389 on_selection_changed (pspp_sheet_view_get_selection (
1390 PSPP_SHEET_VIEW (var_sheet)), NULL);
1395 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1397 return var_sheet->may_delete_vars;
1401 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1402 gboolean may_delete_vars)
1404 if (var_sheet->may_delete_vars != may_delete_vars)
1406 var_sheet->may_delete_vars = may_delete_vars;
1407 on_selection_changed (pspp_sheet_view_get_selection (
1408 PSPP_SHEET_VIEW (var_sheet)), NULL);
1413 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1415 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1418 path = gtk_tree_path_new_from_indices (dict_index, -1);
1419 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1420 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1421 gtk_tree_path_free (path);
1425 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1427 return GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1429 GTK_TYPE_UI_MANAGER));