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_OUTPUT)))
290 var_set_role (var, ROLE_OUTPUT);
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)
325 return GTK_STOCK_JUSTIFY_LEFT;
328 return GTK_STOCK_JUSTIFY_CENTER;
331 return GTK_STOCK_JUSTIFY_RIGHT;
334 g_return_val_if_reached ("");
339 get_var_role_stock_id (enum var_role role)
343 case ROLE_INPUT: return "variable-role-input";
344 case ROLE_OUTPUT: return "variable-role-target";
345 case ROLE_BOTH: return "variable-role-both";
346 case ROLE_NONE: return "variable-role-none";
347 case ROLE_PARTITION: return "variable-role-partition";
348 case ROLE_SPLIT: return "variable-role-split";
350 g_return_val_if_reached ("");
355 render_var_cell (PsppSheetViewColumn *tree_column,
356 GtkCellRenderer *cell,
361 PsppireVarSheet *var_sheet = user_data;
362 const struct fmt_spec *print;
363 enum vs_column column_id;
364 struct variable *var;
367 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
368 "column-number")) - 1;
369 row = GPOINTER_TO_INT (iter->user_data);
371 if (row >= psppire_dict_get_var_cnt (var_sheet->dict))
373 if (GTK_IS_CELL_RENDERER_TEXT (cell))
377 "editable", column_id == VS_NAME,
379 if (column_id == VS_WIDTH
380 || column_id == VS_DECIMALS
381 || column_id == VS_COLUMNS)
382 g_object_set (cell, "adjustment", NULL, NULL);
385 g_object_set (cell, "stock-id", "", NULL);
389 var = psppire_dict_get_variable (var_sheet->dict, row);
391 print = var_get_print_format (var);
396 "text", var_get_name (var),
403 "text", fmt_gui_name (print->type),
409 set_spin_cell (cell, print->w,
410 fmt_min_width (print->type, var_sheet->format_use),
411 fmt_max_width (print->type, var_sheet->format_use),
412 fmt_step_width (print->type));
416 if (fmt_takes_decimals (print->type))
418 int max_w = fmt_max_width (print->type, var_sheet->format_use);
419 int max_d = fmt_max_decimals (print->type, max_w,
420 var_sheet->format_use);
421 set_spin_cell (cell, print->d, 0, max_d, 1);
433 "text", var_has_label (var) ? var_get_label (var) : "",
439 g_object_set (cell, "editable", FALSE, NULL);
440 if ( ! var_has_value_labels (var))
441 g_object_set (cell, "text", _("None"), NULL);
444 const struct val_labs *vls = var_get_value_labels (var);
445 const struct val_lab **labels = val_labs_sorted (vls);
446 const struct val_lab *vl = labels[0];
447 gchar *vstr = value_to_text (vl->value, var);
448 char *text = xasprintf (_("{%s, %s}..."), vstr,
449 val_lab_get_escaped_label (vl));
452 g_object_set (cell, "text", text, NULL);
460 char *text = missing_values_to_string (var_sheet->dict, var, NULL);
470 set_spin_cell (cell, var_get_display_width (var), 1, 2 * MAX_STRING, 1);
474 if (GTK_IS_CELL_RENDERER_TEXT (cell))
476 "text", alignment_to_string (var_get_alignment (var)),
480 g_object_set (cell, "stock-id",
481 get_var_align_stock_id (var_get_alignment (var)), NULL);
485 if (GTK_IS_CELL_RENDERER_TEXT (cell))
487 "text", measure_to_string (var_get_measure (var)),
492 enum fmt_type type = var_get_print_format (var)->type;
493 enum measure measure = var_get_measure (var);
495 g_object_set (cell, "stock-id",
496 get_var_measurement_stock_id (type, measure),
502 if (GTK_IS_CELL_RENDERER_TEXT (cell))
504 "text", var_role_to_string (var_get_role (var)),
508 g_object_set (cell, "stock-id",
509 get_var_role_stock_id (var_get_role (var)), NULL);
514 static struct variable *
515 path_string_to_variable (PsppireVarSheet *var_sheet, gchar *path_string)
521 path = gtk_tree_path_new_from_string (path_string);
522 row = gtk_tree_path_get_indices (path)[0];
523 gtk_tree_path_free (path);
525 dict = psppire_var_sheet_get_dictionary (var_sheet);
526 g_return_val_if_fail (dict != NULL, NULL);
528 return psppire_dict_get_variable (dict, row);
532 on_type_click (PsppireCellRendererButton *cell,
534 PsppireVarSheet *var_sheet)
536 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
537 struct fmt_spec format;
538 struct variable *var;
540 var = path_string_to_variable (var_sheet, path);
541 g_return_if_fail (var != NULL);
543 format = *var_get_print_format (var);
544 psppire_var_type_dialog_run (GTK_WINDOW (toplevel), &format);
546 var_set_width_and_formats (var, fmt_var_width (&format), &format, &format);
550 on_value_labels_click (PsppireCellRendererButton *cell,
552 PsppireVarSheet *var_sheet)
554 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
555 struct val_labs *labels;
556 struct variable *var;
558 var = path_string_to_variable (var_sheet, path);
559 g_return_if_fail (var != NULL);
561 labels = psppire_val_labs_dialog_run (GTK_WINDOW (toplevel), var);
564 var_set_value_labels (var, labels);
565 val_labs_destroy (labels);
570 on_missing_values_click (PsppireCellRendererButton *cell,
572 PsppireVarSheet *var_sheet)
574 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
575 struct missing_values mv;
576 struct variable *var;
578 var = path_string_to_variable (var_sheet, path);
579 g_return_if_fail (var != NULL);
581 psppire_missing_val_dialog_run (GTK_WINDOW (toplevel), var, &mv);
582 var_set_missing_values (var, &mv);
587 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
591 g_object_set (G_OBJECT (renderer),
592 PSPPIRE_IS_CELL_RENDERER_BUTTON (renderer) ? "label" : "text",
593 string, (void *) NULL);
594 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
595 NULL, NULL, NULL, &width, NULL);
600 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
607 ds_put_byte_multiple (&s, '0', char_cnt);
608 ds_put_byte (&s, ' ');
609 width = get_string_width (treeview, renderer, ds_cstr (&s));
615 static PsppSheetViewColumn *
616 add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer,
617 enum vs_column column_id,
618 const char *title, int width)
620 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
621 int title_width, content_width;
622 PsppSheetViewColumn *column;
624 column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL);
625 g_object_set_data (G_OBJECT (column), "column-number",
626 GINT_TO_POINTER (column_id) + 1);
628 pspp_sheet_view_column_set_cell_data_func (
629 column, renderer, render_var_cell, var_sheet, NULL);
631 title_width = get_string_width (sheet_view, renderer, title);
632 content_width = get_monospace_width (sheet_view, renderer, width);
633 g_object_set_data (G_OBJECT (column), "content-width",
634 GINT_TO_POINTER (content_width));
636 pspp_sheet_view_column_set_fixed_width (column,
637 MAX (title_width, content_width));
638 pspp_sheet_view_column_set_resizable (column, TRUE);
640 pspp_sheet_view_append_column (sheet_view, column);
642 g_signal_connect (renderer, "edited",
643 G_CALLBACK (on_var_column_edited),
645 g_object_set_data (G_OBJECT (renderer), "column-id",
646 GINT_TO_POINTER (column_id));
647 g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet);
652 static PsppSheetViewColumn *
653 add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
654 const char *title, int width)
656 return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (),
657 column_id, title, width);
660 static PsppSheetViewColumn *
661 add_spin_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
662 const char *title, int width)
664 return add_var_sheet_column (var_sheet, gtk_cell_renderer_spin_new (),
665 column_id, title, width);
669 measure_to_stock_id (enum fmt_type type, int measure)
671 return get_var_measurement_stock_id (type, measure);
675 alignment_to_stock_id (enum fmt_type type, int alignment)
677 return get_var_align_stock_id (alignment);
681 role_to_stock_id (enum fmt_type type, int role)
683 return get_var_role_stock_id (role);
687 render_var_pixbuf (GtkCellLayout *cell_layout,
688 GtkCellRenderer *cell,
689 GtkTreeModel *tree_model,
693 const char *(*value_to_stock_id) (enum fmt_type, int value);
694 enum fmt_type type = GPOINTER_TO_INT (data);
697 value_to_stock_id = g_object_get_data (G_OBJECT (cell), "value-to-stock-id");
699 gtk_tree_model_get (tree_model, iter, 0, &value, -1);
700 g_object_set (cell, "stock-id", value_to_stock_id (type, value), NULL);
704 on_combo_editing_started (GtkCellRenderer *renderer,
705 GtkCellEditable *editable,
709 PsppireVarSheet *var_sheet = user_data;
711 if (GTK_IS_COMBO_BOX (editable))
713 struct variable *var = path_string_to_variable (var_sheet, path_string);
714 const struct fmt_spec *format = var_get_print_format (var);
715 const char *(*value_to_stock_id) (enum fmt_type, int value);
716 GtkCellRenderer *cell;
718 value_to_stock_id = g_object_get_data (G_OBJECT (renderer),
719 "value-to-stock-id");
721 cell = gtk_cell_renderer_pixbuf_new ();
722 g_object_set (cell, "width", 16, "height", 16, NULL);
723 g_object_set_data (G_OBJECT (cell),
724 "value-to-stock-id", value_to_stock_id);
725 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (editable), cell, FALSE);
726 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (editable), cell,
728 GINT_TO_POINTER (format->type),
734 add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
735 const char *title, int width,
736 const char *(*value_to_stock_id) (enum fmt_type, int value),
739 PsppSheetViewColumn *column;
740 GtkCellRenderer *cell;
745 store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
746 va_start (args, value_to_stock_id);
747 while ((name = va_arg (args, const char *)) != NULL)
749 int value = va_arg (args, int);
750 gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
757 cell = gtk_cell_renderer_combo_new ();
760 "model", GTK_TREE_MODEL (store),
763 if (value_to_stock_id != NULL)
765 g_object_set_data (G_OBJECT (cell),
766 "value-to-stock-id", value_to_stock_id);
767 g_signal_connect (cell, "editing-started",
768 G_CALLBACK (on_combo_editing_started),
772 column = add_var_sheet_column (var_sheet, cell, column_id, title, width);
774 cell = gtk_cell_renderer_pixbuf_new ();
775 g_object_set (cell, "width", 16, "height", 16, NULL);
776 pspp_sheet_view_column_pack_end (column, cell, FALSE);
777 pspp_sheet_view_column_set_cell_data_func (
778 column, cell, render_var_cell, var_sheet, NULL);
782 add_popup_menu (PsppireVarSheet *var_sheet,
783 PsppSheetViewColumn *column,
784 void (*on_click) (PsppireCellRendererButton *,
786 PsppireVarSheet *var_sheet))
788 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
789 const char *button_label = "...";
790 GtkCellRenderer *button_renderer;
793 button_renderer = psppire_cell_renderer_button_new ();
794 g_object_set (button_renderer,
795 "label", button_label,
798 g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click),
800 pspp_sheet_view_column_pack_start (column, button_renderer, FALSE);
801 pspp_sheet_view_column_set_cell_data_func (
802 column, button_renderer, render_popup_cell, var_sheet, NULL);
804 content_width = GPOINTER_TO_INT (g_object_get_data (
805 G_OBJECT (column), "content-width"));
806 content_width += get_string_width (sheet_view, button_renderer,
808 if (content_width > pspp_sheet_view_column_get_fixed_width (column))
809 pspp_sheet_view_column_set_fixed_width (column, content_width);
813 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
814 gint wx, gint wy, size_t *row, size_t *column)
816 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
820 PsppSheetViewColumn *tree_column;
821 GtkTreeModel *tree_model;
825 /* Check that WIDGET is really visible on the screen before we
826 do anything else. This is a bug fix for a sticky situation:
827 when text_data_import_assistant() returns, it frees the data
828 necessary to compose the tool tip message, but there may be
829 a tool tip under preparation at that point (even if there is
830 no visible tool tip) that will call back into us a little
831 bit later. Perhaps the correct solution to this problem is
832 to make the data related to the tool tips part of a GObject
833 that only gets destroyed when all references are released,
834 but this solution appears to be effective too. */
835 if (!gtk_widget_get_mapped (widget))
838 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
840 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
841 &path, &tree_column, NULL, NULL))
844 column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number");
845 if (column_ptr == NULL)
847 *column = GPOINTER_TO_INT (column_ptr) - 1;
849 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
852 tree_model = pspp_sheet_view_get_model (tree_view);
853 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
854 gtk_tree_path_free (path);
858 *row = GPOINTER_TO_INT (iter.user_data);
863 on_query_var_tooltip (GtkWidget *widget, gint wx, gint wy,
864 gboolean keyboard_mode UNUSED,
865 GtkTooltip *tooltip, gpointer *user_data UNUSED)
867 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
869 struct variable *var;
872 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
875 dict = psppire_var_sheet_get_dictionary (var_sheet);
876 g_return_val_if_fail (dict != NULL, FALSE);
878 if (row >= psppire_dict_get_var_cnt (dict))
880 gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a "
885 var = psppire_dict_get_variable (dict, row);
886 g_return_val_if_fail (var != NULL, FALSE);
892 char text[FMT_STRING_LEN_MAX + 1];
894 fmt_to_string (var_get_print_format (var), text);
895 gtk_tooltip_set_text (tooltip, text);
900 if (var_has_value_labels (var))
902 const struct val_labs *vls = var_get_value_labels (var);
903 const struct val_lab **labels = val_labs_sorted (vls);
908 for (i = 0; i < val_labs_count (vls); i++)
910 const struct val_lab *vl = labels[i];
913 if (i >= 10 || ds_length (&s) > 500)
915 ds_put_cstr (&s, "...");
919 vstr = value_to_text (vl->value, var);
920 ds_put_format (&s, _("{%s, %s}\n"), vstr,
921 val_lab_get_escaped_label (vl));
925 ds_chomp_byte (&s, '\n');
927 gtk_tooltip_set_text (tooltip, ds_cstr (&s));
939 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
941 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
944 menu = get_widget_assert (var_sheet->builder, "varsheet-variable-popup");
945 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
949 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
951 do_popup_menu (widget, 0, gtk_get_current_event_time ());
955 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
956 gpointer user_data UNUSED)
958 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
960 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
962 PsppSheetSelection *selection;
964 selection = pspp_sheet_view_get_selection (sheet_view);
965 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
969 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
970 &path, NULL, NULL, NULL))
972 pspp_sheet_selection_unselect_all (selection);
973 pspp_sheet_selection_select_path (selection, path);
974 gtk_tree_path_free (path);
978 do_popup_menu (widget, event->button, event->time);
986 psppire_fmt_use_get_type (void)
988 static GType etype = 0;
991 static const GEnumValue values[] =
993 { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
994 { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
998 etype = g_enum_register_static
999 (g_intern_static_string ("PsppireFmtUse"), values);
1008 PROP_MAY_CREATE_VARS,
1009 PROP_MAY_DELETE_VARS,
1015 psppire_var_sheet_set_property (GObject *object,
1017 const GValue *value,
1020 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
1024 case PROP_DICTIONARY:
1025 psppire_var_sheet_set_dictionary (obj,
1026 PSPPIRE_DICT (g_value_get_object (
1030 case PROP_MAY_CREATE_VARS:
1031 psppire_var_sheet_set_may_create_vars (obj,
1032 g_value_get_boolean (value));
1035 case PROP_MAY_DELETE_VARS:
1036 psppire_var_sheet_set_may_delete_vars (obj,
1037 g_value_get_boolean (value));
1040 case PROP_FORMAT_TYPE:
1041 obj->format_use = g_value_get_enum (value);
1044 case PROP_UI_MANAGER:
1046 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1052 psppire_var_sheet_get_property (GObject *object,
1057 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
1061 case PROP_DICTIONARY:
1062 g_value_set_object (value,
1063 G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
1066 case PROP_MAY_CREATE_VARS:
1067 g_value_set_boolean (value, obj->may_create_vars);
1070 case PROP_MAY_DELETE_VARS:
1071 g_value_set_boolean (value, obj->may_delete_vars);
1074 case PROP_FORMAT_TYPE:
1075 g_value_set_enum (value, obj->format_use);
1078 case PROP_UI_MANAGER:
1079 g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj));
1083 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1089 psppire_var_sheet_dispose (GObject *obj)
1091 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
1094 if (var_sheet->dispose_has_run)
1097 var_sheet->dispose_has_run = TRUE;
1099 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1100 if ( var_sheet->dict_signals[i])
1101 g_signal_handler_disconnect (var_sheet->dict,
1102 var_sheet->dict_signals[i]);
1104 if (var_sheet->dict)
1105 g_object_unref (var_sheet->dict);
1108 g_object_unref (var_sheet->uim);
1110 /* These dialogs are not GObjects (although they should be!)
1111 But for now, unreffing them only causes a GCritical Error
1112 so comment them out for now. (and accept the memory leakage)
1114 g_object_unref (var_sheet->val_labs_dialog);
1115 g_object_unref (var_sheet->missing_val_dialog);
1116 g_object_unref (var_sheet->var_type_dialog);
1119 G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
1123 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
1125 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1128 gobject_class->set_property = psppire_var_sheet_set_property;
1129 gobject_class->get_property = psppire_var_sheet_get_property;
1130 gobject_class->dispose = psppire_var_sheet_dispose;
1132 g_signal_new ("var-double-clicked",
1133 G_OBJECT_CLASS_TYPE (gobject_class),
1136 g_signal_accumulator_true_handled, NULL,
1137 psppire_marshal_BOOLEAN__INT,
1138 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1140 pspec = g_param_spec_object ("dictionary",
1141 "Dictionary displayed by the sheet",
1142 "The PsppireDict that the sheet displays "
1143 "may allow the user to edit",
1146 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
1148 pspec = g_param_spec_boolean ("may-create-vars",
1149 "May create variables",
1150 "Whether the user may create more variables",
1153 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
1155 pspec = g_param_spec_boolean ("may-delete-vars",
1156 "May delete variables",
1157 "Whether the user may delete variables",
1160 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
1162 pspec = g_param_spec_enum ("format-use",
1163 "Use of variable format",
1164 ("Whether variables have input or output "
1166 PSPPIRE_TYPE_FMT_USE,
1169 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
1171 pspec = g_param_spec_object ("ui-manager",
1173 "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.",
1174 GTK_TYPE_UI_MANAGER,
1176 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1180 render_row_number_cell (PsppSheetViewColumn *tree_column,
1181 GtkCellRenderer *cell,
1182 GtkTreeModel *model,
1186 PsppireVarSheet *var_sheet = user_data;
1187 GValue gvalue = { 0, };
1190 row = GPOINTER_TO_INT (iter->user_data);
1192 g_value_init (&gvalue, G_TYPE_INT);
1193 g_value_set_int (&gvalue, row + 1);
1194 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1195 g_value_unset (&gvalue);
1197 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1198 g_object_set (cell, "editable", TRUE, NULL);
1200 g_object_set (cell, "editable", FALSE, NULL);
1204 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1206 PsppireVarSheet *var_sheet)
1210 g_return_if_fail (var_sheet->dict != NULL);
1212 path = gtk_tree_path_new_from_string (path_string);
1213 if (gtk_tree_path_get_depth (path) == 1)
1215 gint *indices = gtk_tree_path_get_indices (path);
1216 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1219 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1220 indices[0], &handled);
1223 gtk_tree_path_free (path);
1227 psppire_var_sheet_variables_column_clicked (PsppSheetViewColumn *column,
1228 PsppireVarSheet *var_sheet)
1230 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1231 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1233 pspp_sheet_selection_select_all (selection);
1236 static PsppSheetViewColumn *
1237 make_row_number_column (PsppireVarSheet *var_sheet)
1239 PsppSheetViewColumn *column;
1240 GtkCellRenderer *renderer;
1242 renderer = psppire_cell_renderer_button_new ();
1243 g_object_set (renderer, "xalign", 1.0, NULL);
1244 g_signal_connect (renderer, "double-clicked",
1245 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1248 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1250 pspp_sheet_view_column_set_clickable (column, TRUE);
1251 pspp_sheet_view_column_set_cell_data_func (
1252 column, renderer, render_row_number_cell, var_sheet, NULL);
1253 pspp_sheet_view_column_set_fixed_width (column, 50);
1254 g_signal_connect (column, "clicked",
1255 G_CALLBACK (psppire_var_sheet_variables_column_clicked),
1262 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1264 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1265 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1266 PsppireDict *dict = var_sheet->dict;
1267 const struct range_set_node *node;
1268 struct range_set *selected;
1270 selected = pspp_sheet_selection_get_range_set (selection);
1271 for (node = range_set_last (selected); node != NULL;
1272 node = range_set_prev (selected, node))
1276 for (i = 1; i <= range_set_node_get_width (node); i++)
1278 unsigned long row = range_set_node_get_end (node) - i;
1279 if (row >= 0 && row < psppire_dict_get_var_cnt (dict))
1280 psppire_dict_delete_variables (dict, row, 1);
1283 range_set_destroy (selected);
1287 on_selection_changed (PsppSheetSelection *selection,
1288 gpointer user_data UNUSED)
1290 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1291 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1292 gint n_selected_rows;
1293 gboolean may_delete;
1297 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1299 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1300 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1301 && n_selected_rows > 0));
1303 switch (n_selected_rows)
1310 /* The row used for inserting new variables cannot be deleted. */
1311 path = gtk_tree_path_new_from_indices (
1312 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1313 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1314 gtk_tree_path_free (path);
1321 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1322 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1326 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1328 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1329 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1330 PsppireDict *dict = var_sheet->dict;
1331 struct range_set *selected;
1334 selected = pspp_sheet_selection_get_range_set (selection);
1335 row = range_set_scan (selected, 0);
1336 range_set_destroy (selected);
1338 if (row <= psppire_dict_get_var_cnt (dict))
1341 if (psppire_dict_generate_name (dict, name, sizeof name))
1342 psppire_dict_insert_variable (dict, row, name);
1347 psppire_var_sheet_init (PsppireVarSheet *obj)
1349 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1350 PsppSheetViewColumn *column;
1355 obj->format_use = FMT_FOR_OUTPUT;
1356 obj->may_create_vars = TRUE;
1357 obj->may_delete_vars = TRUE;
1359 obj->scroll_to_bottom_signal = 0;
1361 obj->container = NULL;
1362 obj->dispose_has_run = FALSE;
1365 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1367 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1368 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1369 g_signal_connect (list->data, "editing-started",
1370 G_CALLBACK (on_name_column_editing_started), NULL);
1373 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1374 add_popup_menu (obj, column, on_type_click);
1376 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1378 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1380 add_text_column (obj, VS_LABEL, _("Label"), 20);
1382 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1383 add_popup_menu (obj, column, on_value_labels_click);
1385 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1386 add_popup_menu (obj, column, on_missing_values_click);
1388 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1390 add_combo_column (obj, VS_ALIGN, _("Align"), 8, alignment_to_stock_id,
1391 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1392 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1393 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1396 add_combo_column (obj, VS_MEASURE, _("Measure"), 12, measure_to_stock_id,
1397 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1398 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1399 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1402 add_combo_column (obj, VS_ROLE, _("Role"), 12, role_to_stock_id,
1403 var_role_to_string (ROLE_INPUT), ROLE_INPUT,
1404 var_role_to_string (ROLE_OUTPUT), ROLE_OUTPUT,
1405 var_role_to_string (ROLE_BOTH), ROLE_BOTH,
1406 var_role_to_string (ROLE_NONE), ROLE_NONE,
1407 var_role_to_string (ROLE_PARTITION), ROLE_PARTITION,
1408 var_role_to_string (ROLE_SPLIT), ROLE_SPLIT,
1411 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1412 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1413 PSPP_SHEET_SELECTION_MULTIPLE);
1415 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1416 g_signal_connect (obj, "query-tooltip",
1417 G_CALLBACK (on_query_var_tooltip), NULL);
1418 g_signal_connect (obj, "button-press-event",
1419 G_CALLBACK (on_button_pressed), NULL);
1420 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1422 obj->builder = builder_new ("var-sheet.ui");
1424 action = get_action_assert (obj->builder, "edit_clear-variables");
1425 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1427 gtk_action_set_sensitive (action, FALSE);
1428 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1429 "changed", G_CALLBACK (on_selection_changed), NULL);
1431 action = get_action_assert (obj->builder, "edit_insert-variable");
1432 gtk_action_set_sensitive (action, FALSE);
1433 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1438 psppire_var_sheet_new (void)
1440 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1444 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1446 return var_sheet->dict;
1450 refresh_model (PsppireVarSheet *var_sheet)
1452 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1454 if (var_sheet->dict != NULL)
1456 PsppireEmptyListStore *store;
1459 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1460 + var_sheet->may_create_vars);
1461 store = psppire_empty_list_store_new (n_rows);
1462 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1463 GTK_TREE_MODEL (store));
1464 g_object_unref (store);
1469 on_var_changed (PsppireDict *dict, glong row,
1470 guint what, const struct variable *oldvar,
1471 PsppireVarSheet *var_sheet)
1473 PsppireEmptyListStore *store;
1475 g_return_if_fail (dict == var_sheet->dict);
1477 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1478 PSPP_SHEET_VIEW (var_sheet)));
1479 g_return_if_fail (store != NULL);
1481 psppire_empty_list_store_row_changed (store, row);
1485 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1487 PsppireEmptyListStore *store;
1490 g_return_if_fail (dict == var_sheet->dict);
1492 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1493 PSPP_SHEET_VIEW (var_sheet)));
1494 g_return_if_fail (store != NULL);
1496 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1497 + var_sheet->may_create_vars);
1498 psppire_empty_list_store_set_n_rows (store, n_rows);
1499 psppire_empty_list_store_row_inserted (store, row);
1503 on_var_deleted (PsppireDict *dict,
1504 const struct variable *var, int dict_idx, int case_idx,
1505 PsppireVarSheet *var_sheet)
1507 PsppireEmptyListStore *store;
1510 g_return_if_fail (dict == var_sheet->dict);
1512 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1513 PSPP_SHEET_VIEW (var_sheet)));
1514 g_return_if_fail (store != NULL);
1516 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1517 + var_sheet->may_create_vars);
1518 psppire_empty_list_store_set_n_rows (store, n_rows);
1519 psppire_empty_list_store_row_deleted (store, dict_idx);
1523 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1525 g_return_if_fail (dict == var_sheet->dict);
1526 refresh_model (var_sheet);
1530 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1533 if (var_sheet->dict != NULL)
1537 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1539 if (var_sheet->dict_signals[i])
1540 g_signal_handler_disconnect (var_sheet->dict,
1541 var_sheet->dict_signals[i]);
1543 var_sheet->dict_signals[i] = 0;
1546 g_object_unref (var_sheet->dict);
1549 var_sheet->dict = dict;
1553 g_object_ref (dict);
1555 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1556 = g_signal_connect (dict, "backend-changed",
1557 G_CALLBACK (on_backend_changed), var_sheet);
1559 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED]
1560 = g_signal_connect (dict, "variable-changed",
1561 G_CALLBACK (on_var_changed), var_sheet);
1563 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1564 = g_signal_connect (dict, "variable-inserted",
1565 G_CALLBACK (on_var_inserted), var_sheet);
1567 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1568 = g_signal_connect (dict, "variable-deleted",
1569 G_CALLBACK (on_var_deleted), var_sheet);
1572 refresh_model (var_sheet);
1576 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1578 return var_sheet->may_create_vars;
1582 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1583 gboolean may_create_vars)
1585 if (var_sheet->may_create_vars != may_create_vars)
1587 PsppireEmptyListStore *store;
1590 var_sheet->may_create_vars = may_create_vars;
1592 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1593 PSPP_SHEET_VIEW (var_sheet)));
1594 g_return_if_fail (store != NULL);
1596 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1597 + var_sheet->may_create_vars);
1598 psppire_empty_list_store_set_n_rows (store, n_rows);
1600 if (may_create_vars)
1601 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1603 psppire_empty_list_store_row_deleted (store, n_rows);
1605 on_selection_changed (pspp_sheet_view_get_selection (
1606 PSPP_SHEET_VIEW (var_sheet)), NULL);
1611 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1613 return var_sheet->may_delete_vars;
1617 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1618 gboolean may_delete_vars)
1620 if (var_sheet->may_delete_vars != may_delete_vars)
1622 var_sheet->may_delete_vars = may_delete_vars;
1623 on_selection_changed (pspp_sheet_view_get_selection (
1624 PSPP_SHEET_VIEW (var_sheet)), NULL);
1629 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1631 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1634 path = gtk_tree_path_new_from_indices (dict_index, -1);
1635 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1636 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1637 gtk_tree_path_free (path);
1641 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1643 if (var_sheet->uim == NULL)
1645 var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1647 GTK_TYPE_UI_MANAGER));
1648 g_object_ref (var_sheet->uim);
1651 return var_sheet->uim;