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 render_var_cell (PsppSheetViewColumn *tree_column,
340 GtkCellRenderer *cell,
345 PsppireVarSheet *var_sheet = user_data;
346 const struct fmt_spec *print;
347 enum vs_column column_id;
348 struct variable *var;
351 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
352 "column-number")) - 1;
353 row = GPOINTER_TO_INT (iter->user_data);
355 if (row >= psppire_dict_get_var_cnt (var_sheet->dict))
357 if (GTK_IS_CELL_RENDERER_TEXT (cell))
361 "editable", column_id == VS_NAME,
363 if (column_id == VS_WIDTH
364 || column_id == VS_DECIMALS
365 || column_id == VS_COLUMNS)
366 g_object_set (cell, "adjustment", NULL, NULL);
369 g_object_set (cell, "stock-id", "", NULL);
373 var = psppire_dict_get_variable (var_sheet->dict, row);
375 print = var_get_print_format (var);
380 "text", var_get_name (var),
387 "text", fmt_gui_name (print->type),
393 set_spin_cell (cell, print->w,
394 fmt_min_width (print->type, var_sheet->format_use),
395 fmt_max_width (print->type, var_sheet->format_use),
396 fmt_step_width (print->type));
400 if (fmt_takes_decimals (print->type))
402 int max_w = fmt_max_width (print->type, var_sheet->format_use);
403 int max_d = fmt_max_decimals (print->type, max_w,
404 var_sheet->format_use);
405 set_spin_cell (cell, print->d, 0, max_d, 1);
417 "text", var_has_label (var) ? var_get_label (var) : "",
423 g_object_set (cell, "editable", FALSE, NULL);
424 if ( ! var_has_value_labels (var))
425 g_object_set (cell, "text", _("None"), NULL);
428 const struct val_labs *vls = var_get_value_labels (var);
429 const struct val_lab **labels = val_labs_sorted (vls);
430 const struct val_lab *vl = labels[0];
431 gchar *vstr = value_to_text (vl->value, var);
432 char *text = xasprintf (_("{%s, %s}..."), vstr,
433 val_lab_get_escaped_label (vl));
436 g_object_set (cell, "text", text, NULL);
444 char *text = missing_values_to_string (var_sheet->dict, var, NULL);
454 set_spin_cell (cell, var_get_display_width (var), 1, 2 * MAX_STRING, 1);
458 if (GTK_IS_CELL_RENDERER_TEXT (cell))
460 "text", alignment_to_string (var_get_alignment (var)),
464 g_object_set (cell, "stock-id",
465 get_var_align_stock_id (var_get_alignment (var)), NULL);
469 if (GTK_IS_CELL_RENDERER_TEXT (cell))
471 "text", measure_to_string (var_get_measure (var)),
476 enum fmt_type type = var_get_print_format (var)->type;
477 enum measure measure = var_get_measure (var);
479 g_object_set (cell, "stock-id",
480 get_var_measurement_stock_id (type, measure),
487 "text", var_role_to_string (var_get_role (var)),
494 static struct variable *
495 path_string_to_variable (PsppireVarSheet *var_sheet, gchar *path_string)
501 path = gtk_tree_path_new_from_string (path_string);
502 row = gtk_tree_path_get_indices (path)[0];
503 gtk_tree_path_free (path);
505 dict = psppire_var_sheet_get_dictionary (var_sheet);
506 g_return_val_if_fail (dict != NULL, NULL);
508 return psppire_dict_get_variable (dict, row);
512 on_type_click (PsppireCellRendererButton *cell,
514 PsppireVarSheet *var_sheet)
516 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
517 struct fmt_spec format;
518 struct variable *var;
520 var = path_string_to_variable (var_sheet, path);
521 g_return_if_fail (var != NULL);
523 format = *var_get_print_format (var);
524 psppire_var_type_dialog_run (GTK_WINDOW (toplevel), &format);
526 var_set_width_and_formats (var, fmt_var_width (&format), &format, &format);
530 on_value_labels_click (PsppireCellRendererButton *cell,
532 PsppireVarSheet *var_sheet)
534 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
535 struct val_labs *labels;
536 struct variable *var;
538 var = path_string_to_variable (var_sheet, path);
539 g_return_if_fail (var != NULL);
541 labels = psppire_val_labs_dialog_run (GTK_WINDOW (toplevel), var);
544 var_set_value_labels (var, labels);
545 val_labs_destroy (labels);
550 on_missing_values_click (PsppireCellRendererButton *cell,
552 PsppireVarSheet *var_sheet)
554 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
555 struct missing_values mv;
556 struct variable *var;
558 var = path_string_to_variable (var_sheet, path);
559 g_return_if_fail (var != NULL);
561 psppire_missing_val_dialog_run (GTK_WINDOW (toplevel), var, &mv);
562 var_set_missing_values (var, &mv);
567 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
571 g_object_set (G_OBJECT (renderer),
572 PSPPIRE_IS_CELL_RENDERER_BUTTON (renderer) ? "label" : "text",
573 string, (void *) NULL);
574 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
575 NULL, NULL, NULL, &width, NULL);
580 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
587 ds_put_byte_multiple (&s, '0', char_cnt);
588 ds_put_byte (&s, ' ');
589 width = get_string_width (treeview, renderer, ds_cstr (&s));
595 static PsppSheetViewColumn *
596 add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer,
597 enum vs_column column_id,
598 const char *title, int width)
600 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
601 int title_width, content_width;
602 PsppSheetViewColumn *column;
604 column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL);
605 g_object_set_data (G_OBJECT (column), "column-number",
606 GINT_TO_POINTER (column_id) + 1);
608 pspp_sheet_view_column_set_cell_data_func (
609 column, renderer, render_var_cell, var_sheet, NULL);
611 title_width = get_string_width (sheet_view, renderer, title);
612 content_width = get_monospace_width (sheet_view, renderer, width);
613 g_object_set_data (G_OBJECT (column), "content-width",
614 GINT_TO_POINTER (content_width));
616 pspp_sheet_view_column_set_fixed_width (column,
617 MAX (title_width, content_width));
618 pspp_sheet_view_column_set_resizable (column, TRUE);
620 pspp_sheet_view_append_column (sheet_view, column);
622 g_signal_connect (renderer, "edited",
623 G_CALLBACK (on_var_column_edited),
625 g_object_set_data (G_OBJECT (renderer), "column-id",
626 GINT_TO_POINTER (column_id));
627 g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet);
632 static PsppSheetViewColumn *
633 add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
634 const char *title, int width)
636 return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (),
637 column_id, title, width);
640 static PsppSheetViewColumn *
641 add_spin_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
642 const char *title, int width)
644 return add_var_sheet_column (var_sheet, gtk_cell_renderer_spin_new (),
645 column_id, title, width);
648 static PsppSheetViewColumn *
649 add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
650 const char *title, int width,
653 GtkCellRenderer *cell;
658 store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
659 va_start (args, width);
660 while ((name = va_arg (args, const char *)) != NULL)
662 int value = va_arg (args, int);
663 gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
670 cell = gtk_cell_renderer_combo_new ();
673 "model", GTK_TREE_MODEL (store),
677 return add_var_sheet_column (var_sheet, cell, column_id, title, width);
682 add_popup_menu (PsppireVarSheet *var_sheet,
683 PsppSheetViewColumn *column,
684 void (*on_click) (PsppireCellRendererButton *,
686 PsppireVarSheet *var_sheet))
688 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
689 const char *button_label = "...";
690 GtkCellRenderer *button_renderer;
693 button_renderer = psppire_cell_renderer_button_new ();
694 g_object_set (button_renderer,
695 "label", button_label,
698 g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click),
700 pspp_sheet_view_column_pack_start (column, button_renderer, FALSE);
701 pspp_sheet_view_column_set_cell_data_func (
702 column, button_renderer, render_popup_cell, var_sheet, NULL);
704 content_width = GPOINTER_TO_INT (g_object_get_data (
705 G_OBJECT (column), "content-width"));
706 content_width += get_string_width (sheet_view, button_renderer,
708 if (content_width > pspp_sheet_view_column_get_fixed_width (column))
709 pspp_sheet_view_column_set_fixed_width (column, content_width);
713 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
714 gint wx, gint wy, size_t *row, size_t *column)
716 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
720 PsppSheetViewColumn *tree_column;
721 GtkTreeModel *tree_model;
725 /* Check that WIDGET is really visible on the screen before we
726 do anything else. This is a bug fix for a sticky situation:
727 when text_data_import_assistant() returns, it frees the data
728 necessary to compose the tool tip message, but there may be
729 a tool tip under preparation at that point (even if there is
730 no visible tool tip) that will call back into us a little
731 bit later. Perhaps the correct solution to this problem is
732 to make the data related to the tool tips part of a GObject
733 that only gets destroyed when all references are released,
734 but this solution appears to be effective too. */
735 if (!gtk_widget_get_mapped (widget))
738 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
740 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
741 &path, &tree_column, NULL, NULL))
744 column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number");
745 if (column_ptr == NULL)
747 *column = GPOINTER_TO_INT (column_ptr) - 1;
749 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
752 tree_model = pspp_sheet_view_get_model (tree_view);
753 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
754 gtk_tree_path_free (path);
758 *row = GPOINTER_TO_INT (iter.user_data);
763 on_query_var_tooltip (GtkWidget *widget, gint wx, gint wy,
764 gboolean keyboard_mode UNUSED,
765 GtkTooltip *tooltip, gpointer *user_data UNUSED)
767 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
769 struct variable *var;
772 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
775 dict = psppire_var_sheet_get_dictionary (var_sheet);
776 g_return_val_if_fail (dict != NULL, FALSE);
778 if (row >= psppire_dict_get_var_cnt (dict))
780 gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a "
785 var = psppire_dict_get_variable (dict, row);
786 g_return_val_if_fail (var != NULL, FALSE);
792 char text[FMT_STRING_LEN_MAX + 1];
794 fmt_to_string (var_get_print_format (var), text);
795 gtk_tooltip_set_text (tooltip, text);
800 if (var_has_value_labels (var))
802 const struct val_labs *vls = var_get_value_labels (var);
803 const struct val_lab **labels = val_labs_sorted (vls);
808 for (i = 0; i < val_labs_count (vls); i++)
810 const struct val_lab *vl = labels[i];
813 if (i >= 10 || ds_length (&s) > 500)
815 ds_put_cstr (&s, "...");
819 vstr = value_to_text (vl->value, var);
820 ds_put_format (&s, _("{%s, %s}\n"), vstr,
821 val_lab_get_escaped_label (vl));
825 ds_chomp_byte (&s, '\n');
827 gtk_tooltip_set_text (tooltip, ds_cstr (&s));
839 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
841 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
844 menu = get_widget_assert (var_sheet->builder, "varsheet-variable-popup");
845 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
849 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
851 do_popup_menu (widget, 0, gtk_get_current_event_time ());
855 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
856 gpointer user_data UNUSED)
858 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
860 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
862 PsppSheetSelection *selection;
864 selection = pspp_sheet_view_get_selection (sheet_view);
865 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
869 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
870 &path, NULL, NULL, NULL))
872 pspp_sheet_selection_unselect_all (selection);
873 pspp_sheet_selection_select_path (selection, path);
874 gtk_tree_path_free (path);
878 do_popup_menu (widget, event->button, event->time);
886 psppire_fmt_use_get_type (void)
888 static GType etype = 0;
891 static const GEnumValue values[] =
893 { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
894 { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
898 etype = g_enum_register_static
899 (g_intern_static_string ("PsppireFmtUse"), values);
908 PROP_MAY_CREATE_VARS,
909 PROP_MAY_DELETE_VARS,
915 psppire_var_sheet_set_property (GObject *object,
920 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
924 case PROP_DICTIONARY:
925 psppire_var_sheet_set_dictionary (obj,
926 PSPPIRE_DICT (g_value_get_object (
930 case PROP_MAY_CREATE_VARS:
931 psppire_var_sheet_set_may_create_vars (obj,
932 g_value_get_boolean (value));
935 case PROP_MAY_DELETE_VARS:
936 psppire_var_sheet_set_may_delete_vars (obj,
937 g_value_get_boolean (value));
940 case PROP_FORMAT_TYPE:
941 obj->format_use = g_value_get_enum (value);
944 case PROP_UI_MANAGER:
946 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
952 psppire_var_sheet_get_property (GObject *object,
957 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
961 case PROP_DICTIONARY:
962 g_value_set_object (value,
963 G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
966 case PROP_MAY_CREATE_VARS:
967 g_value_set_boolean (value, obj->may_create_vars);
970 case PROP_MAY_DELETE_VARS:
971 g_value_set_boolean (value, obj->may_delete_vars);
974 case PROP_FORMAT_TYPE:
975 g_value_set_enum (value, obj->format_use);
978 case PROP_UI_MANAGER:
979 g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj));
983 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
989 psppire_var_sheet_dispose (GObject *obj)
991 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
994 if (var_sheet->dispose_has_run)
997 var_sheet->dispose_has_run = TRUE;
999 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1000 if ( var_sheet->dict_signals[i])
1001 g_signal_handler_disconnect (var_sheet->dict,
1002 var_sheet->dict_signals[i]);
1004 if (var_sheet->dict)
1005 g_object_unref (var_sheet->dict);
1008 g_object_unref (var_sheet->uim);
1010 /* These dialogs are not GObjects (although they should be!)
1011 But for now, unreffing them only causes a GCritical Error
1012 so comment them out for now. (and accept the memory leakage)
1014 g_object_unref (var_sheet->val_labs_dialog);
1015 g_object_unref (var_sheet->missing_val_dialog);
1016 g_object_unref (var_sheet->var_type_dialog);
1019 G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
1023 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
1025 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1028 gobject_class->set_property = psppire_var_sheet_set_property;
1029 gobject_class->get_property = psppire_var_sheet_get_property;
1030 gobject_class->dispose = psppire_var_sheet_dispose;
1032 g_signal_new ("var-double-clicked",
1033 G_OBJECT_CLASS_TYPE (gobject_class),
1036 g_signal_accumulator_true_handled, NULL,
1037 psppire_marshal_BOOLEAN__INT,
1038 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1040 pspec = g_param_spec_object ("dictionary",
1041 "Dictionary displayed by the sheet",
1042 "The PsppireDict that the sheet displays "
1043 "may allow the user to edit",
1046 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
1048 pspec = g_param_spec_boolean ("may-create-vars",
1049 "May create variables",
1050 "Whether the user may create more variables",
1053 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
1055 pspec = g_param_spec_boolean ("may-delete-vars",
1056 "May delete variables",
1057 "Whether the user may delete variables",
1060 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
1062 pspec = g_param_spec_enum ("format-use",
1063 "Use of variable format",
1064 ("Whether variables have input or output "
1066 PSPPIRE_TYPE_FMT_USE,
1069 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
1071 pspec = g_param_spec_object ("ui-manager",
1073 "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.",
1074 GTK_TYPE_UI_MANAGER,
1076 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1080 render_row_number_cell (PsppSheetViewColumn *tree_column,
1081 GtkCellRenderer *cell,
1082 GtkTreeModel *model,
1086 PsppireVarSheet *var_sheet = user_data;
1087 GValue gvalue = { 0, };
1090 row = GPOINTER_TO_INT (iter->user_data);
1092 g_value_init (&gvalue, G_TYPE_INT);
1093 g_value_set_int (&gvalue, row + 1);
1094 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1095 g_value_unset (&gvalue);
1097 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1098 g_object_set (cell, "editable", TRUE, NULL);
1100 g_object_set (cell, "editable", FALSE, NULL);
1104 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1106 PsppireVarSheet *var_sheet)
1110 g_return_if_fail (var_sheet->dict != NULL);
1112 path = gtk_tree_path_new_from_string (path_string);
1113 if (gtk_tree_path_get_depth (path) == 1)
1115 gint *indices = gtk_tree_path_get_indices (path);
1116 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1119 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1120 indices[0], &handled);
1123 gtk_tree_path_free (path);
1127 psppire_var_sheet_variables_column_clicked (PsppSheetViewColumn *column,
1128 PsppireVarSheet *var_sheet)
1130 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1131 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1133 pspp_sheet_selection_select_all (selection);
1136 static PsppSheetViewColumn *
1137 make_row_number_column (PsppireVarSheet *var_sheet)
1139 PsppSheetViewColumn *column;
1140 GtkCellRenderer *renderer;
1142 renderer = psppire_cell_renderer_button_new ();
1143 g_object_set (renderer, "xalign", 1.0, NULL);
1144 g_signal_connect (renderer, "double-clicked",
1145 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1148 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1150 pspp_sheet_view_column_set_clickable (column, TRUE);
1151 pspp_sheet_view_column_set_cell_data_func (
1152 column, renderer, render_row_number_cell, var_sheet, NULL);
1153 pspp_sheet_view_column_set_fixed_width (column, 50);
1154 g_signal_connect (column, "clicked",
1155 G_CALLBACK (psppire_var_sheet_variables_column_clicked),
1162 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1164 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1165 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1166 PsppireDict *dict = var_sheet->dict;
1167 const struct range_set_node *node;
1168 struct range_set *selected;
1170 selected = pspp_sheet_selection_get_range_set (selection);
1171 for (node = range_set_last (selected); node != NULL;
1172 node = range_set_prev (selected, node))
1176 for (i = 1; i <= range_set_node_get_width (node); i++)
1178 unsigned long row = range_set_node_get_end (node) - i;
1179 if (row >= 0 && row < psppire_dict_get_var_cnt (dict))
1180 psppire_dict_delete_variables (dict, row, 1);
1183 range_set_destroy (selected);
1187 on_selection_changed (PsppSheetSelection *selection,
1188 gpointer user_data UNUSED)
1190 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1191 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1192 gint n_selected_rows;
1193 gboolean may_delete;
1197 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1199 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1200 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1201 && n_selected_rows > 0));
1203 switch (n_selected_rows)
1210 /* The row used for inserting new variables cannot be deleted. */
1211 path = gtk_tree_path_new_from_indices (
1212 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1213 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1214 gtk_tree_path_free (path);
1221 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1222 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1226 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1228 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1229 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1230 PsppireDict *dict = var_sheet->dict;
1231 struct range_set *selected;
1234 selected = pspp_sheet_selection_get_range_set (selection);
1235 row = range_set_scan (selected, 0);
1236 range_set_destroy (selected);
1238 if (row <= psppire_dict_get_var_cnt (dict))
1241 if (psppire_dict_generate_name (dict, name, sizeof name))
1242 psppire_dict_insert_variable (dict, row, name);
1247 psppire_var_sheet_init (PsppireVarSheet *obj)
1249 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1250 PsppSheetViewColumn *column;
1251 GtkCellRenderer *cell;
1256 obj->format_use = FMT_FOR_OUTPUT;
1257 obj->may_create_vars = TRUE;
1258 obj->may_delete_vars = TRUE;
1260 obj->scroll_to_bottom_signal = 0;
1262 obj->container = NULL;
1263 obj->dispose_has_run = FALSE;
1266 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1268 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1269 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1270 g_signal_connect (list->data, "editing-started",
1271 G_CALLBACK (on_name_column_editing_started), NULL);
1274 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1275 add_popup_menu (obj, column, on_type_click);
1277 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1279 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1281 add_text_column (obj, VS_LABEL, _("Label"), 20);
1283 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1284 add_popup_menu (obj, column, on_value_labels_click);
1286 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1287 add_popup_menu (obj, column, on_missing_values_click);
1289 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1292 = add_combo_column (obj, VS_ALIGN, _("Align"), 8,
1293 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1294 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1295 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1297 cell = gtk_cell_renderer_pixbuf_new ();
1298 g_object_set (cell, "width", 16, "height", 16, NULL);
1299 pspp_sheet_view_column_pack_end (column, cell, FALSE);
1300 pspp_sheet_view_column_set_cell_data_func (
1301 column, cell, render_var_cell, obj, NULL);
1304 = add_combo_column (obj, VS_MEASURE, _("Measure"), 12,
1305 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1306 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1307 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1309 cell = gtk_cell_renderer_pixbuf_new ();
1310 g_object_set (cell, "width", 16, "height", 16, NULL);
1311 pspp_sheet_view_column_pack_end (column, cell, FALSE);
1312 pspp_sheet_view_column_set_cell_data_func (
1313 column, cell, render_var_cell, obj, NULL);
1315 add_combo_column (obj, VS_ROLE, _("Role"), 12,
1316 var_role_to_string (ROLE_INPUT), ROLE_INPUT,
1317 var_role_to_string (ROLE_OUTPUT), ROLE_OUTPUT,
1318 var_role_to_string (ROLE_BOTH), ROLE_BOTH,
1319 var_role_to_string (ROLE_NONE), ROLE_NONE,
1320 var_role_to_string (ROLE_PARTITION), ROLE_PARTITION,
1321 var_role_to_string (ROLE_SPLIT), ROLE_SPLIT,
1324 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1325 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1326 PSPP_SHEET_SELECTION_MULTIPLE);
1328 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1329 g_signal_connect (obj, "query-tooltip",
1330 G_CALLBACK (on_query_var_tooltip), NULL);
1331 g_signal_connect (obj, "button-press-event",
1332 G_CALLBACK (on_button_pressed), NULL);
1333 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1335 obj->builder = builder_new ("var-sheet.ui");
1337 action = get_action_assert (obj->builder, "edit_clear-variables");
1338 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1340 gtk_action_set_sensitive (action, FALSE);
1341 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1342 "changed", G_CALLBACK (on_selection_changed), NULL);
1344 action = get_action_assert (obj->builder, "edit_insert-variable");
1345 gtk_action_set_sensitive (action, FALSE);
1346 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1351 psppire_var_sheet_new (void)
1353 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1357 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1359 return var_sheet->dict;
1363 refresh_model (PsppireVarSheet *var_sheet)
1365 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1367 if (var_sheet->dict != NULL)
1369 PsppireEmptyListStore *store;
1372 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1373 + var_sheet->may_create_vars);
1374 store = psppire_empty_list_store_new (n_rows);
1375 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1376 GTK_TREE_MODEL (store));
1377 g_object_unref (store);
1382 on_var_changed (PsppireDict *dict, glong row,
1383 guint what, const struct variable *oldvar,
1384 PsppireVarSheet *var_sheet)
1386 PsppireEmptyListStore *store;
1388 g_return_if_fail (dict == var_sheet->dict);
1390 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1391 PSPP_SHEET_VIEW (var_sheet)));
1392 g_return_if_fail (store != NULL);
1394 psppire_empty_list_store_row_changed (store, row);
1398 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1400 PsppireEmptyListStore *store;
1403 g_return_if_fail (dict == var_sheet->dict);
1405 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1406 PSPP_SHEET_VIEW (var_sheet)));
1407 g_return_if_fail (store != NULL);
1409 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1410 + var_sheet->may_create_vars);
1411 psppire_empty_list_store_set_n_rows (store, n_rows);
1412 psppire_empty_list_store_row_inserted (store, row);
1416 on_var_deleted (PsppireDict *dict,
1417 const struct variable *var, int dict_idx, int case_idx,
1418 PsppireVarSheet *var_sheet)
1420 PsppireEmptyListStore *store;
1423 g_return_if_fail (dict == var_sheet->dict);
1425 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1426 PSPP_SHEET_VIEW (var_sheet)));
1427 g_return_if_fail (store != NULL);
1429 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1430 + var_sheet->may_create_vars);
1431 psppire_empty_list_store_set_n_rows (store, n_rows);
1432 psppire_empty_list_store_row_deleted (store, dict_idx);
1436 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1438 g_return_if_fail (dict == var_sheet->dict);
1439 refresh_model (var_sheet);
1443 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1446 if (var_sheet->dict != NULL)
1450 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1452 if (var_sheet->dict_signals[i])
1453 g_signal_handler_disconnect (var_sheet->dict,
1454 var_sheet->dict_signals[i]);
1456 var_sheet->dict_signals[i] = 0;
1459 g_object_unref (var_sheet->dict);
1462 var_sheet->dict = dict;
1466 g_object_ref (dict);
1468 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1469 = g_signal_connect (dict, "backend-changed",
1470 G_CALLBACK (on_backend_changed), var_sheet);
1472 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED]
1473 = g_signal_connect (dict, "variable-changed",
1474 G_CALLBACK (on_var_changed), var_sheet);
1476 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1477 = g_signal_connect (dict, "variable-inserted",
1478 G_CALLBACK (on_var_inserted), var_sheet);
1480 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1481 = g_signal_connect (dict, "variable-deleted",
1482 G_CALLBACK (on_var_deleted), var_sheet);
1485 refresh_model (var_sheet);
1489 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1491 return var_sheet->may_create_vars;
1495 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1496 gboolean may_create_vars)
1498 if (var_sheet->may_create_vars != may_create_vars)
1500 PsppireEmptyListStore *store;
1503 var_sheet->may_create_vars = may_create_vars;
1505 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1506 PSPP_SHEET_VIEW (var_sheet)));
1507 g_return_if_fail (store != NULL);
1509 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1510 + var_sheet->may_create_vars);
1511 psppire_empty_list_store_set_n_rows (store, n_rows);
1513 if (may_create_vars)
1514 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1516 psppire_empty_list_store_row_deleted (store, n_rows);
1518 on_selection_changed (pspp_sheet_view_get_selection (
1519 PSPP_SHEET_VIEW (var_sheet)), NULL);
1524 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1526 return var_sheet->may_delete_vars;
1530 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1531 gboolean may_delete_vars)
1533 if (var_sheet->may_delete_vars != may_delete_vars)
1535 var_sheet->may_delete_vars = may_delete_vars;
1536 on_selection_changed (pspp_sheet_view_get_selection (
1537 PSPP_SHEET_VIEW (var_sheet)), NULL);
1542 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1544 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1547 path = gtk_tree_path_new_from_indices (dict_index, -1);
1548 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1549 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1550 gtk_tree_path_free (path);
1554 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1556 if (var_sheet->uim == NULL)
1558 var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1560 GTK_TYPE_UI_MANAGER));
1561 g_object_ref (var_sheet->uim);
1564 return var_sheet->uim;