1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2011, 2012, 2013 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-request",
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, false);
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 if (row >= psppire_dict_get_var_cnt (var_sheet->dict))
367 if (GTK_IS_CELL_RENDERER_TEXT (cell))
371 "editable", column_id == VS_NAME,
373 if (column_id == VS_WIDTH
374 || column_id == VS_DECIMALS
375 || column_id == VS_COLUMNS)
376 g_object_set (cell, "adjustment", NULL, NULL);
379 g_object_set (cell, "stock-id", "", NULL);
383 var = psppire_dict_get_variable (var_sheet->dict, row);
385 print = var_get_print_format (var);
390 "text", var_get_name (var),
397 "text", fmt_gui_name (print->type),
403 set_spin_cell (cell, print->w,
404 fmt_min_width (print->type, var_sheet->format_use),
405 fmt_max_width (print->type, var_sheet->format_use),
406 fmt_step_width (print->type));
410 if (fmt_takes_decimals (print->type))
412 int max_w = fmt_max_width (print->type, var_sheet->format_use);
413 int max_d = fmt_max_decimals (print->type, max_w,
414 var_sheet->format_use);
415 set_spin_cell (cell, print->d, 0, max_d, 1);
427 "text", var_has_label (var) ? var_get_label (var) : "",
433 g_object_set (cell, "editable", FALSE, NULL);
434 if ( ! var_has_value_labels (var))
435 g_object_set (cell, "text", _("None"), NULL);
438 const struct val_labs *vls = var_get_value_labels (var);
439 const struct val_lab **labels = val_labs_sorted (vls);
440 const struct val_lab *vl = labels[0];
441 gchar *vstr = value_to_text (vl->value, var);
442 char *text = xasprintf (_("{%s, %s}..."), vstr,
443 val_lab_get_escaped_label (vl));
446 g_object_set (cell, "text", text, NULL);
454 char *text = missing_values_to_string (var, NULL);
464 set_spin_cell (cell, var_get_display_width (var), 1, 2 * MAX_STRING, 1);
468 if (GTK_IS_CELL_RENDERER_TEXT (cell))
470 "text", alignment_to_string (var_get_alignment (var)),
474 g_object_set (cell, "stock-id",
475 get_var_align_stock_id (var_get_alignment (var)), NULL);
479 if (GTK_IS_CELL_RENDERER_TEXT (cell))
481 "text", measure_to_string (var_get_measure (var)),
486 enum fmt_type type = var_get_print_format (var)->type;
487 enum measure measure = var_get_measure (var);
489 g_object_set (cell, "stock-id",
490 get_var_measurement_stock_id (type, measure),
496 if (GTK_IS_CELL_RENDERER_TEXT (cell))
498 "text", var_role_to_string (var_get_role (var)),
502 g_object_set (cell, "stock-id",
503 get_var_role_stock_id (var_get_role (var)), NULL);
508 static struct variable *
509 path_string_to_variable (PsppireVarSheet *var_sheet, gchar *path_string)
515 path = gtk_tree_path_new_from_string (path_string);
516 row = gtk_tree_path_get_indices (path)[0];
517 gtk_tree_path_free (path);
519 dict = psppire_var_sheet_get_dictionary (var_sheet);
520 g_return_val_if_fail (dict != NULL, NULL);
522 return psppire_dict_get_variable (dict, row);
526 on_type_click (PsppireCellRendererButton *cell,
528 PsppireVarSheet *var_sheet)
530 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
531 struct fmt_spec format;
532 struct variable *var;
534 var = path_string_to_variable (var_sheet, path);
535 g_return_if_fail (var != NULL);
537 format = *var_get_print_format (var);
538 psppire_var_type_dialog_run (GTK_WINDOW (toplevel), &format);
540 var_set_width_and_formats (var, fmt_var_width (&format), &format, &format);
544 on_value_labels_click (PsppireCellRendererButton *cell,
546 PsppireVarSheet *var_sheet)
548 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
549 struct val_labs *labels;
550 struct variable *var;
552 var = path_string_to_variable (var_sheet, path);
553 g_return_if_fail (var != NULL);
555 labels = psppire_val_labs_dialog_run (GTK_WINDOW (toplevel), var);
558 var_set_value_labels (var, labels);
559 val_labs_destroy (labels);
564 on_missing_values_click (PsppireCellRendererButton *cell,
566 PsppireVarSheet *var_sheet)
568 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
569 struct missing_values mv;
570 struct variable *var;
572 var = path_string_to_variable (var_sheet, path);
573 g_return_if_fail (var != NULL);
575 psppire_missing_val_dialog_run (GTK_WINDOW (toplevel), var, &mv);
576 var_set_missing_values (var, &mv);
581 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
585 g_object_set (G_OBJECT (renderer),
586 PSPPIRE_IS_CELL_RENDERER_BUTTON (renderer) ? "label" : "text",
587 string, (void *) NULL);
588 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
589 NULL, NULL, NULL, &width, NULL);
594 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
601 ds_put_byte_multiple (&s, '0', char_cnt);
602 ds_put_byte (&s, ' ');
603 width = get_string_width (treeview, renderer, ds_cstr (&s));
609 static PsppSheetViewColumn *
610 add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer,
611 enum vs_column column_id,
612 const char *title, int width)
614 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
615 int title_width, content_width;
616 PsppSheetViewColumn *column;
618 column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL);
619 g_object_set_data (G_OBJECT (column), "column-number",
620 GINT_TO_POINTER (column_id) + 1);
622 pspp_sheet_view_column_set_cell_data_func (
623 column, renderer, render_var_cell, var_sheet, NULL);
625 title_width = get_string_width (sheet_view, renderer, title);
626 content_width = get_monospace_width (sheet_view, renderer, width);
627 g_object_set_data (G_OBJECT (column), "content-width",
628 GINT_TO_POINTER (content_width));
630 pspp_sheet_view_column_set_fixed_width (column,
631 MAX (title_width, content_width));
632 pspp_sheet_view_column_set_resizable (column, TRUE);
634 pspp_sheet_view_append_column (sheet_view, column);
636 g_signal_connect (renderer, "edited",
637 G_CALLBACK (on_var_column_edited),
639 g_object_set_data (G_OBJECT (renderer), "column-id",
640 GINT_TO_POINTER (column_id));
641 g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet);
646 static PsppSheetViewColumn *
647 add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
648 const char *title, int width)
650 return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (),
651 column_id, title, width);
654 static PsppSheetViewColumn *
655 add_spin_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
656 const char *title, int width)
658 return add_var_sheet_column (var_sheet, gtk_cell_renderer_spin_new (),
659 column_id, title, width);
663 measure_to_stock_id (enum fmt_type type, int measure)
665 return get_var_measurement_stock_id (type, measure);
669 alignment_to_stock_id (enum fmt_type type, int alignment)
671 return get_var_align_stock_id (alignment);
675 role_to_stock_id (enum fmt_type type, int role)
677 return get_var_role_stock_id (role);
681 render_var_pixbuf (GtkCellLayout *cell_layout,
682 GtkCellRenderer *cell,
683 GtkTreeModel *tree_model,
687 const char *(*value_to_stock_id) (enum fmt_type, int value);
688 enum fmt_type type = GPOINTER_TO_INT (data);
691 value_to_stock_id = g_object_get_data (G_OBJECT (cell), "value-to-stock-id");
693 gtk_tree_model_get (tree_model, iter, 0, &value, -1);
694 g_object_set (cell, "stock-id", value_to_stock_id (type, value), NULL);
698 on_combo_editing_started (GtkCellRenderer *renderer,
699 GtkCellEditable *editable,
703 PsppireVarSheet *var_sheet = user_data;
705 if (GTK_IS_COMBO_BOX (editable))
707 struct variable *var = path_string_to_variable (var_sheet, path_string);
708 const struct fmt_spec *format = var_get_print_format (var);
709 const char *(*value_to_stock_id) (enum fmt_type, int value);
710 GtkCellRenderer *cell;
712 value_to_stock_id = g_object_get_data (G_OBJECT (renderer),
713 "value-to-stock-id");
715 cell = gtk_cell_renderer_pixbuf_new ();
716 g_object_set (cell, "width", 16, "height", 16, NULL);
717 g_object_set_data (G_OBJECT (cell),
718 "value-to-stock-id", value_to_stock_id);
719 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (editable), cell, FALSE);
720 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (editable), cell,
722 GINT_TO_POINTER (format->type),
728 add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
729 const char *title, int width,
730 const char *(*value_to_stock_id) (enum fmt_type, int value),
733 PsppSheetViewColumn *column;
734 GtkCellRenderer *cell;
739 store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
740 va_start (args, value_to_stock_id);
741 while ((name = va_arg (args, const char *)) != NULL)
743 int value = va_arg (args, int);
744 gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
751 cell = gtk_cell_renderer_combo_new ();
754 "model", GTK_TREE_MODEL (store),
757 if (value_to_stock_id != NULL)
759 g_object_set_data (G_OBJECT (cell),
760 "value-to-stock-id", value_to_stock_id);
761 g_signal_connect (cell, "editing-started",
762 G_CALLBACK (on_combo_editing_started),
766 column = add_var_sheet_column (var_sheet, cell, column_id, title, width);
768 cell = gtk_cell_renderer_pixbuf_new ();
769 g_object_set (cell, "width", 16, "height", 16, NULL);
770 pspp_sheet_view_column_pack_end (column, cell, FALSE);
771 pspp_sheet_view_column_set_cell_data_func (
772 column, cell, render_var_cell, var_sheet, NULL);
776 add_popup_menu (PsppireVarSheet *var_sheet,
777 PsppSheetViewColumn *column,
778 void (*on_click) (PsppireCellRendererButton *,
780 PsppireVarSheet *var_sheet))
782 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
783 const char *button_label = "...";
784 GtkCellRenderer *button_renderer;
787 button_renderer = psppire_cell_renderer_button_new ();
788 g_object_set (button_renderer,
789 "label", button_label,
792 g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click),
794 pspp_sheet_view_column_pack_start (column, button_renderer, FALSE);
795 pspp_sheet_view_column_set_cell_data_func (
796 column, button_renderer, render_popup_cell, var_sheet, NULL);
798 content_width = GPOINTER_TO_INT (g_object_get_data (
799 G_OBJECT (column), "content-width"));
800 content_width += get_string_width (sheet_view, button_renderer,
802 if (content_width > pspp_sheet_view_column_get_fixed_width (column))
803 pspp_sheet_view_column_set_fixed_width (column, content_width);
807 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
808 gint wx, gint wy, size_t *row, size_t *column)
810 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
814 PsppSheetViewColumn *tree_column;
815 GtkTreeModel *tree_model;
819 /* Check that WIDGET is really visible on the screen before we
820 do anything else. This is a bug fix for a sticky situation:
821 when text_data_import_assistant() returns, it frees the data
822 necessary to compose the tool tip message, but there may be
823 a tool tip under preparation at that point (even if there is
824 no visible tool tip) that will call back into us a little
825 bit later. Perhaps the correct solution to this problem is
826 to make the data related to the tool tips part of a GObject
827 that only gets destroyed when all references are released,
828 but this solution appears to be effective too. */
829 if (!gtk_widget_get_mapped (widget))
832 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
834 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
835 &path, &tree_column, NULL, NULL))
838 column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number");
839 if (column_ptr == NULL)
841 *column = GPOINTER_TO_INT (column_ptr) - 1;
843 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
846 tree_model = pspp_sheet_view_get_model (tree_view);
847 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
848 gtk_tree_path_free (path);
852 *row = GPOINTER_TO_INT (iter.user_data);
857 on_query_var_tooltip (GtkWidget *widget, gint wx, gint wy,
858 gboolean keyboard_mode UNUSED,
859 GtkTooltip *tooltip, gpointer *user_data UNUSED)
861 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
863 struct variable *var;
866 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
869 dict = psppire_var_sheet_get_dictionary (var_sheet);
870 g_return_val_if_fail (dict != NULL, FALSE);
872 if (row >= psppire_dict_get_var_cnt (dict))
874 gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a "
879 var = psppire_dict_get_variable (dict, row);
880 g_return_val_if_fail (var != NULL, FALSE);
886 char text[FMT_STRING_LEN_MAX + 1];
888 fmt_to_string (var_get_print_format (var), text);
889 gtk_tooltip_set_text (tooltip, text);
894 if (var_has_value_labels (var))
896 const struct val_labs *vls = var_get_value_labels (var);
897 const struct val_lab **labels = val_labs_sorted (vls);
902 for (i = 0; i < val_labs_count (vls); i++)
904 const struct val_lab *vl = labels[i];
907 if (i >= 10 || ds_length (&s) > 500)
909 ds_put_cstr (&s, "...");
913 vstr = value_to_text (vl->value, var);
914 ds_put_format (&s, _("{%s, %s}\n"), vstr,
915 val_lab_get_escaped_label (vl));
919 ds_chomp_byte (&s, '\n');
921 gtk_tooltip_set_text (tooltip, ds_cstr (&s));
933 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
935 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
938 menu = get_widget_assert (var_sheet->builder, "varsheet-variable-popup");
939 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
943 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
945 do_popup_menu (widget, 0, gtk_get_current_event_time ());
949 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
950 gpointer user_data UNUSED)
952 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
954 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
956 PsppSheetSelection *selection;
958 selection = pspp_sheet_view_get_selection (sheet_view);
959 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
963 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
964 &path, NULL, NULL, NULL))
966 pspp_sheet_selection_unselect_all (selection);
967 pspp_sheet_selection_select_path (selection, path);
968 gtk_tree_path_free (path);
972 do_popup_menu (widget, event->button, event->time);
980 psppire_fmt_use_get_type (void)
982 static GType etype = 0;
985 static const GEnumValue values[] =
987 { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
988 { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
992 etype = g_enum_register_static
993 (g_intern_static_string ("PsppireFmtUse"), values);
1002 PROP_MAY_CREATE_VARS,
1003 PROP_MAY_DELETE_VARS,
1009 psppire_var_sheet_set_property (GObject *object,
1011 const GValue *value,
1014 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
1018 case PROP_DICTIONARY:
1019 psppire_var_sheet_set_dictionary (obj,
1020 PSPPIRE_DICT (g_value_get_object (
1024 case PROP_MAY_CREATE_VARS:
1025 psppire_var_sheet_set_may_create_vars (obj,
1026 g_value_get_boolean (value));
1029 case PROP_MAY_DELETE_VARS:
1030 psppire_var_sheet_set_may_delete_vars (obj,
1031 g_value_get_boolean (value));
1034 case PROP_FORMAT_TYPE:
1035 obj->format_use = g_value_get_enum (value);
1038 case PROP_UI_MANAGER:
1040 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1046 psppire_var_sheet_get_property (GObject *object,
1051 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
1055 case PROP_DICTIONARY:
1056 g_value_set_object (value,
1057 G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
1060 case PROP_MAY_CREATE_VARS:
1061 g_value_set_boolean (value, obj->may_create_vars);
1064 case PROP_MAY_DELETE_VARS:
1065 g_value_set_boolean (value, obj->may_delete_vars);
1068 case PROP_FORMAT_TYPE:
1069 g_value_set_enum (value, obj->format_use);
1072 case PROP_UI_MANAGER:
1073 g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj));
1077 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1083 psppire_var_sheet_dispose (GObject *obj)
1085 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
1088 if (var_sheet->dispose_has_run)
1091 var_sheet->dispose_has_run = TRUE;
1093 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1094 if ( var_sheet->dict_signals[i])
1095 g_signal_handler_disconnect (var_sheet->dict,
1096 var_sheet->dict_signals[i]);
1098 if (var_sheet->dict)
1099 g_object_unref (var_sheet->dict);
1102 g_object_unref (var_sheet->uim);
1104 /* These dialogs are not GObjects (although they should be!)
1105 But for now, unreffing them only causes a GCritical Error
1106 so comment them out for now. (and accept the memory leakage)
1108 g_object_unref (var_sheet->val_labs_dialog);
1109 g_object_unref (var_sheet->missing_val_dialog);
1110 g_object_unref (var_sheet->var_type_dialog);
1113 G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
1117 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
1119 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1122 gobject_class->set_property = psppire_var_sheet_set_property;
1123 gobject_class->get_property = psppire_var_sheet_get_property;
1124 gobject_class->dispose = psppire_var_sheet_dispose;
1126 g_signal_new ("var-double-clicked",
1127 G_OBJECT_CLASS_TYPE (gobject_class),
1130 g_signal_accumulator_true_handled, NULL,
1131 psppire_marshal_BOOLEAN__INT,
1132 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1134 pspec = g_param_spec_object ("dictionary",
1135 "Dictionary displayed by the sheet",
1136 "The PsppireDict that the sheet displays "
1137 "may allow the user to edit",
1140 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
1142 pspec = g_param_spec_boolean ("may-create-vars",
1143 "May create variables",
1144 "Whether the user may create more variables",
1147 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
1149 pspec = g_param_spec_boolean ("may-delete-vars",
1150 "May delete variables",
1151 "Whether the user may delete variables",
1154 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
1156 pspec = g_param_spec_enum ("format-use",
1157 "Use of variable format",
1158 ("Whether variables have input or output "
1160 PSPPIRE_TYPE_FMT_USE,
1163 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
1165 pspec = g_param_spec_object ("ui-manager",
1167 "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.",
1168 GTK_TYPE_UI_MANAGER,
1170 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1174 render_row_number_cell (PsppSheetViewColumn *tree_column,
1175 GtkCellRenderer *cell,
1176 GtkTreeModel *model,
1180 PsppireVarSheet *var_sheet = user_data;
1181 GValue gvalue = { 0, };
1184 row = GPOINTER_TO_INT (iter->user_data);
1186 g_value_init (&gvalue, G_TYPE_INT);
1187 g_value_set_int (&gvalue, row + 1);
1188 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1189 g_value_unset (&gvalue);
1191 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1192 g_object_set (cell, "editable", TRUE, NULL);
1194 g_object_set (cell, "editable", FALSE, NULL);
1198 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1200 PsppireVarSheet *var_sheet)
1204 g_return_if_fail (var_sheet->dict != NULL);
1206 path = gtk_tree_path_new_from_string (path_string);
1207 if (gtk_tree_path_get_depth (path) == 1)
1209 gint *indices = gtk_tree_path_get_indices (path);
1210 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1213 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1214 indices[0], &handled);
1217 gtk_tree_path_free (path);
1221 psppire_var_sheet_variables_column_clicked (PsppSheetViewColumn *column,
1222 PsppireVarSheet *var_sheet)
1224 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1225 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1227 pspp_sheet_selection_select_all (selection);
1230 static PsppSheetViewColumn *
1231 make_row_number_column (PsppireVarSheet *var_sheet)
1233 PsppSheetViewColumn *column;
1234 GtkCellRenderer *renderer;
1236 renderer = psppire_cell_renderer_button_new ();
1237 g_object_set (renderer, "xalign", 1.0, NULL);
1238 g_signal_connect (renderer, "double-clicked",
1239 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1242 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1244 pspp_sheet_view_column_set_clickable (column, TRUE);
1245 pspp_sheet_view_column_set_cell_data_func (
1246 column, renderer, render_row_number_cell, var_sheet, NULL);
1247 pspp_sheet_view_column_set_fixed_width (column, 50);
1248 g_signal_connect (column, "clicked",
1249 G_CALLBACK (psppire_var_sheet_variables_column_clicked),
1256 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1258 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1259 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1260 PsppireDict *dict = var_sheet->dict;
1261 const struct range_set_node *node;
1262 struct range_set *selected;
1264 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 >= 0 && 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;
1287 gboolean may_delete;
1291 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1293 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1294 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1295 && 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);
1315 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1316 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1320 on_edit_insert_variable (GtkAction *action, 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;
1349 obj->format_use = FMT_FOR_OUTPUT;
1350 obj->may_create_vars = TRUE;
1351 obj->may_delete_vars = TRUE;
1353 obj->scroll_to_bottom_signal = 0;
1355 obj->container = NULL;
1356 obj->dispose_has_run = FALSE;
1359 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1361 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1362 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1363 g_signal_connect (list->data, "editing-started",
1364 G_CALLBACK (on_name_column_editing_started), NULL);
1367 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1368 add_popup_menu (obj, column, on_type_click);
1370 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1372 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1374 add_text_column (obj, VS_LABEL, _("Label"), 20);
1376 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1377 add_popup_menu (obj, column, on_value_labels_click);
1379 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1380 add_popup_menu (obj, column, on_missing_values_click);
1382 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1384 add_combo_column (obj, VS_ALIGN, _("Align"), 8, alignment_to_stock_id,
1385 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1386 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1387 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1390 add_combo_column (obj, VS_MEASURE, _("Measure"), 11, measure_to_stock_id,
1391 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1392 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1393 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1396 add_combo_column (obj, VS_ROLE, _("Role"), 11, role_to_stock_id,
1397 var_role_to_string (ROLE_INPUT), ROLE_INPUT,
1398 var_role_to_string (ROLE_TARGET), ROLE_TARGET,
1399 var_role_to_string (ROLE_BOTH), ROLE_BOTH,
1400 var_role_to_string (ROLE_NONE), ROLE_NONE,
1401 var_role_to_string (ROLE_PARTITION), ROLE_PARTITION,
1402 var_role_to_string (ROLE_SPLIT), ROLE_SPLIT,
1405 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1406 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1407 PSPP_SHEET_SELECTION_MULTIPLE);
1409 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1410 g_signal_connect (obj, "query-tooltip",
1411 G_CALLBACK (on_query_var_tooltip), NULL);
1412 g_signal_connect (obj, "button-press-event",
1413 G_CALLBACK (on_button_pressed), NULL);
1414 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1416 obj->builder = builder_new ("var-sheet.ui");
1418 action = get_action_assert (obj->builder, "edit_clear-variables");
1419 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1421 gtk_action_set_sensitive (action, FALSE);
1422 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1423 "changed", G_CALLBACK (on_selection_changed), NULL);
1425 action = get_action_assert (obj->builder, "edit_insert-variable");
1426 gtk_action_set_sensitive (action, FALSE);
1427 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1432 psppire_var_sheet_new (void)
1434 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1438 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1440 return var_sheet->dict;
1444 refresh_model (PsppireVarSheet *var_sheet)
1446 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1448 if (var_sheet->dict != NULL)
1450 PsppireEmptyListStore *store;
1453 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1454 + var_sheet->may_create_vars);
1455 store = psppire_empty_list_store_new (n_rows);
1456 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1457 GTK_TREE_MODEL (store));
1458 g_object_unref (store);
1463 on_var_changed (PsppireDict *dict, glong row,
1464 guint what, const struct variable *oldvar,
1465 PsppireVarSheet *var_sheet)
1467 PsppireEmptyListStore *store;
1469 g_return_if_fail (dict == var_sheet->dict);
1471 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1472 PSPP_SHEET_VIEW (var_sheet)));
1473 g_return_if_fail (store != NULL);
1475 psppire_empty_list_store_row_changed (store, row);
1479 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1481 PsppireEmptyListStore *store;
1484 g_return_if_fail (dict == var_sheet->dict);
1486 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1487 PSPP_SHEET_VIEW (var_sheet)));
1488 g_return_if_fail (store != NULL);
1490 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1491 + var_sheet->may_create_vars);
1492 psppire_empty_list_store_set_n_rows (store, n_rows);
1493 psppire_empty_list_store_row_inserted (store, row);
1497 on_var_deleted (PsppireDict *dict,
1498 const struct variable *var, int dict_idx, int case_idx,
1499 PsppireVarSheet *var_sheet)
1501 PsppireEmptyListStore *store;
1504 g_return_if_fail (dict == var_sheet->dict);
1506 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1507 PSPP_SHEET_VIEW (var_sheet)));
1508 g_return_if_fail (store != NULL);
1510 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1511 + var_sheet->may_create_vars);
1512 psppire_empty_list_store_set_n_rows (store, n_rows);
1513 psppire_empty_list_store_row_deleted (store, dict_idx);
1517 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1519 g_return_if_fail (dict == var_sheet->dict);
1520 refresh_model (var_sheet);
1524 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1527 if (var_sheet->dict != NULL)
1531 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1533 if (var_sheet->dict_signals[i])
1534 g_signal_handler_disconnect (var_sheet->dict,
1535 var_sheet->dict_signals[i]);
1537 var_sheet->dict_signals[i] = 0;
1540 g_object_unref (var_sheet->dict);
1543 var_sheet->dict = dict;
1547 g_object_ref (dict);
1549 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1550 = g_signal_connect (dict, "backend-changed",
1551 G_CALLBACK (on_backend_changed), var_sheet);
1553 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED]
1554 = g_signal_connect (dict, "variable-changed",
1555 G_CALLBACK (on_var_changed), var_sheet);
1557 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1558 = g_signal_connect (dict, "variable-inserted",
1559 G_CALLBACK (on_var_inserted), var_sheet);
1561 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1562 = g_signal_connect (dict, "variable-deleted",
1563 G_CALLBACK (on_var_deleted), var_sheet);
1566 refresh_model (var_sheet);
1570 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1572 return var_sheet->may_create_vars;
1576 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1577 gboolean may_create_vars)
1579 if (var_sheet->may_create_vars != may_create_vars)
1581 PsppireEmptyListStore *store;
1584 var_sheet->may_create_vars = may_create_vars;
1586 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1587 PSPP_SHEET_VIEW (var_sheet)));
1588 g_return_if_fail (store != NULL);
1590 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1591 + var_sheet->may_create_vars);
1592 psppire_empty_list_store_set_n_rows (store, n_rows);
1594 if (may_create_vars)
1595 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1597 psppire_empty_list_store_row_deleted (store, n_rows);
1599 on_selection_changed (pspp_sheet_view_get_selection (
1600 PSPP_SHEET_VIEW (var_sheet)), NULL);
1605 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1607 return var_sheet->may_delete_vars;
1611 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1612 gboolean may_delete_vars)
1614 if (var_sheet->may_delete_vars != may_delete_vars)
1616 var_sheet->may_delete_vars = may_delete_vars;
1617 on_selection_changed (pspp_sheet_view_get_selection (
1618 PSPP_SHEET_VIEW (var_sheet)), NULL);
1623 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1625 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1628 path = gtk_tree_path_new_from_indices (dict_index, -1);
1629 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1630 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1631 gtk_tree_path_free (path);
1635 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1637 if (var_sheet->uim == NULL)
1639 var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1641 GTK_TYPE_UI_MANAGER));
1642 g_object_ref (var_sheet->uim);
1645 return var_sheet->uim;