1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2011, 2012, 2013, 2014 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-dictview.h"
33 #include "ui/gui/psppire-empty-list-store.h"
34 #include "ui/gui/psppire-marshal.h"
35 #include "ui/gui/val-labs-dialog.h"
36 #include "ui/gui/var-type-dialog.h"
37 #include "ui/gui/var-display.h"
38 #include "ui/gui/var-type-dialog.h"
40 #include "gl/intprops.h"
43 #define _(msgid) gettext (msgid)
44 #define N_(msgid) msgid
61 G_DEFINE_TYPE (PsppireVarSheet, psppire_var_sheet, PSPP_TYPE_SHEET_VIEW);
64 set_spin_cell (GtkCellRenderer *cell, int value, int min, int max, int step)
66 char text[INT_BUFSIZE_BOUND (int)];
67 GtkAdjustment *adjust;
70 adjust = GTK_ADJUSTMENT (gtk_adjustment_new (value, min, max,
75 sprintf (text, "%d", value);
84 error_dialog (GtkWindow *w, gchar *primary_text, gchar *secondary_text)
87 gtk_message_dialog_new (w,
88 GTK_DIALOG_DESTROY_WITH_PARENT,
90 GTK_BUTTONS_CLOSE, "%s", primary_text);
92 g_object_set (dialog, "icon-name", "psppicon", NULL);
94 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
95 "%s", secondary_text);
97 gtk_dialog_run (GTK_DIALOG (dialog));
99 gtk_widget_destroy (dialog);
103 on_name_column_editing_started (GtkCellRenderer *cell,
104 GtkCellEditable *editable,
108 PsppireVarSheet *var_sheet = g_object_get_data (G_OBJECT (cell), "var-sheet");
109 PsppireDict *dict = psppire_var_sheet_get_dictionary (var_sheet);
111 g_return_if_fail (var_sheet);
112 g_return_if_fail (dict);
114 if (GTK_IS_ENTRY (editable))
116 GtkEntry *entry = GTK_ENTRY (editable);
117 if (gtk_entry_get_text (entry)[0] == '\0')
120 if (psppire_dict_generate_name (dict, name, sizeof name))
121 gtk_entry_set_text (entry, name);
127 scroll_to_bottom (GtkWidget *widget,
128 GtkRequisition *requisition,
129 gpointer unused UNUSED)
131 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
132 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
133 GtkAdjustment *vadjust;
135 vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
136 gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
138 if (var_sheet->scroll_to_bottom_signal)
140 g_signal_handler_disconnect (var_sheet,
141 var_sheet->scroll_to_bottom_signal);
142 var_sheet->scroll_to_bottom_signal = 0;
146 static struct variable *
147 path_string_to_var (PsppireVarSheet *var_sheet, gchar *path_string)
152 path = gtk_tree_path_new_from_string (path_string);
153 row = gtk_tree_path_get_indices (path)[0];
154 gtk_tree_path_free (path);
156 return psppire_dict_get_variable (var_sheet->dict, row);
160 on_var_column_edited (GtkCellRendererText *cell,
165 PsppireVarSheet *var_sheet = user_data;
166 GtkWindow *window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (var_sheet)));
167 struct dictionary *dict = var_sheet->dict->dict;
168 enum vs_column column_id;
169 struct variable *var;
172 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell),
175 var = path_string_to_var (var_sheet, path_string);
178 g_return_if_fail (column_id == VS_NAME);
180 if (!dict_id_is_valid (dict, new_text, false))
181 error_dialog (window,
182 g_strdup (_("Cannot create variable.")),
183 g_strdup_printf (_("\"%s\" is not a valid variable "
184 "name."), new_text));
185 else if (dict_lookup_var (dict, new_text) != NULL)
186 error_dialog (window,
187 g_strdup (_("Cannot create variable.")),
188 g_strdup_printf (_("This dictionary already contains "
189 "a variable named \"%s\"."),
193 dict_create_var (var_sheet->dict->dict, new_text, 0);
194 if (!var_sheet->scroll_to_bottom_signal)
196 gtk_widget_queue_resize (GTK_WIDGET (var_sheet));
197 var_sheet->scroll_to_bottom_signal =
198 g_signal_connect (var_sheet, "size-allocate",
199 G_CALLBACK (scroll_to_bottom), NULL);
209 if (!dict_id_is_valid (dict, new_text, false))
210 error_dialog (window,
211 g_strdup (_("Cannot rename variable.")),
212 g_strdup_printf (_("\"%s\" is not a valid variable "
213 "name."), new_text));
214 else if (dict_lookup_var (dict, new_text) != NULL
215 && dict_lookup_var (dict, new_text) != var)
216 error_dialog (window,
217 g_strdup (_("Cannot rename variable.")),
218 g_strdup_printf (_("This dictionary already contains "
219 "a variable named \"%s\"."),
222 dict_rename_var (dict, var, new_text);
230 width = atoi (new_text);
233 struct fmt_spec format;
235 format = *var_get_print_format (var);
236 fmt_change_width (&format, width, var_sheet->format_use);
237 var_set_width (var, fmt_var_width (&format));
238 var_set_both_formats (var, &format);
243 decimals = atoi (new_text);
246 struct fmt_spec format;
248 format = *var_get_print_format (var);
249 fmt_change_decimals (&format, decimals, var_sheet->format_use);
250 var_set_print_format (var, &format);
255 var_set_label (var, new_text);
263 width = atoi (new_text);
264 if (width > 0 && width < 2 * MAX_STRING)
265 var_set_display_width (var, width);
269 if (!strcmp (new_text, alignment_to_string (ALIGN_LEFT)))
270 var_set_alignment (var, ALIGN_LEFT);
271 else if (!strcmp (new_text, alignment_to_string (ALIGN_CENTRE)))
272 var_set_alignment (var, ALIGN_CENTRE);
273 else if (!strcmp (new_text, alignment_to_string (ALIGN_RIGHT)))
274 var_set_alignment (var, ALIGN_RIGHT);
278 if (!strcmp (new_text, measure_to_string (MEASURE_NOMINAL)))
279 var_set_measure (var, MEASURE_NOMINAL);
280 else if (!strcmp (new_text, measure_to_string (MEASURE_ORDINAL)))
281 var_set_measure (var, MEASURE_ORDINAL);
282 else if (!strcmp (new_text, measure_to_string (MEASURE_SCALE)))
283 var_set_measure (var, MEASURE_SCALE);
287 if (!strcmp (new_text, var_role_to_string (ROLE_INPUT)))
288 var_set_role (var, ROLE_INPUT);
289 else if (!strcmp (new_text, var_role_to_string (ROLE_TARGET)))
290 var_set_role (var, ROLE_TARGET);
291 else if (!strcmp (new_text, var_role_to_string (ROLE_BOTH)))
292 var_set_role (var, ROLE_BOTH);
293 else if (!strcmp (new_text, var_role_to_string (ROLE_NONE)))
294 var_set_role (var, ROLE_NONE);
295 else if (!strcmp (new_text, var_role_to_string (ROLE_PARTITION)))
296 var_set_role (var, ROLE_PARTITION);
297 else if (!strcmp (new_text, var_role_to_string (ROLE_SPLIT)))
298 var_set_role (var, ROLE_SPLIT);
304 render_popup_cell (PsppSheetViewColumn *tree_column,
305 GtkCellRenderer *cell,
310 PsppireVarSheet *var_sheet = user_data;
313 row = GPOINTER_TO_INT (iter->user_data);
315 "editable", row < psppire_dict_get_var_cnt (var_sheet->dict),
320 get_var_align_stock_id (enum alignment alignment)
324 case ALIGN_LEFT: return "align-left";
325 case ALIGN_CENTRE: return "align-center";
326 case ALIGN_RIGHT: return "align-right";
328 g_return_val_if_reached ("");
333 get_var_role_stock_id (enum var_role role)
337 case ROLE_INPUT: return "role-input";
338 case ROLE_TARGET: return "role-target";
339 case ROLE_BOTH: return "role-both";
340 case ROLE_NONE: return "role-none";
341 case ROLE_PARTITION: return "role-partition";
342 case ROLE_SPLIT: return "role-split";
344 g_return_val_if_reached ("");
349 render_var_cell (PsppSheetViewColumn *tree_column,
350 GtkCellRenderer *cell,
355 PsppireVarSheet *var_sheet = user_data;
356 const struct fmt_spec *print;
357 enum vs_column column_id;
358 struct variable *var;
361 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
362 "column-number")) - 1;
363 row = GPOINTER_TO_INT (iter->user_data);
365 gtk_cell_renderer_set_visible (cell, true);
366 if (row >= psppire_dict_get_var_cnt (var_sheet->dict))
368 if (GTK_IS_CELL_RENDERER_TEXT (cell))
372 "editable", column_id == VS_NAME,
374 if (column_id == VS_WIDTH
375 || column_id == VS_DECIMALS
376 || column_id == VS_COLUMNS)
377 g_object_set (cell, "adjustment", NULL, NULL);
381 gtk_cell_renderer_set_visible (cell, false);
386 var = psppire_dict_get_variable (var_sheet->dict, row);
388 print = var_get_print_format (var);
393 "text", var_get_name (var),
400 "text", fmt_gui_name (print->type),
406 set_spin_cell (cell, print->w,
407 fmt_min_width (print->type, var_sheet->format_use),
408 fmt_max_width (print->type, var_sheet->format_use),
409 fmt_step_width (print->type));
413 if (fmt_takes_decimals (print->type))
415 int max_w = fmt_max_width (print->type, var_sheet->format_use);
416 int max_d = fmt_max_decimals (print->type, max_w,
417 var_sheet->format_use);
418 set_spin_cell (cell, print->d, 0, max_d, 1);
430 "text", var_has_label (var) ? var_get_label (var) : "",
436 g_object_set (cell, "editable", FALSE, NULL);
437 if ( ! var_has_value_labels (var))
438 g_object_set (cell, "text", _("None"), NULL);
441 const struct val_labs *vls = var_get_value_labels (var);
442 const struct val_lab **labels = val_labs_sorted (vls);
443 const struct val_lab *vl = labels[0];
444 gchar *vstr = value_to_text (vl->value, var);
445 char *text = xasprintf (_("{%s, %s}..."), vstr,
446 val_lab_get_escaped_label (vl));
449 g_object_set (cell, "text", text, NULL);
457 char *text = missing_values_to_string (var, NULL);
467 set_spin_cell (cell, var_get_display_width (var), 1, 2 * MAX_STRING, 1);
471 if (GTK_IS_CELL_RENDERER_TEXT (cell))
473 "text", alignment_to_string (var_get_alignment (var)),
477 g_object_set (cell, "icon-name",
478 get_var_align_stock_id (var_get_alignment (var)), NULL);
482 if (GTK_IS_CELL_RENDERER_TEXT (cell))
484 "text", measure_to_string (var_get_measure (var)),
489 enum fmt_type type = var_get_print_format (var)->type;
490 enum measure measure = var_get_measure (var);
492 g_object_set (cell, "icon-name",
493 get_var_measurement_stock_id (type, measure),
499 if (GTK_IS_CELL_RENDERER_TEXT (cell))
501 "text", var_role_to_string (var_get_role (var)),
505 g_object_set (cell, "icon-name",
506 get_var_role_stock_id (var_get_role (var)), NULL);
511 static struct variable *
512 path_string_to_variable (PsppireVarSheet *var_sheet, gchar *path_string)
518 path = gtk_tree_path_new_from_string (path_string);
519 row = gtk_tree_path_get_indices (path)[0];
520 gtk_tree_path_free (path);
522 dict = psppire_var_sheet_get_dictionary (var_sheet);
523 g_return_val_if_fail (dict != NULL, NULL);
525 return psppire_dict_get_variable (dict, row);
529 on_type_click (PsppireCellRendererButton *cell,
531 PsppireVarSheet *var_sheet)
533 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
534 struct fmt_spec format;
535 struct variable *var;
537 var = path_string_to_variable (var_sheet, path);
538 g_return_if_fail (var != NULL);
540 format = *var_get_print_format (var);
541 psppire_var_type_dialog_run (GTK_WINDOW (toplevel), &format);
543 var_set_width_and_formats (var, fmt_var_width (&format), &format, &format);
547 on_value_labels_click (PsppireCellRendererButton *cell,
549 PsppireVarSheet *var_sheet)
551 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
552 struct val_labs *labels;
553 struct variable *var;
555 var = path_string_to_variable (var_sheet, path);
556 g_return_if_fail (var != NULL);
558 labels = psppire_val_labs_dialog_run (GTK_WINDOW (toplevel), var);
561 var_set_value_labels (var, labels);
562 val_labs_destroy (labels);
567 on_missing_values_click (PsppireCellRendererButton *cell,
569 PsppireVarSheet *var_sheet)
571 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
572 struct missing_values mv;
573 struct variable *var;
575 var = path_string_to_variable (var_sheet, path);
576 g_return_if_fail (var != NULL);
578 psppire_missing_val_dialog_run (GTK_WINDOW (toplevel), var, &mv);
579 var_set_missing_values (var, &mv);
584 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
588 g_object_set (G_OBJECT (renderer),
589 PSPPIRE_IS_CELL_RENDERER_BUTTON (renderer) ? "label" : "text",
590 string, (void *) NULL);
592 gtk_cell_renderer_get_preferred_width (renderer, GTK_WIDGET (treeview), NULL, &width);
598 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
605 ds_put_byte_multiple (&s, '0', char_cnt);
606 ds_put_byte (&s, ' ');
607 width = get_string_width (treeview, renderer, ds_cstr (&s));
613 static PsppSheetViewColumn *
614 add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer,
615 enum vs_column column_id,
616 const char *title, int width)
618 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
619 int title_width, content_width;
620 PsppSheetViewColumn *column;
622 column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL);
623 g_object_set_data (G_OBJECT (column), "column-number",
624 GINT_TO_POINTER (column_id) + 1);
626 pspp_sheet_view_column_set_cell_data_func (
627 column, renderer, render_var_cell, var_sheet, NULL);
629 title_width = get_string_width (sheet_view, renderer, title);
630 content_width = get_monospace_width (sheet_view, renderer, width);
631 g_object_set_data (G_OBJECT (column), "content-width",
632 GINT_TO_POINTER (content_width));
634 pspp_sheet_view_column_set_fixed_width (column,
635 MAX (title_width, content_width));
636 pspp_sheet_view_column_set_resizable (column, TRUE);
638 pspp_sheet_view_append_column (sheet_view, column);
640 g_signal_connect (renderer, "edited",
641 G_CALLBACK (on_var_column_edited),
643 g_object_set_data (G_OBJECT (renderer), "column-id",
644 GINT_TO_POINTER (column_id));
645 g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet);
650 static PsppSheetViewColumn *
651 add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
652 const char *title, int width)
654 return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (),
655 column_id, title, width);
658 static PsppSheetViewColumn *
659 add_spin_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
660 const char *title, int width)
662 return add_var_sheet_column (var_sheet, gtk_cell_renderer_spin_new (),
663 column_id, title, width);
667 measure_to_stock_id (enum fmt_type type, int measure)
669 return get_var_measurement_stock_id (type, measure);
673 alignment_to_stock_id (enum fmt_type type, int alignment)
675 return get_var_align_stock_id (alignment);
679 role_to_stock_id (enum fmt_type type, int role)
681 return get_var_role_stock_id (role);
685 render_var_pixbuf (GtkCellLayout *cell_layout,
686 GtkCellRenderer *cell,
687 GtkTreeModel *tree_model,
691 const char *(*value_to_stock_id) (enum fmt_type, int value);
692 enum fmt_type type = GPOINTER_TO_INT (data);
695 value_to_stock_id = g_object_get_data (G_OBJECT (cell), "value-to-stock-id");
697 gtk_tree_model_get (tree_model, iter, 0, &value, -1);
698 g_object_set (cell, "icon-name", value_to_stock_id (type, value), NULL);
702 on_combo_editing_started (GtkCellRenderer *renderer,
703 GtkCellEditable *editable,
707 PsppireVarSheet *var_sheet = user_data;
709 if (GTK_IS_COMBO_BOX (editable))
711 struct variable *var = path_string_to_variable (var_sheet, path_string);
712 const struct fmt_spec *format = var_get_print_format (var);
713 const char *(*value_to_stock_id) (enum fmt_type, int value);
714 GtkCellRenderer *cell;
716 value_to_stock_id = g_object_get_data (G_OBJECT (renderer),
717 "value-to-stock-id");
719 cell = gtk_cell_renderer_pixbuf_new ();
720 g_object_set (cell, "width", 16, "height", 16, NULL);
721 g_object_set_data (G_OBJECT (cell),
722 "value-to-stock-id", value_to_stock_id);
723 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (editable), cell, FALSE);
724 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (editable), cell,
726 GINT_TO_POINTER (format->type),
732 add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
733 const char *title, int width,
734 const char *(*value_to_stock_id) (enum fmt_type, int value),
737 PsppSheetViewColumn *column;
738 GtkCellRenderer *cell;
743 store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
744 va_start (args, value_to_stock_id);
745 while ((name = va_arg (args, const char *)) != NULL)
747 int value = va_arg (args, int);
748 gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
755 cell = gtk_cell_renderer_combo_new ();
758 "model", GTK_TREE_MODEL (store),
761 if (value_to_stock_id != NULL)
763 g_object_set_data (G_OBJECT (cell),
764 "value-to-stock-id", value_to_stock_id);
765 g_signal_connect (cell, "editing-started",
766 G_CALLBACK (on_combo_editing_started),
770 column = add_var_sheet_column (var_sheet, cell, column_id, title, width);
772 cell = gtk_cell_renderer_pixbuf_new ();
773 g_object_set (cell, "width", 16, "height", 16, NULL);
774 pspp_sheet_view_column_pack_end (column, cell, FALSE);
775 pspp_sheet_view_column_set_cell_data_func (
776 column, cell, render_var_cell, var_sheet, NULL);
780 add_popup_menu (PsppireVarSheet *var_sheet,
781 PsppSheetViewColumn *column,
782 void (*on_click) (PsppireCellRendererButton *,
784 PsppireVarSheet *var_sheet))
786 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
787 const char *button_label = "...";
788 GtkCellRenderer *button_renderer;
791 button_renderer = psppire_cell_renderer_button_new ();
792 g_object_set (button_renderer,
793 "label", button_label,
796 g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click),
798 pspp_sheet_view_column_pack_start (column, button_renderer, FALSE);
799 pspp_sheet_view_column_set_cell_data_func (
800 column, button_renderer, render_popup_cell, var_sheet, NULL);
802 content_width = GPOINTER_TO_INT (g_object_get_data (
803 G_OBJECT (column), "content-width"));
804 content_width += get_string_width (sheet_view, button_renderer,
806 if (content_width > pspp_sheet_view_column_get_fixed_width (column))
807 pspp_sheet_view_column_set_fixed_width (column, content_width);
811 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
812 gint wx, gint wy, size_t *row, size_t *column)
814 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
818 PsppSheetViewColumn *tree_column;
819 GtkTreeModel *tree_model;
823 /* Check that WIDGET is really visible on the screen before we
824 do anything else. This is a bug fix for a sticky situation:
825 when text_data_import_assistant() returns, it frees the data
826 necessary to compose the tool tip message, but there may be
827 a tool tip under preparation at that point (even if there is
828 no visible tool tip) that will call back into us a little
829 bit later. Perhaps the correct solution to this problem is
830 to make the data related to the tool tips part of a GObject
831 that only gets destroyed when all references are released,
832 but this solution appears to be effective too. */
833 if (!gtk_widget_get_mapped (widget))
836 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
838 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
839 &path, &tree_column, NULL, NULL))
842 column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number");
843 if (column_ptr == NULL)
845 *column = GPOINTER_TO_INT (column_ptr) - 1;
847 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
850 tree_model = pspp_sheet_view_get_model (tree_view);
851 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
852 gtk_tree_path_free (path);
856 *row = GPOINTER_TO_INT (iter.user_data);
861 on_query_var_tooltip (GtkWidget *widget, gint wx, gint wy,
862 gboolean keyboard_mode UNUSED,
863 GtkTooltip *tooltip, gpointer *user_data UNUSED)
865 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
867 struct variable *var;
870 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
873 dict = psppire_var_sheet_get_dictionary (var_sheet);
874 g_return_val_if_fail (dict != NULL, FALSE);
876 if (row >= psppire_dict_get_var_cnt (dict))
878 gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a "
883 var = psppire_dict_get_variable (dict, row);
884 g_return_val_if_fail (var != NULL, FALSE);
890 char text[FMT_STRING_LEN_MAX + 1];
892 fmt_to_string (var_get_print_format (var), text);
893 gtk_tooltip_set_text (tooltip, text);
898 if (var_has_value_labels (var))
900 const struct val_labs *vls = var_get_value_labels (var);
901 const struct val_lab **labels = val_labs_sorted (vls);
906 for (i = 0; i < val_labs_count (vls); i++)
908 const struct val_lab *vl = labels[i];
911 if (i >= 10 || ds_length (&s) > 500)
913 ds_put_cstr (&s, "...");
917 vstr = value_to_text (vl->value, var);
918 ds_put_format (&s, _("{%s, %s}\n"), vstr,
919 val_lab_get_escaped_label (vl));
923 ds_chomp_byte (&s, '\n');
925 gtk_tooltip_set_text (tooltip, ds_cstr (&s));
937 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
939 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
940 GtkWidget *menu = gtk_menu_new ();
944 GtkWidget *insert_variable = gtk_menu_item_new_with_mnemonic (_("_Insert Variable"));
945 GtkWidget *clear_variables = gtk_menu_item_new_with_mnemonic (_("Cl_ear Variables"));
947 g_signal_connect_swapped (insert_variable, "activate", G_CALLBACK (psppire_var_sheet_insert_variable), var_sheet);
948 g_signal_connect_swapped (clear_variables, "activate", G_CALLBACK (psppire_var_sheet_clear_variables), var_sheet);
950 gtk_menu_attach (GTK_MENU (menu), insert_variable, 0, 1, i, i + 1); ++i;
951 gtk_menu_attach (GTK_MENU (menu), clear_variables, 0, 1, i, i + 1); ++i;
953 gtk_widget_show_all (menu);
955 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
959 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
961 do_popup_menu (widget, 0, gtk_get_current_event_time ());
965 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
966 gpointer user_data UNUSED)
968 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
970 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
972 PsppSheetSelection *selection;
974 selection = pspp_sheet_view_get_selection (sheet_view);
975 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
979 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
980 &path, NULL, NULL, NULL))
982 pspp_sheet_selection_unselect_all (selection);
983 pspp_sheet_selection_select_path (selection, path);
984 gtk_tree_path_free (path);
988 do_popup_menu (widget, event->button, event->time);
997 psppire_fmt_use_get_type (void)
999 static GType etype = 0;
1002 static const GEnumValue values[] =
1004 { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
1005 { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
1009 etype = g_enum_register_static
1010 (g_intern_static_string ("PsppireFmtUse"), values);
1019 PROP_MAY_CREATE_VARS,
1020 PROP_MAY_DELETE_VARS,
1025 psppire_var_sheet_set_property (GObject *object,
1027 const GValue *value,
1030 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
1034 case PROP_DICTIONARY:
1035 psppire_var_sheet_set_dictionary (obj,
1036 PSPPIRE_DICT (g_value_get_object (
1040 case PROP_MAY_CREATE_VARS:
1041 psppire_var_sheet_set_may_create_vars (obj,
1042 g_value_get_boolean (value));
1045 case PROP_MAY_DELETE_VARS:
1046 psppire_var_sheet_set_may_delete_vars (obj,
1047 g_value_get_boolean (value));
1050 case PROP_FORMAT_TYPE:
1051 obj->format_use = g_value_get_enum (value);
1055 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1061 psppire_var_sheet_get_property (GObject *object,
1066 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
1070 case PROP_DICTIONARY:
1071 g_value_set_object (value,
1072 G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
1075 case PROP_MAY_CREATE_VARS:
1076 g_value_set_boolean (value, obj->may_create_vars);
1079 case PROP_MAY_DELETE_VARS:
1080 g_value_set_boolean (value, obj->may_delete_vars);
1083 case PROP_FORMAT_TYPE:
1084 g_value_set_enum (value, obj->format_use);
1088 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1094 psppire_var_sheet_dispose (GObject *obj)
1096 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
1099 if (var_sheet->dispose_has_run)
1102 var_sheet->dispose_has_run = TRUE;
1104 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1105 if ( var_sheet->dict_signals[i])
1106 g_signal_handler_disconnect (var_sheet->dict,
1107 var_sheet->dict_signals[i]);
1109 if (var_sheet->dict)
1110 g_object_unref (var_sheet->dict);
1112 /* These dialogs are not GObjects (although they should be!)
1113 But for now, unreffing them only causes a GCritical Error
1114 so comment them out for now. (and accept the memory leakage)
1116 g_object_unref (var_sheet->val_labs_dialog);
1117 g_object_unref (var_sheet->missing_val_dialog);
1118 g_object_unref (var_sheet->var_type_dialog);
1121 G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
1125 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
1127 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1130 gobject_class->set_property = psppire_var_sheet_set_property;
1131 gobject_class->get_property = psppire_var_sheet_get_property;
1132 gobject_class->dispose = psppire_var_sheet_dispose;
1134 g_signal_new ("var-double-clicked",
1135 G_OBJECT_CLASS_TYPE (gobject_class),
1138 g_signal_accumulator_true_handled, NULL,
1139 psppire_marshal_BOOLEAN__INT,
1140 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1142 pspec = g_param_spec_object ("dictionary",
1143 "Dictionary displayed by the sheet",
1144 "The PsppireDict that the sheet displays "
1145 "may allow the user to edit",
1148 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
1150 pspec = g_param_spec_boolean ("may-create-vars",
1151 "May create variables",
1152 "Whether the user may create more variables",
1155 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
1157 pspec = g_param_spec_boolean ("may-delete-vars",
1158 "May delete variables",
1159 "Whether the user may delete variables",
1162 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
1164 pspec = g_param_spec_enum ("format-use",
1165 "Use of variable format",
1166 ("Whether variables have input or output "
1168 PSPPIRE_TYPE_FMT_USE,
1171 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
1175 render_row_number_cell (PsppSheetViewColumn *tree_column,
1176 GtkCellRenderer *cell,
1177 GtkTreeModel *model,
1181 PsppireVarSheet *var_sheet = user_data;
1182 GValue gvalue = { 0, };
1185 row = GPOINTER_TO_INT (iter->user_data);
1187 g_value_init (&gvalue, G_TYPE_INT);
1188 g_value_set_int (&gvalue, row + 1);
1189 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1190 g_value_unset (&gvalue);
1192 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1193 g_object_set (cell, "editable", TRUE, NULL);
1195 g_object_set (cell, "editable", FALSE, NULL);
1199 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1201 PsppireVarSheet *var_sheet)
1205 g_return_if_fail (var_sheet->dict != NULL);
1207 path = gtk_tree_path_new_from_string (path_string);
1208 if (gtk_tree_path_get_depth (path) == 1)
1210 gint *indices = gtk_tree_path_get_indices (path);
1211 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1214 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1215 indices[0], &handled);
1218 gtk_tree_path_free (path);
1222 psppire_var_sheet_variables_column_clicked (PsppSheetViewColumn *column,
1223 PsppireVarSheet *var_sheet)
1225 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1226 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1228 pspp_sheet_selection_select_all (selection);
1231 static PsppSheetViewColumn *
1232 make_row_number_column (PsppireVarSheet *var_sheet)
1234 PsppSheetViewColumn *column;
1235 GtkCellRenderer *renderer;
1237 renderer = psppire_cell_renderer_button_new ();
1238 g_object_set (renderer, "xalign", 1.0, NULL);
1239 g_signal_connect (renderer, "double-clicked",
1240 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1243 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1245 pspp_sheet_view_column_set_clickable (column, TRUE);
1246 pspp_sheet_view_column_set_cell_data_func (
1247 column, renderer, render_row_number_cell, var_sheet, NULL);
1248 pspp_sheet_view_column_set_fixed_width (column, 50);
1249 g_signal_connect (column, "clicked",
1250 G_CALLBACK (psppire_var_sheet_variables_column_clicked),
1257 psppire_var_sheet_clear_variables (PsppireVarSheet *var_sheet)
1259 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1260 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1261 PsppireDict *dict = var_sheet->dict;
1262 const struct range_set_node *node;
1263 struct range_set *selected = pspp_sheet_selection_get_range_set (selection);
1265 for (node = range_set_last (selected); node != NULL;
1266 node = range_set_prev (selected, node))
1270 for (i = 1; i <= range_set_node_get_width (node); i++)
1272 unsigned long row = range_set_node_get_end (node) - i;
1273 if (row < psppire_dict_get_var_cnt (dict))
1274 psppire_dict_delete_variables (dict, row, 1);
1277 range_set_destroy (selected);
1281 on_selection_changed (PsppSheetSelection *selection,
1282 gpointer user_data UNUSED)
1284 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1285 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1286 gint n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1287 gboolean may_delete;
1290 GtkWidget *top = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
1291 if (! PSPPIRE_IS_DATA_WINDOW (top))
1294 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (top);
1295 gtk_widget_set_sensitive (dw->mi_insert_var, n_selected_rows > 0);
1297 switch (n_selected_rows)
1304 /* The row used for inserting new variables cannot be deleted. */
1305 path = gtk_tree_path_new_from_indices (
1306 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1307 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1308 gtk_tree_path_free (path);
1316 gtk_widget_set_sensitive (dw->mi_clear_variables, var_sheet->may_delete_vars && may_delete);
1320 psppire_var_sheet_insert_variable (PsppireVarSheet *var_sheet)
1322 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1323 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1324 PsppireDict *dict = var_sheet->dict;
1325 struct range_set *selected;
1328 selected = pspp_sheet_selection_get_range_set (selection);
1329 row = range_set_scan (selected, 0);
1330 range_set_destroy (selected);
1332 if (row <= psppire_dict_get_var_cnt (dict))
1335 if (psppire_dict_generate_name (dict, name, sizeof name))
1336 psppire_dict_insert_variable (dict, row, name);
1341 psppire_var_sheet_init (PsppireVarSheet *obj)
1343 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1344 PsppSheetViewColumn *column;
1348 obj->format_use = FMT_FOR_OUTPUT;
1349 obj->may_create_vars = TRUE;
1350 obj->may_delete_vars = TRUE;
1352 obj->scroll_to_bottom_signal = 0;
1354 obj->container = NULL;
1355 obj->dispose_has_run = FALSE;
1357 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1359 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1360 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1361 g_signal_connect (list->data, "editing-started",
1362 G_CALLBACK (on_name_column_editing_started), NULL);
1365 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1366 add_popup_menu (obj, column, on_type_click);
1368 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1370 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1372 add_text_column (obj, VS_LABEL, _("Label"), 20);
1374 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1375 add_popup_menu (obj, column, on_value_labels_click);
1377 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1378 add_popup_menu (obj, column, on_missing_values_click);
1380 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1382 add_combo_column (obj, VS_ALIGN, _("Align"), 8, alignment_to_stock_id,
1383 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1384 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1385 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1388 add_combo_column (obj, VS_MEASURE, _("Measure"), 11, measure_to_stock_id,
1389 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1390 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1391 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1394 add_combo_column (obj, VS_ROLE, _("Role"), 11, role_to_stock_id,
1395 var_role_to_string (ROLE_INPUT), ROLE_INPUT,
1396 var_role_to_string (ROLE_TARGET), ROLE_TARGET,
1397 var_role_to_string (ROLE_BOTH), ROLE_BOTH,
1398 var_role_to_string (ROLE_NONE), ROLE_NONE,
1399 var_role_to_string (ROLE_PARTITION), ROLE_PARTITION,
1400 var_role_to_string (ROLE_SPLIT), ROLE_SPLIT,
1403 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1404 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1405 PSPP_SHEET_SELECTION_MULTIPLE);
1407 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1408 g_signal_connect (obj, "query-tooltip",
1409 G_CALLBACK (on_query_var_tooltip), NULL);
1410 g_signal_connect (obj, "button-press-event",
1411 G_CALLBACK (on_button_pressed), NULL);
1413 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1415 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1416 "changed", G_CALLBACK (on_selection_changed), NULL);
1420 psppire_var_sheet_new (void)
1422 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1426 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1428 return var_sheet->dict;
1432 refresh_model (PsppireVarSheet *var_sheet)
1434 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1436 if (var_sheet->dict != NULL)
1438 PsppireEmptyListStore *store;
1441 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1442 + var_sheet->may_create_vars);
1443 store = psppire_empty_list_store_new (n_rows);
1444 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1445 GTK_TREE_MODEL (store));
1446 g_object_unref (store);
1451 on_var_changed (PsppireDict *dict, glong row,
1452 guint what, const struct variable *oldvar,
1453 PsppireVarSheet *var_sheet)
1455 PsppireEmptyListStore *store;
1457 g_return_if_fail (dict == var_sheet->dict);
1459 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1460 PSPP_SHEET_VIEW (var_sheet)));
1461 g_return_if_fail (store != NULL);
1463 psppire_empty_list_store_row_changed (store, row);
1467 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1469 PsppireEmptyListStore *store;
1472 g_return_if_fail (dict == var_sheet->dict);
1474 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1475 PSPP_SHEET_VIEW (var_sheet)));
1476 g_return_if_fail (store != NULL);
1478 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1479 + var_sheet->may_create_vars);
1480 psppire_empty_list_store_set_n_rows (store, n_rows);
1481 psppire_empty_list_store_row_inserted (store, row);
1485 on_var_deleted (PsppireDict *dict,
1486 const struct variable *var, int dict_idx, int case_idx,
1487 PsppireVarSheet *var_sheet)
1489 PsppireEmptyListStore *store;
1492 g_return_if_fail (dict == var_sheet->dict);
1494 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1495 PSPP_SHEET_VIEW (var_sheet)));
1496 g_return_if_fail (store != NULL);
1498 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1499 + var_sheet->may_create_vars);
1500 psppire_empty_list_store_set_n_rows (store, n_rows);
1501 psppire_empty_list_store_row_deleted (store, dict_idx);
1505 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1507 g_return_if_fail (dict == var_sheet->dict);
1508 refresh_model (var_sheet);
1512 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1515 if (var_sheet->dict != NULL)
1519 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1521 if (var_sheet->dict_signals[i])
1522 g_signal_handler_disconnect (var_sheet->dict,
1523 var_sheet->dict_signals[i]);
1525 var_sheet->dict_signals[i] = 0;
1528 g_object_unref (var_sheet->dict);
1531 var_sheet->dict = dict;
1535 g_object_ref (dict);
1537 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1538 = g_signal_connect (dict, "backend-changed",
1539 G_CALLBACK (on_backend_changed), var_sheet);
1541 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED]
1542 = g_signal_connect (dict, "variable-changed",
1543 G_CALLBACK (on_var_changed), var_sheet);
1545 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1546 = g_signal_connect (dict, "variable-inserted",
1547 G_CALLBACK (on_var_inserted), var_sheet);
1549 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1550 = g_signal_connect (dict, "variable-deleted",
1551 G_CALLBACK (on_var_deleted), var_sheet);
1554 refresh_model (var_sheet);
1558 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1560 return var_sheet->may_create_vars;
1564 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1565 gboolean may_create_vars)
1567 if (var_sheet->may_create_vars != may_create_vars)
1569 PsppireEmptyListStore *store;
1572 var_sheet->may_create_vars = may_create_vars;
1574 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1575 PSPP_SHEET_VIEW (var_sheet)));
1576 g_return_if_fail (store != NULL);
1578 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1579 + var_sheet->may_create_vars);
1580 psppire_empty_list_store_set_n_rows (store, n_rows);
1582 if (may_create_vars)
1583 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1585 psppire_empty_list_store_row_deleted (store, n_rows);
1587 on_selection_changed (pspp_sheet_view_get_selection (
1588 PSPP_SHEET_VIEW (var_sheet)), NULL);
1593 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1595 return var_sheet->may_delete_vars;
1599 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1600 gboolean may_delete_vars)
1602 if (var_sheet->may_delete_vars != may_delete_vars)
1604 var_sheet->may_delete_vars = may_delete_vars;
1605 on_selection_changed (pspp_sheet_view_get_selection (
1606 PSPP_SHEET_VIEW (var_sheet)), NULL);
1611 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1613 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1616 path = gtk_tree_path_new_from_indices (dict_index, -1);
1617 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1618 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1619 gtk_tree_path_free (path);