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_NONE)))
281 var_set_role (var, ROLE_NONE);
282 else if (!strcmp (new_text, var_role_to_string (ROLE_INPUT)))
283 var_set_role (var, ROLE_INPUT);
284 else if (!strcmp (new_text, var_role_to_string (ROLE_OUTPUT)))
285 var_set_role (var, ROLE_OUTPUT);
286 else if (!strcmp (new_text, var_role_to_string (ROLE_BOTH)))
287 var_set_role (var, ROLE_BOTH);
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)),
467 g_object_set (cell, "stock-id",
468 psppire_dict_view_get_var_measurement_stock_id (var),
474 "text", var_role_to_string (var_get_role (var)),
481 static struct variable *
482 path_string_to_variable (PsppireVarSheet *var_sheet, gchar *path_string)
488 path = gtk_tree_path_new_from_string (path_string);
489 row = gtk_tree_path_get_indices (path)[0];
490 gtk_tree_path_free (path);
492 dict = psppire_var_sheet_get_dictionary (var_sheet);
493 g_return_val_if_fail (dict != NULL, NULL);
495 return psppire_dict_get_variable (dict, row);
499 on_type_click (PsppireCellRendererButton *cell,
501 PsppireVarSheet *var_sheet)
503 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
504 struct fmt_spec format;
505 struct variable *var;
507 var = path_string_to_variable (var_sheet, path);
508 g_return_if_fail (var != NULL);
510 format = *var_get_print_format (var);
511 psppire_var_type_dialog_run (GTK_WINDOW (toplevel), &format);
513 var_set_width_and_formats (var, fmt_var_width (&format), &format, &format);
517 on_value_labels_click (PsppireCellRendererButton *cell,
519 PsppireVarSheet *var_sheet)
521 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
522 struct val_labs *labels;
523 struct variable *var;
525 var = path_string_to_variable (var_sheet, path);
526 g_return_if_fail (var != NULL);
528 labels = psppire_val_labs_dialog_run (GTK_WINDOW (toplevel), var);
531 var_set_value_labels (var, labels);
532 val_labs_destroy (labels);
537 on_missing_values_click (PsppireCellRendererButton *cell,
539 PsppireVarSheet *var_sheet)
541 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
542 struct missing_values mv;
543 struct variable *var;
545 var = path_string_to_variable (var_sheet, path);
546 g_return_if_fail (var != NULL);
548 psppire_missing_val_dialog_run (GTK_WINDOW (toplevel), var, &mv);
549 var_set_missing_values (var, &mv);
554 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
558 g_object_set (G_OBJECT (renderer),
559 PSPPIRE_IS_CELL_RENDERER_BUTTON (renderer) ? "label" : "text",
560 string, (void *) NULL);
561 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
562 NULL, NULL, NULL, &width, NULL);
567 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
574 ds_put_byte_multiple (&s, '0', char_cnt);
575 ds_put_byte (&s, ' ');
576 width = get_string_width (treeview, renderer, ds_cstr (&s));
582 static PsppSheetViewColumn *
583 add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer,
584 enum vs_column column_id,
585 const char *title, int width)
587 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
588 int title_width, content_width;
589 PsppSheetViewColumn *column;
591 column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL);
592 g_object_set_data (G_OBJECT (column), "column-number",
593 GINT_TO_POINTER (column_id) + 1);
595 pspp_sheet_view_column_set_cell_data_func (
596 column, renderer, render_var_cell, var_sheet, NULL);
598 title_width = get_string_width (sheet_view, renderer, title);
599 content_width = get_monospace_width (sheet_view, renderer, width);
600 g_object_set_data (G_OBJECT (column), "content-width",
601 GINT_TO_POINTER (content_width));
603 pspp_sheet_view_column_set_fixed_width (column,
604 MAX (title_width, content_width));
605 pspp_sheet_view_column_set_resizable (column, TRUE);
607 pspp_sheet_view_append_column (sheet_view, column);
609 g_signal_connect (renderer, "edited",
610 G_CALLBACK (on_var_column_edited),
612 g_object_set_data (G_OBJECT (renderer), "column-id",
613 GINT_TO_POINTER (column_id));
614 g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet);
619 static PsppSheetViewColumn *
620 add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
621 const char *title, int width)
623 return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (),
624 column_id, title, width);
627 static PsppSheetViewColumn *
628 add_spin_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
629 const char *title, int width)
631 return add_var_sheet_column (var_sheet, gtk_cell_renderer_spin_new (),
632 column_id, title, width);
635 static PsppSheetViewColumn *
636 add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
637 const char *title, int width,
640 GtkCellRenderer *cell;
645 store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
646 va_start (args, width);
647 while ((name = va_arg (args, const char *)) != NULL)
649 int value = va_arg (args, int);
650 gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
657 cell = gtk_cell_renderer_combo_new ();
660 "model", GTK_TREE_MODEL (store),
664 return add_var_sheet_column (var_sheet, cell, column_id, title, width);
669 add_popup_menu (PsppireVarSheet *var_sheet,
670 PsppSheetViewColumn *column,
671 void (*on_click) (PsppireCellRendererButton *,
673 PsppireVarSheet *var_sheet))
675 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
676 const char *button_label = "...";
677 GtkCellRenderer *button_renderer;
680 button_renderer = psppire_cell_renderer_button_new ();
681 g_object_set (button_renderer,
682 "label", button_label,
685 g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click),
687 pspp_sheet_view_column_pack_start (column, button_renderer, FALSE);
688 pspp_sheet_view_column_set_cell_data_func (
689 column, button_renderer, render_popup_cell, var_sheet, NULL);
691 content_width = GPOINTER_TO_INT (g_object_get_data (
692 G_OBJECT (column), "content-width"));
693 content_width += get_string_width (sheet_view, button_renderer,
695 if (content_width > pspp_sheet_view_column_get_fixed_width (column))
696 pspp_sheet_view_column_set_fixed_width (column, content_width);
700 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
701 gint wx, gint wy, size_t *row, size_t *column)
703 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
707 PsppSheetViewColumn *tree_column;
708 GtkTreeModel *tree_model;
712 /* Check that WIDGET is really visible on the screen before we
713 do anything else. This is a bug fix for a sticky situation:
714 when text_data_import_assistant() returns, it frees the data
715 necessary to compose the tool tip message, but there may be
716 a tool tip under preparation at that point (even if there is
717 no visible tool tip) that will call back into us a little
718 bit later. Perhaps the correct solution to this problem is
719 to make the data related to the tool tips part of a GObject
720 that only gets destroyed when all references are released,
721 but this solution appears to be effective too. */
722 if (!gtk_widget_get_mapped (widget))
725 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
727 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
728 &path, &tree_column, NULL, NULL))
731 column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number");
732 if (column_ptr == NULL)
734 *column = GPOINTER_TO_INT (column_ptr) - 1;
736 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
739 tree_model = pspp_sheet_view_get_model (tree_view);
740 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
741 gtk_tree_path_free (path);
745 *row = GPOINTER_TO_INT (iter.user_data);
750 on_query_var_tooltip (GtkWidget *widget, gint wx, gint wy,
751 gboolean keyboard_mode UNUSED,
752 GtkTooltip *tooltip, gpointer *user_data UNUSED)
754 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
756 struct variable *var;
759 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
762 dict = psppire_var_sheet_get_dictionary (var_sheet);
763 g_return_val_if_fail (dict != NULL, FALSE);
765 if (row >= psppire_dict_get_var_cnt (dict))
767 gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a "
772 var = psppire_dict_get_variable (dict, row);
773 g_return_val_if_fail (var != NULL, FALSE);
779 char text[FMT_STRING_LEN_MAX + 1];
781 fmt_to_string (var_get_print_format (var), text);
782 gtk_tooltip_set_text (tooltip, text);
787 if (var_has_value_labels (var))
789 const struct val_labs *vls = var_get_value_labels (var);
790 const struct val_lab **labels = val_labs_sorted (vls);
795 for (i = 0; i < val_labs_count (vls); i++)
797 const struct val_lab *vl = labels[i];
800 if (i >= 10 || ds_length (&s) > 500)
802 ds_put_cstr (&s, "...");
806 vstr = value_to_text (vl->value, var);
807 ds_put_format (&s, _("{%s, %s}\n"), vstr,
808 val_lab_get_escaped_label (vl));
812 ds_chomp_byte (&s, '\n');
814 gtk_tooltip_set_text (tooltip, ds_cstr (&s));
826 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
828 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
831 menu = get_widget_assert (var_sheet->builder, "varsheet-variable-popup");
832 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
836 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
838 do_popup_menu (widget, 0, gtk_get_current_event_time ());
842 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
843 gpointer user_data UNUSED)
845 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
847 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
849 PsppSheetSelection *selection;
851 selection = pspp_sheet_view_get_selection (sheet_view);
852 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
856 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
857 &path, NULL, NULL, NULL))
859 pspp_sheet_selection_unselect_all (selection);
860 pspp_sheet_selection_select_path (selection, path);
861 gtk_tree_path_free (path);
865 do_popup_menu (widget, event->button, event->time);
873 psppire_fmt_use_get_type (void)
875 static GType etype = 0;
878 static const GEnumValue values[] =
880 { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
881 { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
885 etype = g_enum_register_static
886 (g_intern_static_string ("PsppireFmtUse"), values);
895 PROP_MAY_CREATE_VARS,
896 PROP_MAY_DELETE_VARS,
902 psppire_var_sheet_set_property (GObject *object,
907 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
911 case PROP_DICTIONARY:
912 psppire_var_sheet_set_dictionary (obj,
913 PSPPIRE_DICT (g_value_get_object (
917 case PROP_MAY_CREATE_VARS:
918 psppire_var_sheet_set_may_create_vars (obj,
919 g_value_get_boolean (value));
922 case PROP_MAY_DELETE_VARS:
923 psppire_var_sheet_set_may_delete_vars (obj,
924 g_value_get_boolean (value));
927 case PROP_FORMAT_TYPE:
928 obj->format_use = g_value_get_enum (value);
931 case PROP_UI_MANAGER:
933 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
939 psppire_var_sheet_get_property (GObject *object,
944 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
948 case PROP_DICTIONARY:
949 g_value_set_object (value,
950 G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
953 case PROP_MAY_CREATE_VARS:
954 g_value_set_boolean (value, obj->may_create_vars);
957 case PROP_MAY_DELETE_VARS:
958 g_value_set_boolean (value, obj->may_delete_vars);
961 case PROP_FORMAT_TYPE:
962 g_value_set_enum (value, obj->format_use);
965 case PROP_UI_MANAGER:
966 g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj));
970 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
976 psppire_var_sheet_dispose (GObject *obj)
978 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
981 if (var_sheet->dispose_has_run)
984 var_sheet->dispose_has_run = TRUE;
986 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
987 if ( var_sheet->dict_signals[i])
988 g_signal_handler_disconnect (var_sheet->dict,
989 var_sheet->dict_signals[i]);
992 g_object_unref (var_sheet->dict);
995 g_object_unref (var_sheet->uim);
997 /* These dialogs are not GObjects (although they should be!)
998 But for now, unreffing them only causes a GCritical Error
999 so comment them out for now. (and accept the memory leakage)
1001 g_object_unref (var_sheet->val_labs_dialog);
1002 g_object_unref (var_sheet->missing_val_dialog);
1003 g_object_unref (var_sheet->var_type_dialog);
1006 G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
1010 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
1012 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1015 gobject_class->set_property = psppire_var_sheet_set_property;
1016 gobject_class->get_property = psppire_var_sheet_get_property;
1017 gobject_class->dispose = psppire_var_sheet_dispose;
1019 g_signal_new ("var-double-clicked",
1020 G_OBJECT_CLASS_TYPE (gobject_class),
1023 g_signal_accumulator_true_handled, NULL,
1024 psppire_marshal_BOOLEAN__INT,
1025 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1027 pspec = g_param_spec_object ("dictionary",
1028 "Dictionary displayed by the sheet",
1029 "The PsppireDict that the sheet displays "
1030 "may allow the user to edit",
1033 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
1035 pspec = g_param_spec_boolean ("may-create-vars",
1036 "May create variables",
1037 "Whether the user may create more variables",
1040 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
1042 pspec = g_param_spec_boolean ("may-delete-vars",
1043 "May delete variables",
1044 "Whether the user may delete variables",
1047 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
1049 pspec = g_param_spec_enum ("format-use",
1050 "Use of variable format",
1051 ("Whether variables have input or output "
1053 PSPPIRE_TYPE_FMT_USE,
1056 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
1058 pspec = g_param_spec_object ("ui-manager",
1060 "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.",
1061 GTK_TYPE_UI_MANAGER,
1063 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1067 render_row_number_cell (PsppSheetViewColumn *tree_column,
1068 GtkCellRenderer *cell,
1069 GtkTreeModel *model,
1073 PsppireVarSheet *var_sheet = user_data;
1074 GValue gvalue = { 0, };
1077 row = GPOINTER_TO_INT (iter->user_data);
1079 g_value_init (&gvalue, G_TYPE_INT);
1080 g_value_set_int (&gvalue, row + 1);
1081 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1082 g_value_unset (&gvalue);
1084 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1085 g_object_set (cell, "editable", TRUE, NULL);
1087 g_object_set (cell, "editable", FALSE, NULL);
1091 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1093 PsppireVarSheet *var_sheet)
1097 g_return_if_fail (var_sheet->dict != NULL);
1099 path = gtk_tree_path_new_from_string (path_string);
1100 if (gtk_tree_path_get_depth (path) == 1)
1102 gint *indices = gtk_tree_path_get_indices (path);
1103 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1106 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1107 indices[0], &handled);
1110 gtk_tree_path_free (path);
1114 psppire_var_sheet_variables_column_clicked (PsppSheetViewColumn *column,
1115 PsppireVarSheet *var_sheet)
1117 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1118 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1120 pspp_sheet_selection_select_all (selection);
1123 static PsppSheetViewColumn *
1124 make_row_number_column (PsppireVarSheet *var_sheet)
1126 PsppSheetViewColumn *column;
1127 GtkCellRenderer *renderer;
1129 renderer = psppire_cell_renderer_button_new ();
1130 g_object_set (renderer, "xalign", 1.0, NULL);
1131 g_signal_connect (renderer, "double-clicked",
1132 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1135 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1137 pspp_sheet_view_column_set_clickable (column, TRUE);
1138 pspp_sheet_view_column_set_cell_data_func (
1139 column, renderer, render_row_number_cell, var_sheet, NULL);
1140 pspp_sheet_view_column_set_fixed_width (column, 50);
1141 g_signal_connect (column, "clicked",
1142 G_CALLBACK (psppire_var_sheet_variables_column_clicked),
1149 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1151 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1152 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1153 PsppireDict *dict = var_sheet->dict;
1154 const struct range_set_node *node;
1155 struct range_set *selected;
1157 selected = pspp_sheet_selection_get_range_set (selection);
1158 for (node = range_set_last (selected); node != NULL;
1159 node = range_set_prev (selected, node))
1163 for (i = 1; i <= range_set_node_get_width (node); i++)
1165 unsigned long row = range_set_node_get_end (node) - i;
1166 if (row >= 0 && row < psppire_dict_get_var_cnt (dict))
1167 psppire_dict_delete_variables (dict, row, 1);
1170 range_set_destroy (selected);
1174 on_selection_changed (PsppSheetSelection *selection,
1175 gpointer user_data UNUSED)
1177 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1178 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1179 gint n_selected_rows;
1180 gboolean may_delete;
1184 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1186 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1187 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1188 && n_selected_rows > 0));
1190 switch (n_selected_rows)
1197 /* The row used for inserting new variables cannot be deleted. */
1198 path = gtk_tree_path_new_from_indices (
1199 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1200 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1201 gtk_tree_path_free (path);
1208 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1209 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1213 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1215 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1216 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1217 PsppireDict *dict = var_sheet->dict;
1218 struct range_set *selected;
1221 selected = pspp_sheet_selection_get_range_set (selection);
1222 row = range_set_scan (selected, 0);
1223 range_set_destroy (selected);
1225 if (row <= psppire_dict_get_var_cnt (dict))
1228 if (psppire_dict_generate_name (dict, name, sizeof name))
1229 psppire_dict_insert_variable (dict, row, name);
1234 psppire_var_sheet_init (PsppireVarSheet *obj)
1236 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1237 PsppSheetViewColumn *column;
1238 GtkCellRenderer *cell;
1243 obj->format_use = FMT_FOR_OUTPUT;
1244 obj->may_create_vars = TRUE;
1245 obj->may_delete_vars = TRUE;
1247 obj->scroll_to_bottom_signal = 0;
1249 obj->container = NULL;
1250 obj->dispose_has_run = FALSE;
1253 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1255 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1256 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1257 g_signal_connect (list->data, "editing-started",
1258 G_CALLBACK (on_name_column_editing_started), NULL);
1261 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1262 add_popup_menu (obj, column, on_type_click);
1264 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1266 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1268 add_text_column (obj, VS_LABEL, _("Label"), 20);
1270 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1271 add_popup_menu (obj, column, on_value_labels_click);
1273 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1274 add_popup_menu (obj, column, on_missing_values_click);
1276 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1279 = add_combo_column (obj, VS_ALIGN, _("Align"), 8,
1280 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1281 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1282 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1284 cell = gtk_cell_renderer_pixbuf_new ();
1285 g_object_set (cell, "width", 16, "height", 16, NULL);
1286 pspp_sheet_view_column_pack_end (column, cell, FALSE);
1287 pspp_sheet_view_column_set_cell_data_func (
1288 column, cell, render_var_cell, obj, NULL);
1291 = add_combo_column (obj, VS_MEASURE, _("Measure"), 12,
1292 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1293 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1294 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1296 cell = gtk_cell_renderer_pixbuf_new ();
1297 g_object_set (cell, "width", 16, "height", 16, NULL);
1298 pspp_sheet_view_column_pack_end (column, cell, FALSE);
1299 pspp_sheet_view_column_set_cell_data_func (
1300 column, cell, render_var_cell, obj, NULL);
1302 add_combo_column (obj, VS_ROLE, _("Role"), 12,
1303 var_role_to_string (ROLE_NONE), ROLE_NONE,
1304 var_role_to_string (ROLE_INPUT), ROLE_INPUT,
1305 var_role_to_string (ROLE_OUTPUT), ROLE_OUTPUT,
1306 var_role_to_string (ROLE_BOTH), ROLE_BOTH,
1307 var_role_to_string (ROLE_PARTITION), ROLE_PARTITION,
1308 var_role_to_string (ROLE_SPLIT), ROLE_SPLIT,
1311 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1312 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1313 PSPP_SHEET_SELECTION_MULTIPLE);
1315 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1316 g_signal_connect (obj, "query-tooltip",
1317 G_CALLBACK (on_query_var_tooltip), NULL);
1318 g_signal_connect (obj, "button-press-event",
1319 G_CALLBACK (on_button_pressed), NULL);
1320 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1322 obj->builder = builder_new ("var-sheet.ui");
1324 action = get_action_assert (obj->builder, "edit_clear-variables");
1325 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1327 gtk_action_set_sensitive (action, FALSE);
1328 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1329 "changed", G_CALLBACK (on_selection_changed), NULL);
1331 action = get_action_assert (obj->builder, "edit_insert-variable");
1332 gtk_action_set_sensitive (action, FALSE);
1333 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1338 psppire_var_sheet_new (void)
1340 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1344 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1346 return var_sheet->dict;
1350 refresh_model (PsppireVarSheet *var_sheet)
1352 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1354 if (var_sheet->dict != NULL)
1356 PsppireEmptyListStore *store;
1359 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1360 + var_sheet->may_create_vars);
1361 store = psppire_empty_list_store_new (n_rows);
1362 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1363 GTK_TREE_MODEL (store));
1364 g_object_unref (store);
1369 on_var_changed (PsppireDict *dict, glong row,
1370 guint what, const struct variable *oldvar,
1371 PsppireVarSheet *var_sheet)
1373 PsppireEmptyListStore *store;
1375 g_return_if_fail (dict == var_sheet->dict);
1377 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1378 PSPP_SHEET_VIEW (var_sheet)));
1379 g_return_if_fail (store != NULL);
1381 psppire_empty_list_store_row_changed (store, row);
1385 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1387 PsppireEmptyListStore *store;
1390 g_return_if_fail (dict == var_sheet->dict);
1392 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1393 PSPP_SHEET_VIEW (var_sheet)));
1394 g_return_if_fail (store != NULL);
1396 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1397 + var_sheet->may_create_vars);
1398 psppire_empty_list_store_set_n_rows (store, n_rows);
1399 psppire_empty_list_store_row_inserted (store, row);
1403 on_var_deleted (PsppireDict *dict,
1404 const struct variable *var, int dict_idx, int case_idx,
1405 PsppireVarSheet *var_sheet)
1407 PsppireEmptyListStore *store;
1410 g_return_if_fail (dict == var_sheet->dict);
1412 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1413 PSPP_SHEET_VIEW (var_sheet)));
1414 g_return_if_fail (store != NULL);
1416 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1417 + var_sheet->may_create_vars);
1418 psppire_empty_list_store_set_n_rows (store, n_rows);
1419 psppire_empty_list_store_row_deleted (store, dict_idx);
1423 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1425 g_return_if_fail (dict == var_sheet->dict);
1426 refresh_model (var_sheet);
1430 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1433 if (var_sheet->dict != NULL)
1437 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1439 if (var_sheet->dict_signals[i])
1440 g_signal_handler_disconnect (var_sheet->dict,
1441 var_sheet->dict_signals[i]);
1443 var_sheet->dict_signals[i] = 0;
1446 g_object_unref (var_sheet->dict);
1449 var_sheet->dict = dict;
1453 g_object_ref (dict);
1455 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1456 = g_signal_connect (dict, "backend-changed",
1457 G_CALLBACK (on_backend_changed), var_sheet);
1459 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED]
1460 = g_signal_connect (dict, "variable-changed",
1461 G_CALLBACK (on_var_changed), var_sheet);
1463 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1464 = g_signal_connect (dict, "variable-inserted",
1465 G_CALLBACK (on_var_inserted), var_sheet);
1467 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1468 = g_signal_connect (dict, "variable-deleted",
1469 G_CALLBACK (on_var_deleted), var_sheet);
1472 refresh_model (var_sheet);
1476 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1478 return var_sheet->may_create_vars;
1482 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1483 gboolean may_create_vars)
1485 if (var_sheet->may_create_vars != may_create_vars)
1487 PsppireEmptyListStore *store;
1490 var_sheet->may_create_vars = may_create_vars;
1492 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1493 PSPP_SHEET_VIEW (var_sheet)));
1494 g_return_if_fail (store != NULL);
1496 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1497 + var_sheet->may_create_vars);
1498 psppire_empty_list_store_set_n_rows (store, n_rows);
1500 if (may_create_vars)
1501 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1503 psppire_empty_list_store_row_deleted (store, n_rows);
1505 on_selection_changed (pspp_sheet_view_get_selection (
1506 PSPP_SHEET_VIEW (var_sheet)), NULL);
1511 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1513 return var_sheet->may_delete_vars;
1517 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1518 gboolean may_delete_vars)
1520 if (var_sheet->may_delete_vars != may_delete_vars)
1522 var_sheet->may_delete_vars = may_delete_vars;
1523 on_selection_changed (pspp_sheet_view_get_selection (
1524 PSPP_SHEET_VIEW (var_sheet)), NULL);
1529 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1531 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1534 path = gtk_tree_path_new_from_indices (dict_index, -1);
1535 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1536 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1537 gtk_tree_path_free (path);
1541 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1543 if (var_sheet->uim == NULL)
1545 var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1547 GTK_TYPE_UI_MANAGER));
1548 g_object_ref (var_sheet->uim);
1551 return var_sheet->uim;