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
60 G_DEFINE_TYPE (PsppireVarSheet, psppire_var_sheet, PSPP_TYPE_SHEET_VIEW);
63 set_spin_cell (GtkCellRenderer *cell, int value, int min, int max, int step)
65 char text[INT_BUFSIZE_BOUND (int)];
66 GtkAdjustment *adjust;
69 adjust = GTK_ADJUSTMENT (gtk_adjustment_new (value, min, max,
74 sprintf (text, "%d", value);
83 error_dialog (GtkWindow *w, gchar *primary_text, gchar *secondary_text)
86 gtk_message_dialog_new (w,
87 GTK_DIALOG_DESTROY_WITH_PARENT,
89 GTK_BUTTONS_CLOSE, "%s", primary_text);
91 g_object_set (dialog, "icon-name", "psppicon", NULL);
93 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
94 "%s", secondary_text);
96 gtk_dialog_run (GTK_DIALOG (dialog));
98 gtk_widget_destroy (dialog);
102 on_name_column_editing_started (GtkCellRenderer *cell,
103 GtkCellEditable *editable,
107 PsppireVarSheet *var_sheet = g_object_get_data (G_OBJECT (cell), "var-sheet");
108 PsppireDict *dict = psppire_var_sheet_get_dictionary (var_sheet);
110 g_return_if_fail (var_sheet);
111 g_return_if_fail (dict);
113 if (GTK_IS_ENTRY (editable))
115 GtkEntry *entry = GTK_ENTRY (editable);
116 if (gtk_entry_get_text (entry)[0] == '\0')
119 if (psppire_dict_generate_name (dict, name, sizeof name))
120 gtk_entry_set_text (entry, name);
126 scroll_to_bottom (GtkWidget *widget,
127 GtkRequisition *requisition,
128 gpointer unused UNUSED)
130 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
131 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
132 GtkAdjustment *vadjust;
134 vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
135 gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
137 if (var_sheet->scroll_to_bottom_signal)
139 g_signal_handler_disconnect (var_sheet,
140 var_sheet->scroll_to_bottom_signal);
141 var_sheet->scroll_to_bottom_signal = 0;
146 on_var_column_edited (GtkCellRendererText *cell,
151 PsppireVarSheet *var_sheet = user_data;
152 GtkWindow *window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (var_sheet)));
153 struct dictionary *dict = var_sheet->dict->dict;
154 enum vs_column column_id;
155 struct variable *var;
160 path = gtk_tree_path_new_from_string (path_string);
161 row = gtk_tree_path_get_indices (path)[0];
162 gtk_tree_path_free (path);
164 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell),
167 var = psppire_dict_get_variable (var_sheet->dict, row);
170 g_return_if_fail (column_id == VS_NAME);
172 if (!dict_id_is_valid (dict, new_text, false))
173 error_dialog (window,
174 g_strdup (_("Cannot create variable.")),
175 g_strdup_printf (_("\"%s\" is not a valid variable "
176 "name."), new_text));
177 else if (dict_lookup_var (dict, new_text) != NULL)
178 error_dialog (window,
179 g_strdup (_("Cannot create variable.")),
180 g_strdup_printf (_("This dictionary already contains "
181 "a variable named \"%s\"."),
185 dict_create_var (var_sheet->dict->dict, new_text, 0);
186 if (!var_sheet->scroll_to_bottom_signal)
188 gtk_widget_queue_resize (GTK_WIDGET (var_sheet));
189 var_sheet->scroll_to_bottom_signal =
190 g_signal_connect (var_sheet, "size-request",
191 G_CALLBACK (scroll_to_bottom), NULL);
201 if (!dict_id_is_valid (dict, new_text, false))
202 error_dialog (window,
203 g_strdup (_("Cannot rename variable.")),
204 g_strdup_printf (_("\"%s\" is not a valid variable "
205 "name."), new_text));
206 else if (dict_lookup_var (dict, new_text) != NULL
207 && dict_lookup_var (dict, new_text) != var)
208 error_dialog (window,
209 g_strdup (_("Cannot rename variable.")),
210 g_strdup_printf (_("This dictionary already contains "
211 "a variable named \"%s\"."),
214 dict_rename_var (dict, var, new_text);
222 width = atoi (new_text);
225 struct fmt_spec format;
227 format = *var_get_print_format (var);
228 fmt_change_width (&format, width, var_sheet->format_use);
229 var_set_width (var, fmt_var_width (&format));
230 var_set_both_formats (var, &format);
235 decimals = atoi (new_text);
238 struct fmt_spec format;
240 format = *var_get_print_format (var);
241 fmt_change_decimals (&format, decimals, var_sheet->format_use);
242 var_set_print_format (var, &format);
247 var_set_label (var, new_text, false);
255 width = atoi (new_text);
256 if (width > 0 && width < 2 * MAX_STRING)
257 var_set_display_width (var, width);
261 if (!strcmp (new_text, alignment_to_string (ALIGN_LEFT)))
262 var_set_alignment (var, ALIGN_LEFT);
263 else if (!strcmp (new_text, alignment_to_string (ALIGN_CENTRE)))
264 var_set_alignment (var, ALIGN_CENTRE);
265 else if (!strcmp (new_text, alignment_to_string (ALIGN_RIGHT)))
266 var_set_alignment (var, ALIGN_RIGHT);
270 if (!strcmp (new_text, measure_to_string (MEASURE_NOMINAL)))
271 var_set_measure (var, MEASURE_NOMINAL);
272 else if (!strcmp (new_text, measure_to_string (MEASURE_ORDINAL)))
273 var_set_measure (var, MEASURE_ORDINAL);
274 else if (!strcmp (new_text, measure_to_string (MEASURE_SCALE)))
275 var_set_measure (var, MEASURE_SCALE);
281 render_popup_cell (PsppSheetViewColumn *tree_column,
282 GtkCellRenderer *cell,
287 PsppireVarSheet *var_sheet = user_data;
290 row = GPOINTER_TO_INT (iter->user_data);
292 "editable", row < psppire_dict_get_var_cnt (var_sheet->dict),
297 get_var_align_stock_id (const struct variable *var)
299 switch (var_get_alignment (var))
302 return GTK_STOCK_JUSTIFY_LEFT;
305 return GTK_STOCK_JUSTIFY_CENTER;
308 return GTK_STOCK_JUSTIFY_RIGHT;
311 g_return_val_if_reached ("");
316 render_var_cell (PsppSheetViewColumn *tree_column,
317 GtkCellRenderer *cell,
322 PsppireVarSheet *var_sheet = user_data;
323 const struct fmt_spec *print;
324 enum vs_column column_id;
325 struct variable *var;
328 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
329 "column-number")) - 1;
330 row = GPOINTER_TO_INT (iter->user_data);
332 if (row >= psppire_dict_get_var_cnt (var_sheet->dict))
334 if (GTK_IS_CELL_RENDERER_TEXT (cell))
338 "editable", column_id == VS_NAME,
340 if (column_id == VS_WIDTH
341 || column_id == VS_DECIMALS
342 || column_id == VS_COLUMNS)
343 g_object_set (cell, "adjustment", NULL, NULL);
346 g_object_set (cell, "stock-id", "", NULL);
350 var = psppire_dict_get_variable (var_sheet->dict, row);
352 print = var_get_print_format (var);
357 "text", var_get_name (var),
364 "text", fmt_gui_name (print->type),
370 set_spin_cell (cell, print->w,
371 fmt_min_width (print->type, var_sheet->format_use),
372 fmt_max_width (print->type, var_sheet->format_use),
373 fmt_step_width (print->type));
377 if (fmt_takes_decimals (print->type))
379 int max_w = fmt_max_width (print->type, var_sheet->format_use);
380 int max_d = fmt_max_decimals (print->type, max_w,
381 var_sheet->format_use);
382 set_spin_cell (cell, print->d, 0, max_d, 1);
394 "text", var_has_label (var) ? var_get_label (var) : "",
400 g_object_set (cell, "editable", FALSE, NULL);
401 if ( ! var_has_value_labels (var))
402 g_object_set (cell, "text", _("None"), NULL);
405 const struct val_labs *vls = var_get_value_labels (var);
406 const struct val_lab **labels = val_labs_sorted (vls);
407 const struct val_lab *vl = labels[0];
408 gchar *vstr = value_to_text (vl->value, var);
409 char *text = xasprintf (_("{%s, %s}..."), vstr,
410 val_lab_get_escaped_label (vl));
413 g_object_set (cell, "text", text, NULL);
421 char *text = missing_values_to_string (var_sheet->dict, var, NULL);
431 set_spin_cell (cell, var_get_display_width (var), 1, 2 * MAX_STRING, 1);
435 if (GTK_IS_CELL_RENDERER_TEXT (cell))
437 "text", alignment_to_string (var_get_alignment (var)),
441 g_object_set (cell, "stock-id", get_var_align_stock_id (var), NULL);
445 if (GTK_IS_CELL_RENDERER_TEXT (cell))
447 "text", measure_to_string (var_get_measure (var)),
451 g_object_set (cell, "stock-id",
452 psppire_dict_view_get_var_measurement_stock_id (var),
458 static struct variable *
459 path_string_to_variable (PsppireVarSheet *var_sheet, gchar *path_string)
465 path = gtk_tree_path_new_from_string (path_string);
466 row = gtk_tree_path_get_indices (path)[0];
467 gtk_tree_path_free (path);
469 dict = psppire_var_sheet_get_dictionary (var_sheet);
470 g_return_val_if_fail (dict != NULL, NULL);
472 return psppire_dict_get_variable (dict, row);
476 on_type_click (PsppireCellRendererButton *cell,
478 PsppireVarSheet *var_sheet)
480 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
481 struct fmt_spec format;
482 struct variable *var;
484 var = path_string_to_variable (var_sheet, path);
485 g_return_if_fail (var != NULL);
487 format = *var_get_print_format (var);
488 psppire_var_type_dialog_run (GTK_WINDOW (toplevel), &format);
490 var_set_width_and_formats (var, fmt_var_width (&format), &format, &format);
494 on_value_labels_click (PsppireCellRendererButton *cell,
496 PsppireVarSheet *var_sheet)
498 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
499 struct val_labs *labels;
500 struct variable *var;
502 var = path_string_to_variable (var_sheet, path);
503 g_return_if_fail (var != NULL);
505 labels = psppire_val_labs_dialog_run (GTK_WINDOW (toplevel), var);
508 var_set_value_labels (var, labels);
509 val_labs_destroy (labels);
514 on_missing_values_click (PsppireCellRendererButton *cell,
516 PsppireVarSheet *var_sheet)
518 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
519 struct missing_values mv;
520 struct variable *var;
522 var = path_string_to_variable (var_sheet, path);
523 g_return_if_fail (var != NULL);
525 psppire_missing_val_dialog_run (GTK_WINDOW (toplevel), var, &mv);
526 var_set_missing_values (var, &mv);
531 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
535 g_object_set (G_OBJECT (renderer),
536 PSPPIRE_IS_CELL_RENDERER_BUTTON (renderer) ? "label" : "text",
537 string, (void *) NULL);
538 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
539 NULL, NULL, NULL, &width, NULL);
544 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
551 ds_put_byte_multiple (&s, '0', char_cnt);
552 ds_put_byte (&s, ' ');
553 width = get_string_width (treeview, renderer, ds_cstr (&s));
559 static PsppSheetViewColumn *
560 add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer,
561 enum vs_column column_id,
562 const char *title, int width)
564 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
565 int title_width, content_width;
566 PsppSheetViewColumn *column;
568 column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL);
569 g_object_set_data (G_OBJECT (column), "column-number",
570 GINT_TO_POINTER (column_id) + 1);
572 pspp_sheet_view_column_set_cell_data_func (
573 column, renderer, render_var_cell, var_sheet, NULL);
575 title_width = get_string_width (sheet_view, renderer, title);
576 content_width = get_monospace_width (sheet_view, renderer, width);
577 g_object_set_data (G_OBJECT (column), "content-width",
578 GINT_TO_POINTER (content_width));
580 pspp_sheet_view_column_set_fixed_width (column,
581 MAX (title_width, content_width));
582 pspp_sheet_view_column_set_resizable (column, TRUE);
584 pspp_sheet_view_append_column (sheet_view, column);
586 g_signal_connect (renderer, "edited",
587 G_CALLBACK (on_var_column_edited),
589 g_object_set_data (G_OBJECT (renderer), "column-id",
590 GINT_TO_POINTER (column_id));
591 g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet);
596 static PsppSheetViewColumn *
597 add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
598 const char *title, int width)
600 return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (),
601 column_id, title, width);
604 static PsppSheetViewColumn *
605 add_spin_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
606 const char *title, int width)
608 return add_var_sheet_column (var_sheet, gtk_cell_renderer_spin_new (),
609 column_id, title, width);
612 static PsppSheetViewColumn *
613 add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
614 const char *title, int width,
617 GtkCellRenderer *cell;
622 store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
623 va_start (args, width);
624 while ((name = va_arg (args, const char *)) != NULL)
626 int value = va_arg (args, int);
627 gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
634 cell = gtk_cell_renderer_combo_new ();
637 "model", GTK_TREE_MODEL (store),
641 return add_var_sheet_column (var_sheet, cell, column_id, title, width);
646 add_popup_menu (PsppireVarSheet *var_sheet,
647 PsppSheetViewColumn *column,
648 void (*on_click) (PsppireCellRendererButton *,
650 PsppireVarSheet *var_sheet))
652 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
653 const char *button_label = "...";
654 GtkCellRenderer *button_renderer;
657 button_renderer = psppire_cell_renderer_button_new ();
658 g_object_set (button_renderer,
659 "label", button_label,
662 g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click),
664 pspp_sheet_view_column_pack_start (column, button_renderer, FALSE);
665 pspp_sheet_view_column_set_cell_data_func (
666 column, button_renderer, render_popup_cell, var_sheet, NULL);
668 content_width = GPOINTER_TO_INT (g_object_get_data (
669 G_OBJECT (column), "content-width"));
670 content_width += get_string_width (sheet_view, button_renderer,
672 if (content_width > pspp_sheet_view_column_get_fixed_width (column))
673 pspp_sheet_view_column_set_fixed_width (column, content_width);
677 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
678 gint wx, gint wy, size_t *row, size_t *column)
680 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
684 PsppSheetViewColumn *tree_column;
685 GtkTreeModel *tree_model;
689 /* Check that WIDGET is really visible on the screen before we
690 do anything else. This is a bug fix for a sticky situation:
691 when text_data_import_assistant() returns, it frees the data
692 necessary to compose the tool tip message, but there may be
693 a tool tip under preparation at that point (even if there is
694 no visible tool tip) that will call back into us a little
695 bit later. Perhaps the correct solution to this problem is
696 to make the data related to the tool tips part of a GObject
697 that only gets destroyed when all references are released,
698 but this solution appears to be effective too. */
699 if (!gtk_widget_get_mapped (widget))
702 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
704 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
705 &path, &tree_column, NULL, NULL))
708 column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number");
709 if (column_ptr == NULL)
711 *column = GPOINTER_TO_INT (column_ptr) - 1;
713 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
716 tree_model = pspp_sheet_view_get_model (tree_view);
717 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
718 gtk_tree_path_free (path);
722 *row = GPOINTER_TO_INT (iter.user_data);
727 on_query_var_tooltip (GtkWidget *widget, gint wx, gint wy,
728 gboolean keyboard_mode UNUSED,
729 GtkTooltip *tooltip, gpointer *user_data UNUSED)
731 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
733 struct variable *var;
736 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
739 dict = psppire_var_sheet_get_dictionary (var_sheet);
740 g_return_val_if_fail (dict != NULL, FALSE);
742 if (row >= psppire_dict_get_var_cnt (dict))
744 gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a "
749 var = psppire_dict_get_variable (dict, row);
750 g_return_val_if_fail (var != NULL, FALSE);
756 char text[FMT_STRING_LEN_MAX + 1];
758 fmt_to_string (var_get_print_format (var), text);
759 gtk_tooltip_set_text (tooltip, text);
764 if (var_has_value_labels (var))
766 const struct val_labs *vls = var_get_value_labels (var);
767 const struct val_lab **labels = val_labs_sorted (vls);
772 for (i = 0; i < val_labs_count (vls); i++)
774 const struct val_lab *vl = labels[i];
777 if (i >= 10 || ds_length (&s) > 500)
779 ds_put_cstr (&s, "...");
783 vstr = value_to_text (vl->value, var);
784 ds_put_format (&s, _("{%s, %s}\n"), vstr,
785 val_lab_get_escaped_label (vl));
789 ds_chomp_byte (&s, '\n');
791 gtk_tooltip_set_text (tooltip, ds_cstr (&s));
803 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
805 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
808 menu = get_widget_assert (var_sheet->builder, "varsheet-variable-popup");
809 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
813 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
815 do_popup_menu (widget, 0, gtk_get_current_event_time ());
819 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
820 gpointer user_data UNUSED)
822 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
824 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
826 PsppSheetSelection *selection;
828 selection = pspp_sheet_view_get_selection (sheet_view);
829 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
833 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
834 &path, NULL, NULL, NULL))
836 pspp_sheet_selection_unselect_all (selection);
837 pspp_sheet_selection_select_path (selection, path);
838 gtk_tree_path_free (path);
842 do_popup_menu (widget, event->button, event->time);
850 psppire_fmt_use_get_type (void)
852 static GType etype = 0;
855 static const GEnumValue values[] =
857 { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
858 { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
862 etype = g_enum_register_static
863 (g_intern_static_string ("PsppireFmtUse"), values);
872 PROP_MAY_CREATE_VARS,
873 PROP_MAY_DELETE_VARS,
879 psppire_var_sheet_set_property (GObject *object,
884 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
888 case PROP_DICTIONARY:
889 psppire_var_sheet_set_dictionary (obj,
890 PSPPIRE_DICT (g_value_get_object (
894 case PROP_MAY_CREATE_VARS:
895 psppire_var_sheet_set_may_create_vars (obj,
896 g_value_get_boolean (value));
899 case PROP_MAY_DELETE_VARS:
900 psppire_var_sheet_set_may_delete_vars (obj,
901 g_value_get_boolean (value));
904 case PROP_FORMAT_TYPE:
905 obj->format_use = g_value_get_enum (value);
908 case PROP_UI_MANAGER:
910 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
916 psppire_var_sheet_get_property (GObject *object,
921 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
925 case PROP_DICTIONARY:
926 g_value_set_object (value,
927 G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
930 case PROP_MAY_CREATE_VARS:
931 g_value_set_boolean (value, obj->may_create_vars);
934 case PROP_MAY_DELETE_VARS:
935 g_value_set_boolean (value, obj->may_delete_vars);
938 case PROP_FORMAT_TYPE:
939 g_value_set_enum (value, obj->format_use);
942 case PROP_UI_MANAGER:
943 g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj));
947 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
953 psppire_var_sheet_dispose (GObject *obj)
955 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
958 if (var_sheet->dispose_has_run)
961 var_sheet->dispose_has_run = TRUE;
963 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
964 if ( var_sheet->dict_signals[i])
965 g_signal_handler_disconnect (var_sheet->dict,
966 var_sheet->dict_signals[i]);
969 g_object_unref (var_sheet->dict);
972 g_object_unref (var_sheet->uim);
974 /* These dialogs are not GObjects (although they should be!)
975 But for now, unreffing them only causes a GCritical Error
976 so comment them out for now. (and accept the memory leakage)
978 g_object_unref (var_sheet->val_labs_dialog);
979 g_object_unref (var_sheet->missing_val_dialog);
980 g_object_unref (var_sheet->var_type_dialog);
983 G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
987 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
989 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
992 gobject_class->set_property = psppire_var_sheet_set_property;
993 gobject_class->get_property = psppire_var_sheet_get_property;
994 gobject_class->dispose = psppire_var_sheet_dispose;
996 g_signal_new ("var-double-clicked",
997 G_OBJECT_CLASS_TYPE (gobject_class),
1000 g_signal_accumulator_true_handled, NULL,
1001 psppire_marshal_BOOLEAN__INT,
1002 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1004 pspec = g_param_spec_object ("dictionary",
1005 "Dictionary displayed by the sheet",
1006 "The PsppireDict that the sheet displays "
1007 "may allow the user to edit",
1010 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
1012 pspec = g_param_spec_boolean ("may-create-vars",
1013 "May create variables",
1014 "Whether the user may create more variables",
1017 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
1019 pspec = g_param_spec_boolean ("may-delete-vars",
1020 "May delete variables",
1021 "Whether the user may delete variables",
1024 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
1026 pspec = g_param_spec_enum ("format-use",
1027 "Use of variable format",
1028 ("Whether variables have input or output "
1030 PSPPIRE_TYPE_FMT_USE,
1033 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
1035 pspec = g_param_spec_object ("ui-manager",
1037 "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.",
1038 GTK_TYPE_UI_MANAGER,
1040 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1044 render_row_number_cell (PsppSheetViewColumn *tree_column,
1045 GtkCellRenderer *cell,
1046 GtkTreeModel *model,
1050 PsppireVarSheet *var_sheet = user_data;
1051 GValue gvalue = { 0, };
1054 row = GPOINTER_TO_INT (iter->user_data);
1056 g_value_init (&gvalue, G_TYPE_INT);
1057 g_value_set_int (&gvalue, row + 1);
1058 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1059 g_value_unset (&gvalue);
1061 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1062 g_object_set (cell, "editable", TRUE, NULL);
1064 g_object_set (cell, "editable", FALSE, NULL);
1068 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1070 PsppireVarSheet *var_sheet)
1074 g_return_if_fail (var_sheet->dict != NULL);
1076 path = gtk_tree_path_new_from_string (path_string);
1077 if (gtk_tree_path_get_depth (path) == 1)
1079 gint *indices = gtk_tree_path_get_indices (path);
1080 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1083 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1084 indices[0], &handled);
1087 gtk_tree_path_free (path);
1091 psppire_var_sheet_variables_column_clicked (PsppSheetViewColumn *column,
1092 PsppireVarSheet *var_sheet)
1094 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1095 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1097 pspp_sheet_selection_select_all (selection);
1100 static PsppSheetViewColumn *
1101 make_row_number_column (PsppireVarSheet *var_sheet)
1103 PsppSheetViewColumn *column;
1104 GtkCellRenderer *renderer;
1106 renderer = psppire_cell_renderer_button_new ();
1107 g_object_set (renderer, "xalign", 1.0, NULL);
1108 g_signal_connect (renderer, "double-clicked",
1109 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1112 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1114 pspp_sheet_view_column_set_clickable (column, TRUE);
1115 pspp_sheet_view_column_set_cell_data_func (
1116 column, renderer, render_row_number_cell, var_sheet, NULL);
1117 pspp_sheet_view_column_set_fixed_width (column, 50);
1118 g_signal_connect (column, "clicked",
1119 G_CALLBACK (psppire_var_sheet_variables_column_clicked),
1126 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1128 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1129 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1130 PsppireDict *dict = var_sheet->dict;
1131 const struct range_set_node *node;
1132 struct range_set *selected;
1134 selected = pspp_sheet_selection_get_range_set (selection);
1135 for (node = range_set_last (selected); node != NULL;
1136 node = range_set_prev (selected, node))
1140 for (i = 1; i <= range_set_node_get_width (node); i++)
1142 unsigned long row = range_set_node_get_end (node) - i;
1143 if (row >= 0 && row < psppire_dict_get_var_cnt (dict))
1144 psppire_dict_delete_variables (dict, row, 1);
1147 range_set_destroy (selected);
1151 on_selection_changed (PsppSheetSelection *selection,
1152 gpointer user_data UNUSED)
1154 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1155 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1156 gint n_selected_rows;
1157 gboolean may_delete;
1161 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1163 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1164 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1165 && n_selected_rows > 0));
1167 switch (n_selected_rows)
1174 /* The row used for inserting new variables cannot be deleted. */
1175 path = gtk_tree_path_new_from_indices (
1176 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1177 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1178 gtk_tree_path_free (path);
1185 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1186 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1190 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1192 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1193 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1194 PsppireDict *dict = var_sheet->dict;
1195 struct range_set *selected;
1198 selected = pspp_sheet_selection_get_range_set (selection);
1199 row = range_set_scan (selected, 0);
1200 range_set_destroy (selected);
1202 if (row <= psppire_dict_get_var_cnt (dict))
1205 if (psppire_dict_generate_name (dict, name, sizeof name))
1206 psppire_dict_insert_variable (dict, row, name);
1211 psppire_var_sheet_init (PsppireVarSheet *obj)
1213 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1214 PsppSheetViewColumn *column;
1215 GtkCellRenderer *cell;
1220 obj->format_use = FMT_FOR_OUTPUT;
1221 obj->may_create_vars = TRUE;
1222 obj->may_delete_vars = TRUE;
1224 obj->scroll_to_bottom_signal = 0;
1226 obj->container = NULL;
1227 obj->dispose_has_run = FALSE;
1230 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1232 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1233 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1234 g_signal_connect (list->data, "editing-started",
1235 G_CALLBACK (on_name_column_editing_started), NULL);
1238 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1239 add_popup_menu (obj, column, on_type_click);
1241 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1243 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1245 add_text_column (obj, VS_LABEL, _("Label"), 20);
1247 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1248 add_popup_menu (obj, column, on_value_labels_click);
1250 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1251 add_popup_menu (obj, column, on_missing_values_click);
1253 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1256 = add_combo_column (obj, VS_ALIGN, _("Align"), 8,
1257 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1258 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1259 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1261 cell = gtk_cell_renderer_pixbuf_new ();
1262 g_object_set (cell, "width", 16, "height", 16, NULL);
1263 pspp_sheet_view_column_pack_end (column, cell, FALSE);
1264 pspp_sheet_view_column_set_cell_data_func (
1265 column, cell, render_var_cell, obj, NULL);
1268 = add_combo_column (obj, VS_MEASURE, _("Measure"), 12,
1269 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1270 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1271 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1273 cell = gtk_cell_renderer_pixbuf_new ();
1274 g_object_set (cell, "width", 16, "height", 16, NULL);
1275 pspp_sheet_view_column_pack_end (column, cell, FALSE);
1276 pspp_sheet_view_column_set_cell_data_func (
1277 column, cell, render_var_cell, obj, NULL);
1279 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1280 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1281 PSPP_SHEET_SELECTION_MULTIPLE);
1283 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1284 g_signal_connect (obj, "query-tooltip",
1285 G_CALLBACK (on_query_var_tooltip), NULL);
1286 g_signal_connect (obj, "button-press-event",
1287 G_CALLBACK (on_button_pressed), NULL);
1288 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1290 obj->builder = builder_new ("var-sheet.ui");
1292 action = get_action_assert (obj->builder, "edit_clear-variables");
1293 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1295 gtk_action_set_sensitive (action, FALSE);
1296 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1297 "changed", G_CALLBACK (on_selection_changed), NULL);
1299 action = get_action_assert (obj->builder, "edit_insert-variable");
1300 gtk_action_set_sensitive (action, FALSE);
1301 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1306 psppire_var_sheet_new (void)
1308 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1312 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1314 return var_sheet->dict;
1318 refresh_model (PsppireVarSheet *var_sheet)
1320 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1322 if (var_sheet->dict != NULL)
1324 PsppireEmptyListStore *store;
1327 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1328 + var_sheet->may_create_vars);
1329 store = psppire_empty_list_store_new (n_rows);
1330 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1331 GTK_TREE_MODEL (store));
1332 g_object_unref (store);
1337 on_var_changed (PsppireDict *dict, glong row,
1338 guint what, const struct variable *oldvar,
1339 PsppireVarSheet *var_sheet)
1341 PsppireEmptyListStore *store;
1343 g_return_if_fail (dict == var_sheet->dict);
1345 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1346 PSPP_SHEET_VIEW (var_sheet)));
1347 g_return_if_fail (store != NULL);
1349 psppire_empty_list_store_row_changed (store, row);
1353 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1355 PsppireEmptyListStore *store;
1358 g_return_if_fail (dict == var_sheet->dict);
1360 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1361 PSPP_SHEET_VIEW (var_sheet)));
1362 g_return_if_fail (store != NULL);
1364 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1365 + var_sheet->may_create_vars);
1366 psppire_empty_list_store_set_n_rows (store, n_rows);
1367 psppire_empty_list_store_row_inserted (store, row);
1371 on_var_deleted (PsppireDict *dict,
1372 const struct variable *var, int dict_idx, int case_idx,
1373 PsppireVarSheet *var_sheet)
1375 PsppireEmptyListStore *store;
1378 g_return_if_fail (dict == var_sheet->dict);
1380 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1381 PSPP_SHEET_VIEW (var_sheet)));
1382 g_return_if_fail (store != NULL);
1384 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1385 + var_sheet->may_create_vars);
1386 psppire_empty_list_store_set_n_rows (store, n_rows);
1387 psppire_empty_list_store_row_deleted (store, dict_idx);
1391 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1393 g_return_if_fail (dict == var_sheet->dict);
1394 refresh_model (var_sheet);
1398 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1401 if (var_sheet->dict != NULL)
1405 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1407 if (var_sheet->dict_signals[i])
1408 g_signal_handler_disconnect (var_sheet->dict,
1409 var_sheet->dict_signals[i]);
1411 var_sheet->dict_signals[i] = 0;
1414 g_object_unref (var_sheet->dict);
1417 var_sheet->dict = dict;
1421 g_object_ref (dict);
1423 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1424 = g_signal_connect (dict, "backend-changed",
1425 G_CALLBACK (on_backend_changed), var_sheet);
1427 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED]
1428 = g_signal_connect (dict, "variable-changed",
1429 G_CALLBACK (on_var_changed), var_sheet);
1431 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1432 = g_signal_connect (dict, "variable-inserted",
1433 G_CALLBACK (on_var_inserted), var_sheet);
1435 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1436 = g_signal_connect (dict, "variable-deleted",
1437 G_CALLBACK (on_var_deleted), var_sheet);
1440 refresh_model (var_sheet);
1444 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1446 return var_sheet->may_create_vars;
1450 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1451 gboolean may_create_vars)
1453 if (var_sheet->may_create_vars != may_create_vars)
1455 PsppireEmptyListStore *store;
1458 var_sheet->may_create_vars = may_create_vars;
1460 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1461 PSPP_SHEET_VIEW (var_sheet)));
1462 g_return_if_fail (store != NULL);
1464 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1465 + var_sheet->may_create_vars);
1466 psppire_empty_list_store_set_n_rows (store, n_rows);
1468 if (may_create_vars)
1469 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1471 psppire_empty_list_store_row_deleted (store, n_rows);
1473 on_selection_changed (pspp_sheet_view_get_selection (
1474 PSPP_SHEET_VIEW (var_sheet)), NULL);
1479 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1481 return var_sheet->may_delete_vars;
1485 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1486 gboolean may_delete_vars)
1488 if (var_sheet->may_delete_vars != may_delete_vars)
1490 var_sheet->may_delete_vars = may_delete_vars;
1491 on_selection_changed (pspp_sheet_view_get_selection (
1492 PSPP_SHEET_VIEW (var_sheet)), NULL);
1497 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1499 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1502 path = gtk_tree_path_new_from_indices (dict_index, -1);
1503 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1504 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1505 gtk_tree_path_free (path);
1509 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1511 if (var_sheet->uim == NULL)
1513 var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1515 GTK_TYPE_UI_MANAGER));
1516 g_object_ref (var_sheet->uim);
1519 return var_sheet->uim;