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_unref (var_sheet->builder);
1120 G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
1124 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
1126 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1129 gobject_class->set_property = psppire_var_sheet_set_property;
1130 gobject_class->get_property = psppire_var_sheet_get_property;
1131 gobject_class->dispose = psppire_var_sheet_dispose;
1133 g_signal_new ("var-double-clicked",
1134 G_OBJECT_CLASS_TYPE (gobject_class),
1137 g_signal_accumulator_true_handled, NULL,
1138 psppire_marshal_BOOLEAN__INT,
1139 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1141 pspec = g_param_spec_object ("dictionary",
1142 "Dictionary displayed by the sheet",
1143 "The PsppireDict that the sheet displays "
1144 "may allow the user to edit",
1147 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
1149 pspec = g_param_spec_boolean ("may-create-vars",
1150 "May create variables",
1151 "Whether the user may create more variables",
1154 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
1156 pspec = g_param_spec_boolean ("may-delete-vars",
1157 "May delete variables",
1158 "Whether the user may delete variables",
1161 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
1163 pspec = g_param_spec_enum ("format-use",
1164 "Use of variable format",
1165 ("Whether variables have input or output "
1167 PSPPIRE_TYPE_FMT_USE,
1170 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
1172 pspec = g_param_spec_object ("ui-manager",
1174 "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.",
1175 GTK_TYPE_UI_MANAGER,
1177 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1181 render_row_number_cell (PsppSheetViewColumn *tree_column,
1182 GtkCellRenderer *cell,
1183 GtkTreeModel *model,
1187 PsppireVarSheet *var_sheet = user_data;
1188 GValue gvalue = { 0, };
1191 row = GPOINTER_TO_INT (iter->user_data);
1193 g_value_init (&gvalue, G_TYPE_INT);
1194 g_value_set_int (&gvalue, row + 1);
1195 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1196 g_value_unset (&gvalue);
1198 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1199 g_object_set (cell, "editable", TRUE, NULL);
1201 g_object_set (cell, "editable", FALSE, NULL);
1205 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1207 PsppireVarSheet *var_sheet)
1211 g_return_if_fail (var_sheet->dict != NULL);
1213 path = gtk_tree_path_new_from_string (path_string);
1214 if (gtk_tree_path_get_depth (path) == 1)
1216 gint *indices = gtk_tree_path_get_indices (path);
1217 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1220 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1221 indices[0], &handled);
1224 gtk_tree_path_free (path);
1228 psppire_var_sheet_variables_column_clicked (PsppSheetViewColumn *column,
1229 PsppireVarSheet *var_sheet)
1231 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1232 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1234 pspp_sheet_selection_select_all (selection);
1237 static PsppSheetViewColumn *
1238 make_row_number_column (PsppireVarSheet *var_sheet)
1240 PsppSheetViewColumn *column;
1241 GtkCellRenderer *renderer;
1243 renderer = psppire_cell_renderer_button_new ();
1244 g_object_set (renderer, "xalign", 1.0, NULL);
1245 g_signal_connect (renderer, "double-clicked",
1246 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1249 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1251 pspp_sheet_view_column_set_clickable (column, TRUE);
1252 pspp_sheet_view_column_set_cell_data_func (
1253 column, renderer, render_row_number_cell, var_sheet, NULL);
1254 pspp_sheet_view_column_set_fixed_width (column, 50);
1255 g_signal_connect (column, "clicked",
1256 G_CALLBACK (psppire_var_sheet_variables_column_clicked),
1263 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1265 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1266 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1267 PsppireDict *dict = var_sheet->dict;
1268 const struct range_set_node *node;
1269 struct range_set *selected;
1271 selected = pspp_sheet_selection_get_range_set (selection);
1272 for (node = range_set_last (selected); node != NULL;
1273 node = range_set_prev (selected, node))
1277 for (i = 1; i <= range_set_node_get_width (node); i++)
1279 unsigned long row = range_set_node_get_end (node) - i;
1280 if (row < psppire_dict_get_var_cnt (dict))
1281 psppire_dict_delete_variables (dict, row, 1);
1284 range_set_destroy (selected);
1288 on_selection_changed (PsppSheetSelection *selection,
1289 gpointer user_data UNUSED)
1291 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1292 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1293 gint n_selected_rows;
1294 gboolean may_delete;
1298 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1300 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1301 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1302 && n_selected_rows > 0));
1304 switch (n_selected_rows)
1311 /* The row used for inserting new variables cannot be deleted. */
1312 path = gtk_tree_path_new_from_indices (
1313 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1314 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1315 gtk_tree_path_free (path);
1322 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1323 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1327 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1329 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1330 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1331 PsppireDict *dict = var_sheet->dict;
1332 struct range_set *selected;
1335 selected = pspp_sheet_selection_get_range_set (selection);
1336 row = range_set_scan (selected, 0);
1337 range_set_destroy (selected);
1339 if (row <= psppire_dict_get_var_cnt (dict))
1342 if (psppire_dict_generate_name (dict, name, sizeof name))
1343 psppire_dict_insert_variable (dict, row, name);
1348 psppire_var_sheet_init (PsppireVarSheet *obj)
1350 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1351 PsppSheetViewColumn *column;
1356 obj->format_use = FMT_FOR_OUTPUT;
1357 obj->may_create_vars = TRUE;
1358 obj->may_delete_vars = TRUE;
1360 obj->scroll_to_bottom_signal = 0;
1362 obj->container = NULL;
1363 obj->dispose_has_run = FALSE;
1366 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1368 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1369 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1370 g_signal_connect (list->data, "editing-started",
1371 G_CALLBACK (on_name_column_editing_started), NULL);
1374 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1375 add_popup_menu (obj, column, on_type_click);
1377 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1379 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1381 add_text_column (obj, VS_LABEL, _("Label"), 20);
1383 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1384 add_popup_menu (obj, column, on_value_labels_click);
1386 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1387 add_popup_menu (obj, column, on_missing_values_click);
1389 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1391 add_combo_column (obj, VS_ALIGN, _("Align"), 8, alignment_to_stock_id,
1392 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1393 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1394 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1397 add_combo_column (obj, VS_MEASURE, _("Measure"), 11, measure_to_stock_id,
1398 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1399 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1400 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1403 add_combo_column (obj, VS_ROLE, _("Role"), 11, role_to_stock_id,
1404 var_role_to_string (ROLE_INPUT), ROLE_INPUT,
1405 var_role_to_string (ROLE_TARGET), ROLE_TARGET,
1406 var_role_to_string (ROLE_BOTH), ROLE_BOTH,
1407 var_role_to_string (ROLE_NONE), ROLE_NONE,
1408 var_role_to_string (ROLE_PARTITION), ROLE_PARTITION,
1409 var_role_to_string (ROLE_SPLIT), ROLE_SPLIT,
1412 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1413 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1414 PSPP_SHEET_SELECTION_MULTIPLE);
1416 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1417 g_signal_connect (obj, "query-tooltip",
1418 G_CALLBACK (on_query_var_tooltip), NULL);
1419 g_signal_connect (obj, "button-press-event",
1420 G_CALLBACK (on_button_pressed), NULL);
1421 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1423 obj->builder = builder_new ("var-sheet.ui");
1425 action = get_action_assert (obj->builder, "edit_clear-variables");
1426 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1428 gtk_action_set_sensitive (action, FALSE);
1429 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1430 "changed", G_CALLBACK (on_selection_changed), NULL);
1432 action = get_action_assert (obj->builder, "edit_insert-variable");
1433 gtk_action_set_sensitive (action, FALSE);
1434 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1439 psppire_var_sheet_new (void)
1441 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1445 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1447 return var_sheet->dict;
1451 refresh_model (PsppireVarSheet *var_sheet)
1453 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1455 if (var_sheet->dict != NULL)
1457 PsppireEmptyListStore *store;
1460 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1461 + var_sheet->may_create_vars);
1462 store = psppire_empty_list_store_new (n_rows);
1463 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1464 GTK_TREE_MODEL (store));
1465 g_object_unref (store);
1470 on_var_changed (PsppireDict *dict, glong row,
1471 guint what, const struct variable *oldvar,
1472 PsppireVarSheet *var_sheet)
1474 PsppireEmptyListStore *store;
1476 g_return_if_fail (dict == var_sheet->dict);
1478 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1479 PSPP_SHEET_VIEW (var_sheet)));
1480 g_return_if_fail (store != NULL);
1482 psppire_empty_list_store_row_changed (store, row);
1486 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1488 PsppireEmptyListStore *store;
1491 g_return_if_fail (dict == var_sheet->dict);
1493 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1494 PSPP_SHEET_VIEW (var_sheet)));
1495 g_return_if_fail (store != NULL);
1497 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1498 + var_sheet->may_create_vars);
1499 psppire_empty_list_store_set_n_rows (store, n_rows);
1500 psppire_empty_list_store_row_inserted (store, row);
1504 on_var_deleted (PsppireDict *dict,
1505 const struct variable *var, int dict_idx, int case_idx,
1506 PsppireVarSheet *var_sheet)
1508 PsppireEmptyListStore *store;
1511 g_return_if_fail (dict == var_sheet->dict);
1513 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1514 PSPP_SHEET_VIEW (var_sheet)));
1515 g_return_if_fail (store != NULL);
1517 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1518 + var_sheet->may_create_vars);
1519 psppire_empty_list_store_set_n_rows (store, n_rows);
1520 psppire_empty_list_store_row_deleted (store, dict_idx);
1524 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1526 g_return_if_fail (dict == var_sheet->dict);
1527 refresh_model (var_sheet);
1531 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1534 if (var_sheet->dict != NULL)
1538 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1540 if (var_sheet->dict_signals[i])
1541 g_signal_handler_disconnect (var_sheet->dict,
1542 var_sheet->dict_signals[i]);
1544 var_sheet->dict_signals[i] = 0;
1547 g_object_unref (var_sheet->dict);
1550 var_sheet->dict = dict;
1554 g_object_ref (dict);
1556 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1557 = g_signal_connect (dict, "backend-changed",
1558 G_CALLBACK (on_backend_changed), var_sheet);
1560 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED]
1561 = g_signal_connect (dict, "variable-changed",
1562 G_CALLBACK (on_var_changed), var_sheet);
1564 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1565 = g_signal_connect (dict, "variable-inserted",
1566 G_CALLBACK (on_var_inserted), var_sheet);
1568 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1569 = g_signal_connect (dict, "variable-deleted",
1570 G_CALLBACK (on_var_deleted), var_sheet);
1573 refresh_model (var_sheet);
1577 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1579 return var_sheet->may_create_vars;
1583 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1584 gboolean may_create_vars)
1586 if (var_sheet->may_create_vars != may_create_vars)
1588 PsppireEmptyListStore *store;
1591 var_sheet->may_create_vars = may_create_vars;
1593 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1594 PSPP_SHEET_VIEW (var_sheet)));
1595 g_return_if_fail (store != NULL);
1597 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1598 + var_sheet->may_create_vars);
1599 psppire_empty_list_store_set_n_rows (store, n_rows);
1601 if (may_create_vars)
1602 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1604 psppire_empty_list_store_row_deleted (store, n_rows);
1606 on_selection_changed (pspp_sheet_view_get_selection (
1607 PSPP_SHEET_VIEW (var_sheet)), NULL);
1612 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1614 return var_sheet->may_delete_vars;
1618 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1619 gboolean may_delete_vars)
1621 if (var_sheet->may_delete_vars != may_delete_vars)
1623 var_sheet->may_delete_vars = may_delete_vars;
1624 on_selection_changed (pspp_sheet_view_get_selection (
1625 PSPP_SHEET_VIEW (var_sheet)), NULL);
1630 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1632 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1635 path = gtk_tree_path_new_from_indices (dict_index, -1);
1636 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1637 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1638 gtk_tree_path_free (path);
1642 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1644 if (var_sheet->uim == NULL)
1646 var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1648 GTK_TYPE_UI_MANAGER));
1649 g_object_ref (var_sheet->uim);
1652 return var_sheet->uim;