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-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);
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);
591 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
592 NULL, NULL, NULL, &width, NULL);
597 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
604 ds_put_byte_multiple (&s, '0', char_cnt);
605 ds_put_byte (&s, ' ');
606 width = get_string_width (treeview, renderer, ds_cstr (&s));
612 static PsppSheetViewColumn *
613 add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer,
614 enum vs_column column_id,
615 const char *title, int width)
617 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
618 int title_width, content_width;
619 PsppSheetViewColumn *column;
621 column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL);
622 g_object_set_data (G_OBJECT (column), "column-number",
623 GINT_TO_POINTER (column_id) + 1);
625 pspp_sheet_view_column_set_cell_data_func (
626 column, renderer, render_var_cell, var_sheet, NULL);
628 title_width = get_string_width (sheet_view, renderer, title);
629 content_width = get_monospace_width (sheet_view, renderer, width);
630 g_object_set_data (G_OBJECT (column), "content-width",
631 GINT_TO_POINTER (content_width));
633 pspp_sheet_view_column_set_fixed_width (column,
634 MAX (title_width, content_width));
635 pspp_sheet_view_column_set_resizable (column, TRUE);
637 pspp_sheet_view_append_column (sheet_view, column);
639 g_signal_connect (renderer, "edited",
640 G_CALLBACK (on_var_column_edited),
642 g_object_set_data (G_OBJECT (renderer), "column-id",
643 GINT_TO_POINTER (column_id));
644 g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet);
649 static PsppSheetViewColumn *
650 add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
651 const char *title, int width)
653 return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (),
654 column_id, title, width);
657 static PsppSheetViewColumn *
658 add_spin_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
659 const char *title, int width)
661 return add_var_sheet_column (var_sheet, gtk_cell_renderer_spin_new (),
662 column_id, title, width);
666 measure_to_stock_id (enum fmt_type type, int measure)
668 return get_var_measurement_stock_id (type, measure);
672 alignment_to_stock_id (enum fmt_type type, int alignment)
674 return get_var_align_stock_id (alignment);
678 role_to_stock_id (enum fmt_type type, int role)
680 return get_var_role_stock_id (role);
684 render_var_pixbuf (GtkCellLayout *cell_layout,
685 GtkCellRenderer *cell,
686 GtkTreeModel *tree_model,
690 const char *(*value_to_stock_id) (enum fmt_type, int value);
691 enum fmt_type type = GPOINTER_TO_INT (data);
694 value_to_stock_id = g_object_get_data (G_OBJECT (cell), "value-to-stock-id");
696 gtk_tree_model_get (tree_model, iter, 0, &value, -1);
697 g_object_set (cell, "stock-id", value_to_stock_id (type, value), NULL);
701 on_combo_editing_started (GtkCellRenderer *renderer,
702 GtkCellEditable *editable,
706 PsppireVarSheet *var_sheet = user_data;
708 if (GTK_IS_COMBO_BOX (editable))
710 struct variable *var = path_string_to_variable (var_sheet, path_string);
711 const struct fmt_spec *format = var_get_print_format (var);
712 const char *(*value_to_stock_id) (enum fmt_type, int value);
713 GtkCellRenderer *cell;
715 value_to_stock_id = g_object_get_data (G_OBJECT (renderer),
716 "value-to-stock-id");
718 cell = gtk_cell_renderer_pixbuf_new ();
719 g_object_set (cell, "width", 16, "height", 16, NULL);
720 g_object_set_data (G_OBJECT (cell),
721 "value-to-stock-id", value_to_stock_id);
722 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (editable), cell, FALSE);
723 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (editable), cell,
725 GINT_TO_POINTER (format->type),
731 add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
732 const char *title, int width,
733 const char *(*value_to_stock_id) (enum fmt_type, int value),
736 PsppSheetViewColumn *column;
737 GtkCellRenderer *cell;
742 store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
743 va_start (args, value_to_stock_id);
744 while ((name = va_arg (args, const char *)) != NULL)
746 int value = va_arg (args, int);
747 gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
754 cell = gtk_cell_renderer_combo_new ();
757 "model", GTK_TREE_MODEL (store),
760 if (value_to_stock_id != NULL)
762 g_object_set_data (G_OBJECT (cell),
763 "value-to-stock-id", value_to_stock_id);
764 g_signal_connect (cell, "editing-started",
765 G_CALLBACK (on_combo_editing_started),
769 column = add_var_sheet_column (var_sheet, cell, column_id, title, width);
771 cell = gtk_cell_renderer_pixbuf_new ();
772 g_object_set (cell, "width", 16, "height", 16, NULL);
773 pspp_sheet_view_column_pack_end (column, cell, FALSE);
774 pspp_sheet_view_column_set_cell_data_func (
775 column, cell, render_var_cell, var_sheet, NULL);
779 add_popup_menu (PsppireVarSheet *var_sheet,
780 PsppSheetViewColumn *column,
781 void (*on_click) (PsppireCellRendererButton *,
783 PsppireVarSheet *var_sheet))
785 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
786 const char *button_label = "...";
787 GtkCellRenderer *button_renderer;
790 button_renderer = psppire_cell_renderer_button_new ();
791 g_object_set (button_renderer,
792 "label", button_label,
795 g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click),
797 pspp_sheet_view_column_pack_start (column, button_renderer, FALSE);
798 pspp_sheet_view_column_set_cell_data_func (
799 column, button_renderer, render_popup_cell, var_sheet, NULL);
801 content_width = GPOINTER_TO_INT (g_object_get_data (
802 G_OBJECT (column), "content-width"));
803 content_width += get_string_width (sheet_view, button_renderer,
805 if (content_width > pspp_sheet_view_column_get_fixed_width (column))
806 pspp_sheet_view_column_set_fixed_width (column, content_width);
810 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
811 gint wx, gint wy, size_t *row, size_t *column)
813 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
817 PsppSheetViewColumn *tree_column;
818 GtkTreeModel *tree_model;
822 /* Check that WIDGET is really visible on the screen before we
823 do anything else. This is a bug fix for a sticky situation:
824 when text_data_import_assistant() returns, it frees the data
825 necessary to compose the tool tip message, but there may be
826 a tool tip under preparation at that point (even if there is
827 no visible tool tip) that will call back into us a little
828 bit later. Perhaps the correct solution to this problem is
829 to make the data related to the tool tips part of a GObject
830 that only gets destroyed when all references are released,
831 but this solution appears to be effective too. */
832 if (!gtk_widget_get_mapped (widget))
835 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
837 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
838 &path, &tree_column, NULL, NULL))
841 column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number");
842 if (column_ptr == NULL)
844 *column = GPOINTER_TO_INT (column_ptr) - 1;
846 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
849 tree_model = pspp_sheet_view_get_model (tree_view);
850 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
851 gtk_tree_path_free (path);
855 *row = GPOINTER_TO_INT (iter.user_data);
860 on_query_var_tooltip (GtkWidget *widget, gint wx, gint wy,
861 gboolean keyboard_mode UNUSED,
862 GtkTooltip *tooltip, gpointer *user_data UNUSED)
864 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
866 struct variable *var;
869 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
872 dict = psppire_var_sheet_get_dictionary (var_sheet);
873 g_return_val_if_fail (dict != NULL, FALSE);
875 if (row >= psppire_dict_get_var_cnt (dict))
877 gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a "
882 var = psppire_dict_get_variable (dict, row);
883 g_return_val_if_fail (var != NULL, FALSE);
889 char text[FMT_STRING_LEN_MAX + 1];
891 fmt_to_string (var_get_print_format (var), text);
892 gtk_tooltip_set_text (tooltip, text);
897 if (var_has_value_labels (var))
899 const struct val_labs *vls = var_get_value_labels (var);
900 const struct val_lab **labels = val_labs_sorted (vls);
905 for (i = 0; i < val_labs_count (vls); i++)
907 const struct val_lab *vl = labels[i];
910 if (i >= 10 || ds_length (&s) > 500)
912 ds_put_cstr (&s, "...");
916 vstr = value_to_text (vl->value, var);
917 ds_put_format (&s, _("{%s, %s}\n"), vstr,
918 val_lab_get_escaped_label (vl));
922 ds_chomp_byte (&s, '\n');
924 gtk_tooltip_set_text (tooltip, ds_cstr (&s));
936 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
938 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
941 menu = get_widget_assert (var_sheet->builder, "varsheet-variable-popup");
942 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
946 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
948 do_popup_menu (widget, 0, gtk_get_current_event_time ());
952 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
953 gpointer user_data UNUSED)
955 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
957 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
959 PsppSheetSelection *selection;
961 selection = pspp_sheet_view_get_selection (sheet_view);
962 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
966 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
967 &path, NULL, NULL, NULL))
969 pspp_sheet_selection_unselect_all (selection);
970 pspp_sheet_selection_select_path (selection, path);
971 gtk_tree_path_free (path);
975 do_popup_menu (widget, event->button, event->time);
983 psppire_fmt_use_get_type (void)
985 static GType etype = 0;
988 static const GEnumValue values[] =
990 { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
991 { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
995 etype = g_enum_register_static
996 (g_intern_static_string ("PsppireFmtUse"), values);
1005 PROP_MAY_CREATE_VARS,
1006 PROP_MAY_DELETE_VARS,
1012 psppire_var_sheet_set_property (GObject *object,
1014 const GValue *value,
1017 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
1021 case PROP_DICTIONARY:
1022 psppire_var_sheet_set_dictionary (obj,
1023 PSPPIRE_DICT (g_value_get_object (
1027 case PROP_MAY_CREATE_VARS:
1028 psppire_var_sheet_set_may_create_vars (obj,
1029 g_value_get_boolean (value));
1032 case PROP_MAY_DELETE_VARS:
1033 psppire_var_sheet_set_may_delete_vars (obj,
1034 g_value_get_boolean (value));
1037 case PROP_FORMAT_TYPE:
1038 obj->format_use = g_value_get_enum (value);
1041 case PROP_UI_MANAGER:
1043 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1049 psppire_var_sheet_get_property (GObject *object,
1054 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
1058 case PROP_DICTIONARY:
1059 g_value_set_object (value,
1060 G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
1063 case PROP_MAY_CREATE_VARS:
1064 g_value_set_boolean (value, obj->may_create_vars);
1067 case PROP_MAY_DELETE_VARS:
1068 g_value_set_boolean (value, obj->may_delete_vars);
1071 case PROP_FORMAT_TYPE:
1072 g_value_set_enum (value, obj->format_use);
1075 case PROP_UI_MANAGER:
1076 g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj));
1080 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1086 psppire_var_sheet_dispose (GObject *obj)
1088 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
1091 if (var_sheet->dispose_has_run)
1094 var_sheet->dispose_has_run = TRUE;
1096 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1097 if ( var_sheet->dict_signals[i])
1098 g_signal_handler_disconnect (var_sheet->dict,
1099 var_sheet->dict_signals[i]);
1101 if (var_sheet->dict)
1102 g_object_unref (var_sheet->dict);
1105 g_object_unref (var_sheet->uim);
1107 /* These dialogs are not GObjects (although they should be!)
1108 But for now, unreffing them only causes a GCritical Error
1109 so comment them out for now. (and accept the memory leakage)
1111 g_object_unref (var_sheet->val_labs_dialog);
1112 g_object_unref (var_sheet->missing_val_dialog);
1113 g_object_unref (var_sheet->var_type_dialog);
1116 G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
1120 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
1122 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1125 gobject_class->set_property = psppire_var_sheet_set_property;
1126 gobject_class->get_property = psppire_var_sheet_get_property;
1127 gobject_class->dispose = psppire_var_sheet_dispose;
1129 g_signal_new ("var-double-clicked",
1130 G_OBJECT_CLASS_TYPE (gobject_class),
1133 g_signal_accumulator_true_handled, NULL,
1134 psppire_marshal_BOOLEAN__INT,
1135 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1137 pspec = g_param_spec_object ("dictionary",
1138 "Dictionary displayed by the sheet",
1139 "The PsppireDict that the sheet displays "
1140 "may allow the user to edit",
1143 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
1145 pspec = g_param_spec_boolean ("may-create-vars",
1146 "May create variables",
1147 "Whether the user may create more variables",
1150 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
1152 pspec = g_param_spec_boolean ("may-delete-vars",
1153 "May delete variables",
1154 "Whether the user may delete variables",
1157 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
1159 pspec = g_param_spec_enum ("format-use",
1160 "Use of variable format",
1161 ("Whether variables have input or output "
1163 PSPPIRE_TYPE_FMT_USE,
1166 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
1168 pspec = g_param_spec_object ("ui-manager",
1170 "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.",
1171 GTK_TYPE_UI_MANAGER,
1173 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1177 render_row_number_cell (PsppSheetViewColumn *tree_column,
1178 GtkCellRenderer *cell,
1179 GtkTreeModel *model,
1183 PsppireVarSheet *var_sheet = user_data;
1184 GValue gvalue = { 0, };
1187 row = GPOINTER_TO_INT (iter->user_data);
1189 g_value_init (&gvalue, G_TYPE_INT);
1190 g_value_set_int (&gvalue, row + 1);
1191 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1192 g_value_unset (&gvalue);
1194 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1195 g_object_set (cell, "editable", TRUE, NULL);
1197 g_object_set (cell, "editable", FALSE, NULL);
1201 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1203 PsppireVarSheet *var_sheet)
1207 g_return_if_fail (var_sheet->dict != NULL);
1209 path = gtk_tree_path_new_from_string (path_string);
1210 if (gtk_tree_path_get_depth (path) == 1)
1212 gint *indices = gtk_tree_path_get_indices (path);
1213 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1216 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1217 indices[0], &handled);
1220 gtk_tree_path_free (path);
1224 psppire_var_sheet_variables_column_clicked (PsppSheetViewColumn *column,
1225 PsppireVarSheet *var_sheet)
1227 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1228 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1230 pspp_sheet_selection_select_all (selection);
1233 static PsppSheetViewColumn *
1234 make_row_number_column (PsppireVarSheet *var_sheet)
1236 PsppSheetViewColumn *column;
1237 GtkCellRenderer *renderer;
1239 renderer = psppire_cell_renderer_button_new ();
1240 g_object_set (renderer, "xalign", 1.0, NULL);
1241 g_signal_connect (renderer, "double-clicked",
1242 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1245 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1247 pspp_sheet_view_column_set_clickable (column, TRUE);
1248 pspp_sheet_view_column_set_cell_data_func (
1249 column, renderer, render_row_number_cell, var_sheet, NULL);
1250 pspp_sheet_view_column_set_fixed_width (column, 50);
1251 g_signal_connect (column, "clicked",
1252 G_CALLBACK (psppire_var_sheet_variables_column_clicked),
1259 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1261 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1262 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1263 PsppireDict *dict = var_sheet->dict;
1264 const struct range_set_node *node;
1265 struct range_set *selected;
1267 selected = pspp_sheet_selection_get_range_set (selection);
1268 for (node = range_set_last (selected); node != NULL;
1269 node = range_set_prev (selected, node))
1273 for (i = 1; i <= range_set_node_get_width (node); i++)
1275 unsigned long row = range_set_node_get_end (node) - i;
1276 if (row < psppire_dict_get_var_cnt (dict))
1277 psppire_dict_delete_variables (dict, row, 1);
1280 range_set_destroy (selected);
1284 on_selection_changed (PsppSheetSelection *selection,
1285 gpointer user_data UNUSED)
1287 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1288 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1289 gint n_selected_rows;
1290 gboolean may_delete;
1294 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1296 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1297 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1298 && n_selected_rows > 0));
1300 switch (n_selected_rows)
1307 /* The row used for inserting new variables cannot be deleted. */
1308 path = gtk_tree_path_new_from_indices (
1309 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1310 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1311 gtk_tree_path_free (path);
1318 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1319 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1323 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1325 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1326 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1327 PsppireDict *dict = var_sheet->dict;
1328 struct range_set *selected;
1331 selected = pspp_sheet_selection_get_range_set (selection);
1332 row = range_set_scan (selected, 0);
1333 range_set_destroy (selected);
1335 if (row <= psppire_dict_get_var_cnt (dict))
1338 if (psppire_dict_generate_name (dict, name, sizeof name))
1339 psppire_dict_insert_variable (dict, row, name);
1344 psppire_var_sheet_init (PsppireVarSheet *obj)
1346 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1347 PsppSheetViewColumn *column;
1352 obj->format_use = FMT_FOR_OUTPUT;
1353 obj->may_create_vars = TRUE;
1354 obj->may_delete_vars = TRUE;
1356 obj->scroll_to_bottom_signal = 0;
1358 obj->container = NULL;
1359 obj->dispose_has_run = FALSE;
1362 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1364 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1365 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1366 g_signal_connect (list->data, "editing-started",
1367 G_CALLBACK (on_name_column_editing_started), NULL);
1370 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1371 add_popup_menu (obj, column, on_type_click);
1373 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1375 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1377 add_text_column (obj, VS_LABEL, _("Label"), 20);
1379 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1380 add_popup_menu (obj, column, on_value_labels_click);
1382 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1383 add_popup_menu (obj, column, on_missing_values_click);
1385 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1387 add_combo_column (obj, VS_ALIGN, _("Align"), 8, alignment_to_stock_id,
1388 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1389 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1390 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1393 add_combo_column (obj, VS_MEASURE, _("Measure"), 11, measure_to_stock_id,
1394 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1395 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1396 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1399 add_combo_column (obj, VS_ROLE, _("Role"), 11, role_to_stock_id,
1400 var_role_to_string (ROLE_INPUT), ROLE_INPUT,
1401 var_role_to_string (ROLE_TARGET), ROLE_TARGET,
1402 var_role_to_string (ROLE_BOTH), ROLE_BOTH,
1403 var_role_to_string (ROLE_NONE), ROLE_NONE,
1404 var_role_to_string (ROLE_PARTITION), ROLE_PARTITION,
1405 var_role_to_string (ROLE_SPLIT), ROLE_SPLIT,
1408 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1409 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1410 PSPP_SHEET_SELECTION_MULTIPLE);
1412 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1413 g_signal_connect (obj, "query-tooltip",
1414 G_CALLBACK (on_query_var_tooltip), NULL);
1415 g_signal_connect (obj, "button-press-event",
1416 G_CALLBACK (on_button_pressed), NULL);
1417 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1419 obj->builder = builder_new ("var-sheet.ui");
1421 action = get_action_assert (obj->builder, "edit_clear-variables");
1422 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1424 gtk_action_set_sensitive (action, FALSE);
1425 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1426 "changed", G_CALLBACK (on_selection_changed), NULL);
1428 action = get_action_assert (obj->builder, "edit_insert-variable");
1429 gtk_action_set_sensitive (action, FALSE);
1430 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1435 psppire_var_sheet_new (void)
1437 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1441 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1443 return var_sheet->dict;
1447 refresh_model (PsppireVarSheet *var_sheet)
1449 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1451 if (var_sheet->dict != NULL)
1453 PsppireEmptyListStore *store;
1456 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1457 + var_sheet->may_create_vars);
1458 store = psppire_empty_list_store_new (n_rows);
1459 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1460 GTK_TREE_MODEL (store));
1461 g_object_unref (store);
1466 on_var_changed (PsppireDict *dict, glong row,
1467 guint what, const struct variable *oldvar,
1468 PsppireVarSheet *var_sheet)
1470 PsppireEmptyListStore *store;
1472 g_return_if_fail (dict == var_sheet->dict);
1474 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1475 PSPP_SHEET_VIEW (var_sheet)));
1476 g_return_if_fail (store != NULL);
1478 psppire_empty_list_store_row_changed (store, row);
1482 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1484 PsppireEmptyListStore *store;
1487 g_return_if_fail (dict == var_sheet->dict);
1489 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1490 PSPP_SHEET_VIEW (var_sheet)));
1491 g_return_if_fail (store != NULL);
1493 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1494 + var_sheet->may_create_vars);
1495 psppire_empty_list_store_set_n_rows (store, n_rows);
1496 psppire_empty_list_store_row_inserted (store, row);
1500 on_var_deleted (PsppireDict *dict,
1501 const struct variable *var, int dict_idx, int case_idx,
1502 PsppireVarSheet *var_sheet)
1504 PsppireEmptyListStore *store;
1507 g_return_if_fail (dict == var_sheet->dict);
1509 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1510 PSPP_SHEET_VIEW (var_sheet)));
1511 g_return_if_fail (store != NULL);
1513 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1514 + var_sheet->may_create_vars);
1515 psppire_empty_list_store_set_n_rows (store, n_rows);
1516 psppire_empty_list_store_row_deleted (store, dict_idx);
1520 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1522 g_return_if_fail (dict == var_sheet->dict);
1523 refresh_model (var_sheet);
1527 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1530 if (var_sheet->dict != NULL)
1534 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1536 if (var_sheet->dict_signals[i])
1537 g_signal_handler_disconnect (var_sheet->dict,
1538 var_sheet->dict_signals[i]);
1540 var_sheet->dict_signals[i] = 0;
1543 g_object_unref (var_sheet->dict);
1546 var_sheet->dict = dict;
1550 g_object_ref (dict);
1552 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1553 = g_signal_connect (dict, "backend-changed",
1554 G_CALLBACK (on_backend_changed), var_sheet);
1556 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED]
1557 = g_signal_connect (dict, "variable-changed",
1558 G_CALLBACK (on_var_changed), var_sheet);
1560 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1561 = g_signal_connect (dict, "variable-inserted",
1562 G_CALLBACK (on_var_inserted), var_sheet);
1564 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1565 = g_signal_connect (dict, "variable-deleted",
1566 G_CALLBACK (on_var_deleted), var_sheet);
1569 refresh_model (var_sheet);
1573 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1575 return var_sheet->may_create_vars;
1579 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1580 gboolean may_create_vars)
1582 if (var_sheet->may_create_vars != may_create_vars)
1584 PsppireEmptyListStore *store;
1587 var_sheet->may_create_vars = may_create_vars;
1589 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1590 PSPP_SHEET_VIEW (var_sheet)));
1591 g_return_if_fail (store != NULL);
1593 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1594 + var_sheet->may_create_vars);
1595 psppire_empty_list_store_set_n_rows (store, n_rows);
1597 if (may_create_vars)
1598 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1600 psppire_empty_list_store_row_deleted (store, n_rows);
1602 on_selection_changed (pspp_sheet_view_get_selection (
1603 PSPP_SHEET_VIEW (var_sheet)), NULL);
1608 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1610 return var_sheet->may_delete_vars;
1614 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1615 gboolean may_delete_vars)
1617 if (var_sheet->may_delete_vars != may_delete_vars)
1619 var_sheet->may_delete_vars = may_delete_vars;
1620 on_selection_changed (pspp_sheet_view_get_selection (
1621 PSPP_SHEET_VIEW (var_sheet)), NULL);
1626 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1628 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1631 path = gtk_tree_path_new_from_indices (dict_index, -1);
1632 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1633 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1634 gtk_tree_path_free (path);
1638 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1640 if (var_sheet->uim == NULL)
1642 var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1644 GTK_TYPE_UI_MANAGER));
1645 g_object_ref (var_sheet->uim);
1648 return var_sheet->uim;