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;
147 on_var_column_edited (GtkCellRendererText *cell,
152 PsppireVarSheet *var_sheet = user_data;
153 GtkWindow *window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (var_sheet)));
154 struct dictionary *dict = var_sheet->dict->dict;
155 enum vs_column column_id;
156 struct variable *var;
161 path = gtk_tree_path_new_from_string (path_string);
162 row = gtk_tree_path_get_indices (path)[0];
163 gtk_tree_path_free (path);
165 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell),
168 var = psppire_dict_get_variable (var_sheet->dict, row);
171 g_return_if_fail (column_id == VS_NAME);
173 if (!dict_id_is_valid (dict, new_text, false))
174 error_dialog (window,
175 g_strdup (_("Cannot create variable.")),
176 g_strdup_printf (_("\"%s\" is not a valid variable "
177 "name."), new_text));
178 else if (dict_lookup_var (dict, new_text) != NULL)
179 error_dialog (window,
180 g_strdup (_("Cannot create variable.")),
181 g_strdup_printf (_("This dictionary already contains "
182 "a variable named \"%s\"."),
186 dict_create_var (var_sheet->dict->dict, new_text, 0);
187 if (!var_sheet->scroll_to_bottom_signal)
189 gtk_widget_queue_resize (GTK_WIDGET (var_sheet));
190 var_sheet->scroll_to_bottom_signal =
191 g_signal_connect (var_sheet, "size-request",
192 G_CALLBACK (scroll_to_bottom), NULL);
202 if (!dict_id_is_valid (dict, new_text, false))
203 error_dialog (window,
204 g_strdup (_("Cannot rename variable.")),
205 g_strdup_printf (_("\"%s\" is not a valid variable "
206 "name."), new_text));
207 else if (dict_lookup_var (dict, new_text) != NULL
208 && dict_lookup_var (dict, new_text) != var)
209 error_dialog (window,
210 g_strdup (_("Cannot rename variable.")),
211 g_strdup_printf (_("This dictionary already contains "
212 "a variable named \"%s\"."),
215 dict_rename_var (dict, var, new_text);
223 width = atoi (new_text);
226 struct fmt_spec format;
228 format = *var_get_print_format (var);
229 fmt_change_width (&format, width, var_sheet->format_use);
230 var_set_width (var, fmt_var_width (&format));
231 var_set_both_formats (var, &format);
236 decimals = atoi (new_text);
239 struct fmt_spec format;
241 format = *var_get_print_format (var);
242 fmt_change_decimals (&format, decimals, var_sheet->format_use);
243 var_set_print_format (var, &format);
248 var_set_label (var, new_text, false);
256 width = atoi (new_text);
257 if (width > 0 && width < 2 * MAX_STRING)
258 var_set_display_width (var, width);
262 if (!strcmp (new_text, alignment_to_string (ALIGN_LEFT)))
263 var_set_alignment (var, ALIGN_LEFT);
264 else if (!strcmp (new_text, alignment_to_string (ALIGN_CENTRE)))
265 var_set_alignment (var, ALIGN_CENTRE);
266 else if (!strcmp (new_text, alignment_to_string (ALIGN_RIGHT)))
267 var_set_alignment (var, ALIGN_RIGHT);
271 if (!strcmp (new_text, measure_to_string (MEASURE_NOMINAL)))
272 var_set_measure (var, MEASURE_NOMINAL);
273 else if (!strcmp (new_text, measure_to_string (MEASURE_ORDINAL)))
274 var_set_measure (var, MEASURE_ORDINAL);
275 else if (!strcmp (new_text, measure_to_string (MEASURE_SCALE)))
276 var_set_measure (var, MEASURE_SCALE);
280 if (!strcmp (new_text, var_role_to_string (ROLE_INPUT)))
281 var_set_role (var, ROLE_INPUT);
282 else if (!strcmp (new_text, var_role_to_string (ROLE_OUTPUT)))
283 var_set_role (var, ROLE_OUTPUT);
284 else if (!strcmp (new_text, var_role_to_string (ROLE_BOTH)))
285 var_set_role (var, ROLE_BOTH);
286 else if (!strcmp (new_text, var_role_to_string (ROLE_NONE)))
287 var_set_role (var, ROLE_NONE);
288 else if (!strcmp (new_text, var_role_to_string (ROLE_PARTITION)))
289 var_set_role (var, ROLE_PARTITION);
290 else if (!strcmp (new_text, var_role_to_string (ROLE_SPLIT)))
291 var_set_role (var, ROLE_SPLIT);
297 render_popup_cell (PsppSheetViewColumn *tree_column,
298 GtkCellRenderer *cell,
303 PsppireVarSheet *var_sheet = user_data;
306 row = GPOINTER_TO_INT (iter->user_data);
308 "editable", row < psppire_dict_get_var_cnt (var_sheet->dict),
313 get_var_align_stock_id (const struct variable *var)
315 switch (var_get_alignment (var))
318 return GTK_STOCK_JUSTIFY_LEFT;
321 return GTK_STOCK_JUSTIFY_CENTER;
324 return GTK_STOCK_JUSTIFY_RIGHT;
327 g_return_val_if_reached ("");
332 render_var_cell (PsppSheetViewColumn *tree_column,
333 GtkCellRenderer *cell,
338 PsppireVarSheet *var_sheet = user_data;
339 const struct fmt_spec *print;
340 enum vs_column column_id;
341 struct variable *var;
344 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
345 "column-number")) - 1;
346 row = GPOINTER_TO_INT (iter->user_data);
348 if (row >= psppire_dict_get_var_cnt (var_sheet->dict))
350 if (GTK_IS_CELL_RENDERER_TEXT (cell))
354 "editable", column_id == VS_NAME,
356 if (column_id == VS_WIDTH
357 || column_id == VS_DECIMALS
358 || column_id == VS_COLUMNS)
359 g_object_set (cell, "adjustment", NULL, NULL);
362 g_object_set (cell, "stock-id", "", NULL);
366 var = psppire_dict_get_variable (var_sheet->dict, row);
368 print = var_get_print_format (var);
373 "text", var_get_name (var),
380 "text", fmt_gui_name (print->type),
386 set_spin_cell (cell, print->w,
387 fmt_min_width (print->type, var_sheet->format_use),
388 fmt_max_width (print->type, var_sheet->format_use),
389 fmt_step_width (print->type));
393 if (fmt_takes_decimals (print->type))
395 int max_w = fmt_max_width (print->type, var_sheet->format_use);
396 int max_d = fmt_max_decimals (print->type, max_w,
397 var_sheet->format_use);
398 set_spin_cell (cell, print->d, 0, max_d, 1);
410 "text", var_has_label (var) ? var_get_label (var) : "",
416 g_object_set (cell, "editable", FALSE, NULL);
417 if ( ! var_has_value_labels (var))
418 g_object_set (cell, "text", _("None"), NULL);
421 const struct val_labs *vls = var_get_value_labels (var);
422 const struct val_lab **labels = val_labs_sorted (vls);
423 const struct val_lab *vl = labels[0];
424 gchar *vstr = value_to_text (vl->value, var);
425 char *text = xasprintf (_("{%s, %s}..."), vstr,
426 val_lab_get_escaped_label (vl));
429 g_object_set (cell, "text", text, NULL);
437 char *text = missing_values_to_string (var_sheet->dict, var, NULL);
447 set_spin_cell (cell, var_get_display_width (var), 1, 2 * MAX_STRING, 1);
451 if (GTK_IS_CELL_RENDERER_TEXT (cell))
453 "text", alignment_to_string (var_get_alignment (var)),
457 g_object_set (cell, "stock-id", get_var_align_stock_id (var), NULL);
461 if (GTK_IS_CELL_RENDERER_TEXT (cell))
463 "text", measure_to_string (var_get_measure (var)),
468 enum fmt_type type = var_get_print_format (var)->type;
469 enum measure measure = var_get_measure (var);
471 g_object_set (cell, "stock-id",
472 get_var_measurement_stock_id (type, measure),
479 "text", var_role_to_string (var_get_role (var)),
486 static struct variable *
487 path_string_to_variable (PsppireVarSheet *var_sheet, gchar *path_string)
493 path = gtk_tree_path_new_from_string (path_string);
494 row = gtk_tree_path_get_indices (path)[0];
495 gtk_tree_path_free (path);
497 dict = psppire_var_sheet_get_dictionary (var_sheet);
498 g_return_val_if_fail (dict != NULL, NULL);
500 return psppire_dict_get_variable (dict, row);
504 on_type_click (PsppireCellRendererButton *cell,
506 PsppireVarSheet *var_sheet)
508 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
509 struct fmt_spec format;
510 struct variable *var;
512 var = path_string_to_variable (var_sheet, path);
513 g_return_if_fail (var != NULL);
515 format = *var_get_print_format (var);
516 psppire_var_type_dialog_run (GTK_WINDOW (toplevel), &format);
518 var_set_width_and_formats (var, fmt_var_width (&format), &format, &format);
522 on_value_labels_click (PsppireCellRendererButton *cell,
524 PsppireVarSheet *var_sheet)
526 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
527 struct val_labs *labels;
528 struct variable *var;
530 var = path_string_to_variable (var_sheet, path);
531 g_return_if_fail (var != NULL);
533 labels = psppire_val_labs_dialog_run (GTK_WINDOW (toplevel), var);
536 var_set_value_labels (var, labels);
537 val_labs_destroy (labels);
542 on_missing_values_click (PsppireCellRendererButton *cell,
544 PsppireVarSheet *var_sheet)
546 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
547 struct missing_values mv;
548 struct variable *var;
550 var = path_string_to_variable (var_sheet, path);
551 g_return_if_fail (var != NULL);
553 psppire_missing_val_dialog_run (GTK_WINDOW (toplevel), var, &mv);
554 var_set_missing_values (var, &mv);
559 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
563 g_object_set (G_OBJECT (renderer),
564 PSPPIRE_IS_CELL_RENDERER_BUTTON (renderer) ? "label" : "text",
565 string, (void *) NULL);
566 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
567 NULL, NULL, NULL, &width, NULL);
572 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
579 ds_put_byte_multiple (&s, '0', char_cnt);
580 ds_put_byte (&s, ' ');
581 width = get_string_width (treeview, renderer, ds_cstr (&s));
587 static PsppSheetViewColumn *
588 add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer,
589 enum vs_column column_id,
590 const char *title, int width)
592 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
593 int title_width, content_width;
594 PsppSheetViewColumn *column;
596 column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL);
597 g_object_set_data (G_OBJECT (column), "column-number",
598 GINT_TO_POINTER (column_id) + 1);
600 pspp_sheet_view_column_set_cell_data_func (
601 column, renderer, render_var_cell, var_sheet, NULL);
603 title_width = get_string_width (sheet_view, renderer, title);
604 content_width = get_monospace_width (sheet_view, renderer, width);
605 g_object_set_data (G_OBJECT (column), "content-width",
606 GINT_TO_POINTER (content_width));
608 pspp_sheet_view_column_set_fixed_width (column,
609 MAX (title_width, content_width));
610 pspp_sheet_view_column_set_resizable (column, TRUE);
612 pspp_sheet_view_append_column (sheet_view, column);
614 g_signal_connect (renderer, "edited",
615 G_CALLBACK (on_var_column_edited),
617 g_object_set_data (G_OBJECT (renderer), "column-id",
618 GINT_TO_POINTER (column_id));
619 g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet);
624 static PsppSheetViewColumn *
625 add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
626 const char *title, int width)
628 return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (),
629 column_id, title, width);
632 static PsppSheetViewColumn *
633 add_spin_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_spin_new (),
637 column_id, title, width);
640 static PsppSheetViewColumn *
641 add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
642 const char *title, int width,
645 GtkCellRenderer *cell;
650 store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
651 va_start (args, width);
652 while ((name = va_arg (args, const char *)) != NULL)
654 int value = va_arg (args, int);
655 gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
662 cell = gtk_cell_renderer_combo_new ();
665 "model", GTK_TREE_MODEL (store),
669 return add_var_sheet_column (var_sheet, cell, column_id, title, width);
674 add_popup_menu (PsppireVarSheet *var_sheet,
675 PsppSheetViewColumn *column,
676 void (*on_click) (PsppireCellRendererButton *,
678 PsppireVarSheet *var_sheet))
680 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
681 const char *button_label = "...";
682 GtkCellRenderer *button_renderer;
685 button_renderer = psppire_cell_renderer_button_new ();
686 g_object_set (button_renderer,
687 "label", button_label,
690 g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click),
692 pspp_sheet_view_column_pack_start (column, button_renderer, FALSE);
693 pspp_sheet_view_column_set_cell_data_func (
694 column, button_renderer, render_popup_cell, var_sheet, NULL);
696 content_width = GPOINTER_TO_INT (g_object_get_data (
697 G_OBJECT (column), "content-width"));
698 content_width += get_string_width (sheet_view, button_renderer,
700 if (content_width > pspp_sheet_view_column_get_fixed_width (column))
701 pspp_sheet_view_column_set_fixed_width (column, content_width);
705 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
706 gint wx, gint wy, size_t *row, size_t *column)
708 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
712 PsppSheetViewColumn *tree_column;
713 GtkTreeModel *tree_model;
717 /* Check that WIDGET is really visible on the screen before we
718 do anything else. This is a bug fix for a sticky situation:
719 when text_data_import_assistant() returns, it frees the data
720 necessary to compose the tool tip message, but there may be
721 a tool tip under preparation at that point (even if there is
722 no visible tool tip) that will call back into us a little
723 bit later. Perhaps the correct solution to this problem is
724 to make the data related to the tool tips part of a GObject
725 that only gets destroyed when all references are released,
726 but this solution appears to be effective too. */
727 if (!gtk_widget_get_mapped (widget))
730 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
732 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
733 &path, &tree_column, NULL, NULL))
736 column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number");
737 if (column_ptr == NULL)
739 *column = GPOINTER_TO_INT (column_ptr) - 1;
741 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
744 tree_model = pspp_sheet_view_get_model (tree_view);
745 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
746 gtk_tree_path_free (path);
750 *row = GPOINTER_TO_INT (iter.user_data);
755 on_query_var_tooltip (GtkWidget *widget, gint wx, gint wy,
756 gboolean keyboard_mode UNUSED,
757 GtkTooltip *tooltip, gpointer *user_data UNUSED)
759 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
761 struct variable *var;
764 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
767 dict = psppire_var_sheet_get_dictionary (var_sheet);
768 g_return_val_if_fail (dict != NULL, FALSE);
770 if (row >= psppire_dict_get_var_cnt (dict))
772 gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a "
777 var = psppire_dict_get_variable (dict, row);
778 g_return_val_if_fail (var != NULL, FALSE);
784 char text[FMT_STRING_LEN_MAX + 1];
786 fmt_to_string (var_get_print_format (var), text);
787 gtk_tooltip_set_text (tooltip, text);
792 if (var_has_value_labels (var))
794 const struct val_labs *vls = var_get_value_labels (var);
795 const struct val_lab **labels = val_labs_sorted (vls);
800 for (i = 0; i < val_labs_count (vls); i++)
802 const struct val_lab *vl = labels[i];
805 if (i >= 10 || ds_length (&s) > 500)
807 ds_put_cstr (&s, "...");
811 vstr = value_to_text (vl->value, var);
812 ds_put_format (&s, _("{%s, %s}\n"), vstr,
813 val_lab_get_escaped_label (vl));
817 ds_chomp_byte (&s, '\n');
819 gtk_tooltip_set_text (tooltip, ds_cstr (&s));
831 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
833 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
836 menu = get_widget_assert (var_sheet->builder, "varsheet-variable-popup");
837 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
841 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
843 do_popup_menu (widget, 0, gtk_get_current_event_time ());
847 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
848 gpointer user_data UNUSED)
850 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
852 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
854 PsppSheetSelection *selection;
856 selection = pspp_sheet_view_get_selection (sheet_view);
857 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
861 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
862 &path, NULL, NULL, NULL))
864 pspp_sheet_selection_unselect_all (selection);
865 pspp_sheet_selection_select_path (selection, path);
866 gtk_tree_path_free (path);
870 do_popup_menu (widget, event->button, event->time);
878 psppire_fmt_use_get_type (void)
880 static GType etype = 0;
883 static const GEnumValue values[] =
885 { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
886 { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
890 etype = g_enum_register_static
891 (g_intern_static_string ("PsppireFmtUse"), values);
900 PROP_MAY_CREATE_VARS,
901 PROP_MAY_DELETE_VARS,
907 psppire_var_sheet_set_property (GObject *object,
912 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
916 case PROP_DICTIONARY:
917 psppire_var_sheet_set_dictionary (obj,
918 PSPPIRE_DICT (g_value_get_object (
922 case PROP_MAY_CREATE_VARS:
923 psppire_var_sheet_set_may_create_vars (obj,
924 g_value_get_boolean (value));
927 case PROP_MAY_DELETE_VARS:
928 psppire_var_sheet_set_may_delete_vars (obj,
929 g_value_get_boolean (value));
932 case PROP_FORMAT_TYPE:
933 obj->format_use = g_value_get_enum (value);
936 case PROP_UI_MANAGER:
938 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
944 psppire_var_sheet_get_property (GObject *object,
949 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
953 case PROP_DICTIONARY:
954 g_value_set_object (value,
955 G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
958 case PROP_MAY_CREATE_VARS:
959 g_value_set_boolean (value, obj->may_create_vars);
962 case PROP_MAY_DELETE_VARS:
963 g_value_set_boolean (value, obj->may_delete_vars);
966 case PROP_FORMAT_TYPE:
967 g_value_set_enum (value, obj->format_use);
970 case PROP_UI_MANAGER:
971 g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj));
975 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
981 psppire_var_sheet_dispose (GObject *obj)
983 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
986 if (var_sheet->dispose_has_run)
989 var_sheet->dispose_has_run = TRUE;
991 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
992 if ( var_sheet->dict_signals[i])
993 g_signal_handler_disconnect (var_sheet->dict,
994 var_sheet->dict_signals[i]);
997 g_object_unref (var_sheet->dict);
1000 g_object_unref (var_sheet->uim);
1002 /* These dialogs are not GObjects (although they should be!)
1003 But for now, unreffing them only causes a GCritical Error
1004 so comment them out for now. (and accept the memory leakage)
1006 g_object_unref (var_sheet->val_labs_dialog);
1007 g_object_unref (var_sheet->missing_val_dialog);
1008 g_object_unref (var_sheet->var_type_dialog);
1011 G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
1015 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
1017 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1020 gobject_class->set_property = psppire_var_sheet_set_property;
1021 gobject_class->get_property = psppire_var_sheet_get_property;
1022 gobject_class->dispose = psppire_var_sheet_dispose;
1024 g_signal_new ("var-double-clicked",
1025 G_OBJECT_CLASS_TYPE (gobject_class),
1028 g_signal_accumulator_true_handled, NULL,
1029 psppire_marshal_BOOLEAN__INT,
1030 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1032 pspec = g_param_spec_object ("dictionary",
1033 "Dictionary displayed by the sheet",
1034 "The PsppireDict that the sheet displays "
1035 "may allow the user to edit",
1038 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
1040 pspec = g_param_spec_boolean ("may-create-vars",
1041 "May create variables",
1042 "Whether the user may create more variables",
1045 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
1047 pspec = g_param_spec_boolean ("may-delete-vars",
1048 "May delete variables",
1049 "Whether the user may delete variables",
1052 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
1054 pspec = g_param_spec_enum ("format-use",
1055 "Use of variable format",
1056 ("Whether variables have input or output "
1058 PSPPIRE_TYPE_FMT_USE,
1061 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
1063 pspec = g_param_spec_object ("ui-manager",
1065 "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.",
1066 GTK_TYPE_UI_MANAGER,
1068 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1072 render_row_number_cell (PsppSheetViewColumn *tree_column,
1073 GtkCellRenderer *cell,
1074 GtkTreeModel *model,
1078 PsppireVarSheet *var_sheet = user_data;
1079 GValue gvalue = { 0, };
1082 row = GPOINTER_TO_INT (iter->user_data);
1084 g_value_init (&gvalue, G_TYPE_INT);
1085 g_value_set_int (&gvalue, row + 1);
1086 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1087 g_value_unset (&gvalue);
1089 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1090 g_object_set (cell, "editable", TRUE, NULL);
1092 g_object_set (cell, "editable", FALSE, NULL);
1096 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1098 PsppireVarSheet *var_sheet)
1102 g_return_if_fail (var_sheet->dict != NULL);
1104 path = gtk_tree_path_new_from_string (path_string);
1105 if (gtk_tree_path_get_depth (path) == 1)
1107 gint *indices = gtk_tree_path_get_indices (path);
1108 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1111 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1112 indices[0], &handled);
1115 gtk_tree_path_free (path);
1119 psppire_var_sheet_variables_column_clicked (PsppSheetViewColumn *column,
1120 PsppireVarSheet *var_sheet)
1122 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1123 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1125 pspp_sheet_selection_select_all (selection);
1128 static PsppSheetViewColumn *
1129 make_row_number_column (PsppireVarSheet *var_sheet)
1131 PsppSheetViewColumn *column;
1132 GtkCellRenderer *renderer;
1134 renderer = psppire_cell_renderer_button_new ();
1135 g_object_set (renderer, "xalign", 1.0, NULL);
1136 g_signal_connect (renderer, "double-clicked",
1137 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1140 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1142 pspp_sheet_view_column_set_clickable (column, TRUE);
1143 pspp_sheet_view_column_set_cell_data_func (
1144 column, renderer, render_row_number_cell, var_sheet, NULL);
1145 pspp_sheet_view_column_set_fixed_width (column, 50);
1146 g_signal_connect (column, "clicked",
1147 G_CALLBACK (psppire_var_sheet_variables_column_clicked),
1154 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1156 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1157 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1158 PsppireDict *dict = var_sheet->dict;
1159 const struct range_set_node *node;
1160 struct range_set *selected;
1162 selected = pspp_sheet_selection_get_range_set (selection);
1163 for (node = range_set_last (selected); node != NULL;
1164 node = range_set_prev (selected, node))
1168 for (i = 1; i <= range_set_node_get_width (node); i++)
1170 unsigned long row = range_set_node_get_end (node) - i;
1171 if (row >= 0 && row < psppire_dict_get_var_cnt (dict))
1172 psppire_dict_delete_variables (dict, row, 1);
1175 range_set_destroy (selected);
1179 on_selection_changed (PsppSheetSelection *selection,
1180 gpointer user_data UNUSED)
1182 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1183 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1184 gint n_selected_rows;
1185 gboolean may_delete;
1189 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1191 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1192 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1193 && n_selected_rows > 0));
1195 switch (n_selected_rows)
1202 /* The row used for inserting new variables cannot be deleted. */
1203 path = gtk_tree_path_new_from_indices (
1204 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1205 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1206 gtk_tree_path_free (path);
1213 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1214 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1218 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1220 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1221 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1222 PsppireDict *dict = var_sheet->dict;
1223 struct range_set *selected;
1226 selected = pspp_sheet_selection_get_range_set (selection);
1227 row = range_set_scan (selected, 0);
1228 range_set_destroy (selected);
1230 if (row <= psppire_dict_get_var_cnt (dict))
1233 if (psppire_dict_generate_name (dict, name, sizeof name))
1234 psppire_dict_insert_variable (dict, row, name);
1239 psppire_var_sheet_init (PsppireVarSheet *obj)
1241 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1242 PsppSheetViewColumn *column;
1243 GtkCellRenderer *cell;
1248 obj->format_use = FMT_FOR_OUTPUT;
1249 obj->may_create_vars = TRUE;
1250 obj->may_delete_vars = TRUE;
1252 obj->scroll_to_bottom_signal = 0;
1254 obj->container = NULL;
1255 obj->dispose_has_run = FALSE;
1258 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1260 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1261 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1262 g_signal_connect (list->data, "editing-started",
1263 G_CALLBACK (on_name_column_editing_started), NULL);
1266 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1267 add_popup_menu (obj, column, on_type_click);
1269 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1271 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1273 add_text_column (obj, VS_LABEL, _("Label"), 20);
1275 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1276 add_popup_menu (obj, column, on_value_labels_click);
1278 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1279 add_popup_menu (obj, column, on_missing_values_click);
1281 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1284 = add_combo_column (obj, VS_ALIGN, _("Align"), 8,
1285 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1286 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1287 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1289 cell = gtk_cell_renderer_pixbuf_new ();
1290 g_object_set (cell, "width", 16, "height", 16, NULL);
1291 pspp_sheet_view_column_pack_end (column, cell, FALSE);
1292 pspp_sheet_view_column_set_cell_data_func (
1293 column, cell, render_var_cell, obj, NULL);
1296 = add_combo_column (obj, VS_MEASURE, _("Measure"), 12,
1297 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1298 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1299 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1301 cell = gtk_cell_renderer_pixbuf_new ();
1302 g_object_set (cell, "width", 16, "height", 16, NULL);
1303 pspp_sheet_view_column_pack_end (column, cell, FALSE);
1304 pspp_sheet_view_column_set_cell_data_func (
1305 column, cell, render_var_cell, obj, NULL);
1307 add_combo_column (obj, VS_ROLE, _("Role"), 12,
1308 var_role_to_string (ROLE_INPUT), ROLE_INPUT,
1309 var_role_to_string (ROLE_OUTPUT), ROLE_OUTPUT,
1310 var_role_to_string (ROLE_BOTH), ROLE_BOTH,
1311 var_role_to_string (ROLE_NONE), ROLE_NONE,
1312 var_role_to_string (ROLE_PARTITION), ROLE_PARTITION,
1313 var_role_to_string (ROLE_SPLIT), ROLE_SPLIT,
1316 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1317 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1318 PSPP_SHEET_SELECTION_MULTIPLE);
1320 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1321 g_signal_connect (obj, "query-tooltip",
1322 G_CALLBACK (on_query_var_tooltip), NULL);
1323 g_signal_connect (obj, "button-press-event",
1324 G_CALLBACK (on_button_pressed), NULL);
1325 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1327 obj->builder = builder_new ("var-sheet.ui");
1329 action = get_action_assert (obj->builder, "edit_clear-variables");
1330 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1332 gtk_action_set_sensitive (action, FALSE);
1333 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1334 "changed", G_CALLBACK (on_selection_changed), NULL);
1336 action = get_action_assert (obj->builder, "edit_insert-variable");
1337 gtk_action_set_sensitive (action, FALSE);
1338 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1343 psppire_var_sheet_new (void)
1345 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1349 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1351 return var_sheet->dict;
1355 refresh_model (PsppireVarSheet *var_sheet)
1357 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1359 if (var_sheet->dict != NULL)
1361 PsppireEmptyListStore *store;
1364 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1365 + var_sheet->may_create_vars);
1366 store = psppire_empty_list_store_new (n_rows);
1367 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1368 GTK_TREE_MODEL (store));
1369 g_object_unref (store);
1374 on_var_changed (PsppireDict *dict, glong row,
1375 guint what, const struct variable *oldvar,
1376 PsppireVarSheet *var_sheet)
1378 PsppireEmptyListStore *store;
1380 g_return_if_fail (dict == var_sheet->dict);
1382 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1383 PSPP_SHEET_VIEW (var_sheet)));
1384 g_return_if_fail (store != NULL);
1386 psppire_empty_list_store_row_changed (store, row);
1390 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1392 PsppireEmptyListStore *store;
1395 g_return_if_fail (dict == var_sheet->dict);
1397 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1398 PSPP_SHEET_VIEW (var_sheet)));
1399 g_return_if_fail (store != NULL);
1401 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1402 + var_sheet->may_create_vars);
1403 psppire_empty_list_store_set_n_rows (store, n_rows);
1404 psppire_empty_list_store_row_inserted (store, row);
1408 on_var_deleted (PsppireDict *dict,
1409 const struct variable *var, int dict_idx, int case_idx,
1410 PsppireVarSheet *var_sheet)
1412 PsppireEmptyListStore *store;
1415 g_return_if_fail (dict == var_sheet->dict);
1417 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1418 PSPP_SHEET_VIEW (var_sheet)));
1419 g_return_if_fail (store != NULL);
1421 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1422 + var_sheet->may_create_vars);
1423 psppire_empty_list_store_set_n_rows (store, n_rows);
1424 psppire_empty_list_store_row_deleted (store, dict_idx);
1428 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1430 g_return_if_fail (dict == var_sheet->dict);
1431 refresh_model (var_sheet);
1435 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1438 if (var_sheet->dict != NULL)
1442 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1444 if (var_sheet->dict_signals[i])
1445 g_signal_handler_disconnect (var_sheet->dict,
1446 var_sheet->dict_signals[i]);
1448 var_sheet->dict_signals[i] = 0;
1451 g_object_unref (var_sheet->dict);
1454 var_sheet->dict = dict;
1458 g_object_ref (dict);
1460 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1461 = g_signal_connect (dict, "backend-changed",
1462 G_CALLBACK (on_backend_changed), var_sheet);
1464 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED]
1465 = g_signal_connect (dict, "variable-changed",
1466 G_CALLBACK (on_var_changed), var_sheet);
1468 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1469 = g_signal_connect (dict, "variable-inserted",
1470 G_CALLBACK (on_var_inserted), var_sheet);
1472 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1473 = g_signal_connect (dict, "variable-deleted",
1474 G_CALLBACK (on_var_deleted), var_sheet);
1477 refresh_model (var_sheet);
1481 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1483 return var_sheet->may_create_vars;
1487 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1488 gboolean may_create_vars)
1490 if (var_sheet->may_create_vars != may_create_vars)
1492 PsppireEmptyListStore *store;
1495 var_sheet->may_create_vars = may_create_vars;
1497 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1498 PSPP_SHEET_VIEW (var_sheet)));
1499 g_return_if_fail (store != NULL);
1501 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1502 + var_sheet->may_create_vars);
1503 psppire_empty_list_store_set_n_rows (store, n_rows);
1505 if (may_create_vars)
1506 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1508 psppire_empty_list_store_row_deleted (store, n_rows);
1510 on_selection_changed (pspp_sheet_view_get_selection (
1511 PSPP_SHEET_VIEW (var_sheet)), NULL);
1516 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1518 return var_sheet->may_delete_vars;
1522 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1523 gboolean may_delete_vars)
1525 if (var_sheet->may_delete_vars != may_delete_vars)
1527 var_sheet->may_delete_vars = may_delete_vars;
1528 on_selection_changed (pspp_sheet_view_get_selection (
1529 PSPP_SHEET_VIEW (var_sheet)), NULL);
1534 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1536 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1539 path = gtk_tree_path_new_from_indices (dict_index, -1);
1540 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1541 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1542 gtk_tree_path_free (path);
1546 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1548 if (var_sheet->uim == NULL)
1550 var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1552 GTK_TYPE_UI_MANAGER));
1553 g_object_ref (var_sheet->uim);
1556 return var_sheet->uim;