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, "stock-id",
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, "stock-id",
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, "stock-id",
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, "stock-id", 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);
942 menu = get_widget_assert (var_sheet->builder, "varsheet-variable-popup");
943 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
947 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
949 do_popup_menu (widget, 0, gtk_get_current_event_time ());
953 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
954 gpointer user_data UNUSED)
956 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
958 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
960 PsppSheetSelection *selection;
962 selection = pspp_sheet_view_get_selection (sheet_view);
963 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
967 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
968 &path, NULL, NULL, NULL))
970 pspp_sheet_selection_unselect_all (selection);
971 pspp_sheet_selection_select_path (selection, path);
972 gtk_tree_path_free (path);
976 do_popup_menu (widget, event->button, event->time);
984 psppire_fmt_use_get_type (void)
986 static GType etype = 0;
989 static const GEnumValue values[] =
991 { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
992 { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
996 etype = g_enum_register_static
997 (g_intern_static_string ("PsppireFmtUse"), values);
1006 PROP_MAY_CREATE_VARS,
1007 PROP_MAY_DELETE_VARS,
1013 psppire_var_sheet_set_property (GObject *object,
1015 const GValue *value,
1018 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
1022 case PROP_DICTIONARY:
1023 psppire_var_sheet_set_dictionary (obj,
1024 PSPPIRE_DICT (g_value_get_object (
1028 case PROP_MAY_CREATE_VARS:
1029 psppire_var_sheet_set_may_create_vars (obj,
1030 g_value_get_boolean (value));
1033 case PROP_MAY_DELETE_VARS:
1034 psppire_var_sheet_set_may_delete_vars (obj,
1035 g_value_get_boolean (value));
1038 case PROP_FORMAT_TYPE:
1039 obj->format_use = g_value_get_enum (value);
1042 case PROP_UI_MANAGER:
1044 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1050 psppire_var_sheet_get_property (GObject *object,
1055 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
1059 case PROP_DICTIONARY:
1060 g_value_set_object (value,
1061 G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
1064 case PROP_MAY_CREATE_VARS:
1065 g_value_set_boolean (value, obj->may_create_vars);
1068 case PROP_MAY_DELETE_VARS:
1069 g_value_set_boolean (value, obj->may_delete_vars);
1072 case PROP_FORMAT_TYPE:
1073 g_value_set_enum (value, obj->format_use);
1076 case PROP_UI_MANAGER:
1077 g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj));
1081 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1087 psppire_var_sheet_dispose (GObject *obj)
1089 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
1092 if (var_sheet->dispose_has_run)
1095 var_sheet->dispose_has_run = TRUE;
1097 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1098 if ( var_sheet->dict_signals[i])
1099 g_signal_handler_disconnect (var_sheet->dict,
1100 var_sheet->dict_signals[i]);
1102 if (var_sheet->dict)
1103 g_object_unref (var_sheet->dict);
1106 g_object_unref (var_sheet->uim);
1108 /* These dialogs are not GObjects (although they should be!)
1109 But for now, unreffing them only causes a GCritical Error
1110 so comment them out for now. (and accept the memory leakage)
1112 g_object_unref (var_sheet->val_labs_dialog);
1113 g_object_unref (var_sheet->missing_val_dialog);
1114 g_object_unref (var_sheet->var_type_dialog);
1117 G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
1121 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
1123 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1126 gobject_class->set_property = psppire_var_sheet_set_property;
1127 gobject_class->get_property = psppire_var_sheet_get_property;
1128 gobject_class->dispose = psppire_var_sheet_dispose;
1130 g_signal_new ("var-double-clicked",
1131 G_OBJECT_CLASS_TYPE (gobject_class),
1134 g_signal_accumulator_true_handled, NULL,
1135 psppire_marshal_BOOLEAN__INT,
1136 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1138 pspec = g_param_spec_object ("dictionary",
1139 "Dictionary displayed by the sheet",
1140 "The PsppireDict that the sheet displays "
1141 "may allow the user to edit",
1144 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
1146 pspec = g_param_spec_boolean ("may-create-vars",
1147 "May create variables",
1148 "Whether the user may create more variables",
1151 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
1153 pspec = g_param_spec_boolean ("may-delete-vars",
1154 "May delete variables",
1155 "Whether the user may delete variables",
1158 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
1160 pspec = g_param_spec_enum ("format-use",
1161 "Use of variable format",
1162 ("Whether variables have input or output "
1164 PSPPIRE_TYPE_FMT_USE,
1167 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
1169 pspec = g_param_spec_object ("ui-manager",
1171 "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.",
1172 GTK_TYPE_UI_MANAGER,
1174 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1178 render_row_number_cell (PsppSheetViewColumn *tree_column,
1179 GtkCellRenderer *cell,
1180 GtkTreeModel *model,
1184 PsppireVarSheet *var_sheet = user_data;
1185 GValue gvalue = { 0, };
1188 row = GPOINTER_TO_INT (iter->user_data);
1190 g_value_init (&gvalue, G_TYPE_INT);
1191 g_value_set_int (&gvalue, row + 1);
1192 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1193 g_value_unset (&gvalue);
1195 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1196 g_object_set (cell, "editable", TRUE, NULL);
1198 g_object_set (cell, "editable", FALSE, NULL);
1202 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1204 PsppireVarSheet *var_sheet)
1208 g_return_if_fail (var_sheet->dict != NULL);
1210 path = gtk_tree_path_new_from_string (path_string);
1211 if (gtk_tree_path_get_depth (path) == 1)
1213 gint *indices = gtk_tree_path_get_indices (path);
1214 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1217 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1218 indices[0], &handled);
1221 gtk_tree_path_free (path);
1225 psppire_var_sheet_variables_column_clicked (PsppSheetViewColumn *column,
1226 PsppireVarSheet *var_sheet)
1228 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1229 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1231 pspp_sheet_selection_select_all (selection);
1234 static PsppSheetViewColumn *
1235 make_row_number_column (PsppireVarSheet *var_sheet)
1237 PsppSheetViewColumn *column;
1238 GtkCellRenderer *renderer;
1240 renderer = psppire_cell_renderer_button_new ();
1241 g_object_set (renderer, "xalign", 1.0, NULL);
1242 g_signal_connect (renderer, "double-clicked",
1243 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1246 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1248 pspp_sheet_view_column_set_clickable (column, TRUE);
1249 pspp_sheet_view_column_set_cell_data_func (
1250 column, renderer, render_row_number_cell, var_sheet, NULL);
1251 pspp_sheet_view_column_set_fixed_width (column, 50);
1252 g_signal_connect (column, "clicked",
1253 G_CALLBACK (psppire_var_sheet_variables_column_clicked),
1260 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1262 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1263 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1264 PsppireDict *dict = var_sheet->dict;
1265 const struct range_set_node *node;
1266 struct range_set *selected;
1268 selected = pspp_sheet_selection_get_range_set (selection);
1269 for (node = range_set_last (selected); node != NULL;
1270 node = range_set_prev (selected, node))
1274 for (i = 1; i <= range_set_node_get_width (node); i++)
1276 unsigned long row = range_set_node_get_end (node) - i;
1277 if (row < psppire_dict_get_var_cnt (dict))
1278 psppire_dict_delete_variables (dict, row, 1);
1281 range_set_destroy (selected);
1285 on_selection_changed (PsppSheetSelection *selection,
1286 gpointer user_data UNUSED)
1288 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1289 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1290 gint n_selected_rows;
1291 gboolean may_delete;
1295 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1297 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1298 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1299 && n_selected_rows > 0));
1301 switch (n_selected_rows)
1308 /* The row used for inserting new variables cannot be deleted. */
1309 path = gtk_tree_path_new_from_indices (
1310 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1311 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1312 gtk_tree_path_free (path);
1319 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1320 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1324 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1326 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1327 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1328 PsppireDict *dict = var_sheet->dict;
1329 struct range_set *selected;
1332 selected = pspp_sheet_selection_get_range_set (selection);
1333 row = range_set_scan (selected, 0);
1334 range_set_destroy (selected);
1336 if (row <= psppire_dict_get_var_cnt (dict))
1339 if (psppire_dict_generate_name (dict, name, sizeof name))
1340 psppire_dict_insert_variable (dict, row, name);
1345 psppire_var_sheet_init (PsppireVarSheet *obj)
1347 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1348 PsppSheetViewColumn *column;
1353 obj->format_use = FMT_FOR_OUTPUT;
1354 obj->may_create_vars = TRUE;
1355 obj->may_delete_vars = TRUE;
1357 obj->scroll_to_bottom_signal = 0;
1359 obj->container = NULL;
1360 obj->dispose_has_run = FALSE;
1363 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1365 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1366 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1367 g_signal_connect (list->data, "editing-started",
1368 G_CALLBACK (on_name_column_editing_started), NULL);
1371 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1372 add_popup_menu (obj, column, on_type_click);
1374 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1376 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1378 add_text_column (obj, VS_LABEL, _("Label"), 20);
1380 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1381 add_popup_menu (obj, column, on_value_labels_click);
1383 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1384 add_popup_menu (obj, column, on_missing_values_click);
1386 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1388 add_combo_column (obj, VS_ALIGN, _("Align"), 8, alignment_to_stock_id,
1389 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1390 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1391 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1394 add_combo_column (obj, VS_MEASURE, _("Measure"), 11, measure_to_stock_id,
1395 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1396 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1397 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1400 add_combo_column (obj, VS_ROLE, _("Role"), 11, role_to_stock_id,
1401 var_role_to_string (ROLE_INPUT), ROLE_INPUT,
1402 var_role_to_string (ROLE_TARGET), ROLE_TARGET,
1403 var_role_to_string (ROLE_BOTH), ROLE_BOTH,
1404 var_role_to_string (ROLE_NONE), ROLE_NONE,
1405 var_role_to_string (ROLE_PARTITION), ROLE_PARTITION,
1406 var_role_to_string (ROLE_SPLIT), ROLE_SPLIT,
1409 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1410 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1411 PSPP_SHEET_SELECTION_MULTIPLE);
1413 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1414 g_signal_connect (obj, "query-tooltip",
1415 G_CALLBACK (on_query_var_tooltip), NULL);
1416 g_signal_connect (obj, "button-press-event",
1417 G_CALLBACK (on_button_pressed), NULL);
1418 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1420 obj->builder = builder_new ("var-sheet.ui");
1422 action = get_action_assert (obj->builder, "edit_clear-variables");
1423 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1425 gtk_action_set_sensitive (action, FALSE);
1426 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1427 "changed", G_CALLBACK (on_selection_changed), NULL);
1429 action = get_action_assert (obj->builder, "edit_insert-variable");
1430 gtk_action_set_sensitive (action, FALSE);
1431 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1436 psppire_var_sheet_new (void)
1438 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1442 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1444 return var_sheet->dict;
1448 refresh_model (PsppireVarSheet *var_sheet)
1450 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1452 if (var_sheet->dict != NULL)
1454 PsppireEmptyListStore *store;
1457 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1458 + var_sheet->may_create_vars);
1459 store = psppire_empty_list_store_new (n_rows);
1460 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1461 GTK_TREE_MODEL (store));
1462 g_object_unref (store);
1467 on_var_changed (PsppireDict *dict, glong row,
1468 guint what, const struct variable *oldvar,
1469 PsppireVarSheet *var_sheet)
1471 PsppireEmptyListStore *store;
1473 g_return_if_fail (dict == var_sheet->dict);
1475 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1476 PSPP_SHEET_VIEW (var_sheet)));
1477 g_return_if_fail (store != NULL);
1479 psppire_empty_list_store_row_changed (store, row);
1483 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1485 PsppireEmptyListStore *store;
1488 g_return_if_fail (dict == var_sheet->dict);
1490 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1491 PSPP_SHEET_VIEW (var_sheet)));
1492 g_return_if_fail (store != NULL);
1494 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1495 + var_sheet->may_create_vars);
1496 psppire_empty_list_store_set_n_rows (store, n_rows);
1497 psppire_empty_list_store_row_inserted (store, row);
1501 on_var_deleted (PsppireDict *dict,
1502 const struct variable *var, int dict_idx, int case_idx,
1503 PsppireVarSheet *var_sheet)
1505 PsppireEmptyListStore *store;
1508 g_return_if_fail (dict == var_sheet->dict);
1510 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1511 PSPP_SHEET_VIEW (var_sheet)));
1512 g_return_if_fail (store != NULL);
1514 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1515 + var_sheet->may_create_vars);
1516 psppire_empty_list_store_set_n_rows (store, n_rows);
1517 psppire_empty_list_store_row_deleted (store, dict_idx);
1521 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1523 g_return_if_fail (dict == var_sheet->dict);
1524 refresh_model (var_sheet);
1528 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1531 if (var_sheet->dict != NULL)
1535 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1537 if (var_sheet->dict_signals[i])
1538 g_signal_handler_disconnect (var_sheet->dict,
1539 var_sheet->dict_signals[i]);
1541 var_sheet->dict_signals[i] = 0;
1544 g_object_unref (var_sheet->dict);
1547 var_sheet->dict = dict;
1551 g_object_ref (dict);
1553 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1554 = g_signal_connect (dict, "backend-changed",
1555 G_CALLBACK (on_backend_changed), var_sheet);
1557 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED]
1558 = g_signal_connect (dict, "variable-changed",
1559 G_CALLBACK (on_var_changed), var_sheet);
1561 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1562 = g_signal_connect (dict, "variable-inserted",
1563 G_CALLBACK (on_var_inserted), var_sheet);
1565 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1566 = g_signal_connect (dict, "variable-deleted",
1567 G_CALLBACK (on_var_deleted), var_sheet);
1570 refresh_model (var_sheet);
1574 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1576 return var_sheet->may_create_vars;
1580 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1581 gboolean may_create_vars)
1583 if (var_sheet->may_create_vars != may_create_vars)
1585 PsppireEmptyListStore *store;
1588 var_sheet->may_create_vars = may_create_vars;
1590 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1591 PSPP_SHEET_VIEW (var_sheet)));
1592 g_return_if_fail (store != NULL);
1594 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1595 + var_sheet->may_create_vars);
1596 psppire_empty_list_store_set_n_rows (store, n_rows);
1598 if (may_create_vars)
1599 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1601 psppire_empty_list_store_row_deleted (store, n_rows);
1603 on_selection_changed (pspp_sheet_view_get_selection (
1604 PSPP_SHEET_VIEW (var_sheet)), NULL);
1609 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1611 return var_sheet->may_delete_vars;
1615 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1616 gboolean may_delete_vars)
1618 if (var_sheet->may_delete_vars != may_delete_vars)
1620 var_sheet->may_delete_vars = may_delete_vars;
1621 on_selection_changed (pspp_sheet_view_get_selection (
1622 PSPP_SHEET_VIEW (var_sheet)), NULL);
1627 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1629 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1632 path = gtk_tree_path_new_from_indices (dict_index, -1);
1633 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1634 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1635 gtk_tree_path_free (path);
1639 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1641 if (var_sheet->uim == NULL)
1643 var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1645 GTK_TYPE_UI_MANAGER));
1646 g_object_ref (var_sheet->uim);
1649 return var_sheet->uim;