1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2011, 2012 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-empty-list-store.h"
33 #include "ui/gui/psppire-marshal.h"
34 #include "ui/gui/val-labs-dialog.h"
35 #include "ui/gui/var-type-dialog.h"
36 #include "ui/gui/var-display.h"
37 #include "ui/gui/var-type-dialog.h"
39 #include "gl/intprops.h"
42 #define _(msgid) gettext (msgid)
43 #define N_(msgid) msgid
59 G_DEFINE_TYPE (PsppireVarSheet, psppire_var_sheet, PSPP_TYPE_SHEET_VIEW);
62 set_spin_cell (GtkCellRenderer *cell, int value, int min, int max, int step)
64 char text[INT_BUFSIZE_BOUND (int)];
65 GtkAdjustment *adjust;
68 adjust = GTK_ADJUSTMENT (gtk_adjustment_new (value, min, max,
73 sprintf (text, "%d", value);
82 error_dialog (GtkWindow *w, gchar *primary_text, gchar *secondary_text)
85 gtk_message_dialog_new (w,
86 GTK_DIALOG_DESTROY_WITH_PARENT,
88 GTK_BUTTONS_CLOSE, "%s", primary_text);
90 g_object_set (dialog, "icon-name", "psppicon", NULL);
92 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
93 "%s", secondary_text);
95 gtk_dialog_run (GTK_DIALOG (dialog));
97 gtk_widget_destroy (dialog);
101 on_name_column_editing_started (GtkCellRenderer *cell,
102 GtkCellEditable *editable,
106 PsppireVarSheet *var_sheet = g_object_get_data (G_OBJECT (cell), "var-sheet");
107 PsppireDict *dict = psppire_var_sheet_get_dictionary (var_sheet);
109 g_return_if_fail (var_sheet);
110 g_return_if_fail (dict);
112 if (GTK_IS_ENTRY (editable))
114 GtkEntry *entry = GTK_ENTRY (editable);
115 if (gtk_entry_get_text (entry)[0] == '\0')
118 if (psppire_dict_generate_name (dict, name, sizeof name))
119 gtk_entry_set_text (entry, name);
125 scroll_to_bottom (GtkWidget *widget,
126 GtkRequisition *requisition,
127 gpointer unused UNUSED)
129 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
130 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
131 GtkAdjustment *vadjust;
133 vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
134 gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
136 if (var_sheet->scroll_to_bottom_signal)
138 g_signal_handler_disconnect (var_sheet,
139 var_sheet->scroll_to_bottom_signal);
140 var_sheet->scroll_to_bottom_signal = 0;
145 on_var_column_edited (GtkCellRendererText *cell,
150 PsppireVarSheet *var_sheet = user_data;
151 GtkWindow *window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (var_sheet)));
152 struct dictionary *dict = var_sheet->dict->dict;
153 enum vs_column column_id;
154 struct variable *var;
159 path = gtk_tree_path_new_from_string (path_string);
160 row = gtk_tree_path_get_indices (path)[0];
161 gtk_tree_path_free (path);
163 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell),
166 var = psppire_dict_get_variable (var_sheet->dict, row);
169 g_return_if_fail (column_id == VS_NAME);
171 if (!dict_id_is_valid (dict, new_text, false))
172 error_dialog (window,
173 g_strdup (_("Cannot create variable.")),
174 g_strdup_printf (_("\"%s\" is not a valid variable "
175 "name."), new_text));
176 else if (dict_lookup_var (dict, new_text) != NULL)
177 error_dialog (window,
178 g_strdup (_("Cannot create variable.")),
179 g_strdup_printf (_("This dictionary already contains "
180 "a variable named \"%s\"."),
184 dict_create_var (var_sheet->dict->dict, new_text, 0);
185 if (!var_sheet->scroll_to_bottom_signal)
187 gtk_widget_queue_resize (GTK_WIDGET (var_sheet));
188 var_sheet->scroll_to_bottom_signal =
189 g_signal_connect (var_sheet, "size-request",
190 G_CALLBACK (scroll_to_bottom), NULL);
200 if (!dict_id_is_valid (dict, new_text, false))
201 error_dialog (window,
202 g_strdup (_("Cannot rename variable.")),
203 g_strdup_printf (_("\"%s\" is not a valid variable "
204 "name."), new_text));
205 else if (dict_lookup_var (dict, new_text) != NULL
206 && dict_lookup_var (dict, new_text) != var)
207 error_dialog (window,
208 g_strdup (_("Cannot rename variable.")),
209 g_strdup_printf (_("This dictionary already contains "
210 "a variable named \"%s\"."),
213 dict_rename_var (dict, var, new_text);
221 width = atoi (new_text);
224 struct fmt_spec format;
226 format = *var_get_print_format (var);
227 fmt_change_width (&format, width, var_sheet->format_use);
228 var_set_print_format (var, &format);
229 var_set_width (var, fmt_var_width (&format));
234 decimals = atoi (new_text);
237 struct fmt_spec format;
239 format = *var_get_print_format (var);
240 fmt_change_decimals (&format, decimals, var_sheet->format_use);
241 var_set_print_format (var, &format);
246 var_set_label (var, new_text, false);
254 width = atoi (new_text);
255 if (width > 0 && width < 2 * MAX_STRING)
256 var_set_display_width (var, width);
260 if (!strcmp (new_text, alignment_to_string (ALIGN_LEFT)))
261 var_set_alignment (var, ALIGN_LEFT);
262 else if (!strcmp (new_text, alignment_to_string (ALIGN_CENTRE)))
263 var_set_alignment (var, ALIGN_CENTRE);
264 else if (!strcmp (new_text, alignment_to_string (ALIGN_RIGHT)))
265 var_set_alignment (var, ALIGN_RIGHT);
269 if (!strcmp (new_text, measure_to_string (MEASURE_NOMINAL)))
270 var_set_measure (var, MEASURE_NOMINAL);
271 else if (!strcmp (new_text, measure_to_string (MEASURE_ORDINAL)))
272 var_set_measure (var, MEASURE_ORDINAL);
273 else if (!strcmp (new_text, measure_to_string (MEASURE_SCALE)))
274 var_set_measure (var, MEASURE_SCALE);
280 render_popup_cell (PsppSheetViewColumn *tree_column,
281 GtkCellRenderer *cell,
286 PsppireVarSheet *var_sheet = user_data;
289 row = GPOINTER_TO_INT (iter->user_data);
291 "editable", row < psppire_dict_get_var_cnt (var_sheet->dict),
296 render_var_cell (PsppSheetViewColumn *tree_column,
297 GtkCellRenderer *cell,
302 PsppireVarSheet *var_sheet = user_data;
303 const struct fmt_spec *print;
304 enum vs_column column_id;
305 struct variable *var;
308 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
309 "column-number")) - 1;
310 row = GPOINTER_TO_INT (iter->user_data);
312 if (row >= psppire_dict_get_var_cnt (var_sheet->dict))
316 "editable", column_id == VS_NAME,
318 if (column_id == VS_WIDTH
319 || column_id == VS_DECIMALS
320 || column_id == VS_COLUMNS)
321 g_object_set (cell, "adjustment", NULL, NULL);
325 var = psppire_dict_get_variable (var_sheet->dict, row);
327 print = var_get_print_format (var);
332 "text", var_get_name (var),
339 "text", fmt_gui_name (print->type),
346 int step = fmt_step_width (print->type);
347 if (var_is_numeric (var))
348 set_spin_cell (cell, print->w,
349 fmt_min_width (print->type, var_sheet->format_use),
350 fmt_max_width (print->type, var_sheet->format_use),
353 set_spin_cell (cell, print->w, 0, 0, step);
358 if (fmt_takes_decimals (print->type))
360 int max_w = fmt_max_width (print->type, var_sheet->format_use);
361 int max_d = fmt_max_decimals (print->type, max_w,
362 var_sheet->format_use);
363 set_spin_cell (cell, print->d, 0, max_d, 1);
375 "text", var_has_label (var) ? var_get_label (var) : "",
381 g_object_set (cell, "editable", FALSE, NULL);
382 if ( ! var_has_value_labels (var))
383 g_object_set (cell, "text", _("None"), NULL);
386 const struct val_labs *vls = var_get_value_labels (var);
387 const struct val_lab **labels = val_labs_sorted (vls);
388 const struct val_lab *vl = labels[0];
389 gchar *vstr = value_to_text (vl->value, var);
390 char *text = xasprintf (_("{%s, %s}..."), vstr,
391 val_lab_get_escaped_label (vl));
394 g_object_set (cell, "text", text, NULL);
402 char *text = missing_values_to_string (var_sheet->dict, var, NULL);
412 set_spin_cell (cell, var_get_display_width (var), 1, 2 * MAX_STRING, 1);
417 "text", alignment_to_string (var_get_alignment (var)),
424 "text", measure_to_string (var_get_measure (var)),
431 static struct variable *
432 path_string_to_variable (PsppireVarSheet *var_sheet, gchar *path_string)
438 path = gtk_tree_path_new_from_string (path_string);
439 row = gtk_tree_path_get_indices (path)[0];
440 gtk_tree_path_free (path);
442 dict = psppire_var_sheet_get_dictionary (var_sheet);
443 g_return_val_if_fail (dict != NULL, NULL);
445 return psppire_dict_get_variable (dict, row);
449 on_type_click (PsppireCellRendererButton *cell,
451 PsppireVarSheet *var_sheet)
453 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
454 struct fmt_spec format;
455 struct variable *var;
457 var = path_string_to_variable (var_sheet, path);
458 g_return_if_fail (var != NULL);
460 format = *var_get_print_format (var);
461 psppire_var_type_dialog_run (GTK_WINDOW (toplevel), &format);
462 var_set_width (var, fmt_var_width (&format));
463 var_set_both_formats (var, &format);
467 on_value_labels_click (PsppireCellRendererButton *cell,
469 PsppireVarSheet *var_sheet)
471 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
472 struct val_labs *labels;
473 struct variable *var;
475 var = path_string_to_variable (var_sheet, path);
476 g_return_if_fail (var != NULL);
478 labels = psppire_val_labs_dialog_run (GTK_WINDOW (toplevel), var);
481 var_set_value_labels (var, labels);
482 val_labs_destroy (labels);
487 on_missing_values_click (PsppireCellRendererButton *cell,
489 PsppireVarSheet *var_sheet)
491 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
492 struct missing_values mv;
493 struct variable *var;
495 var = path_string_to_variable (var_sheet, path);
496 g_return_if_fail (var != NULL);
498 psppire_missing_val_dialog_run (GTK_WINDOW (toplevel), var, &mv);
499 var_set_missing_values (var, &mv);
504 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
508 g_object_set (G_OBJECT (renderer),
509 PSPPIRE_IS_CELL_RENDERER_BUTTON (renderer) ? "label" : "text",
510 string, (void *) NULL);
511 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
512 NULL, NULL, NULL, &width, NULL);
517 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
524 ds_put_byte_multiple (&s, '0', char_cnt);
525 ds_put_byte (&s, ' ');
526 width = get_string_width (treeview, renderer, ds_cstr (&s));
532 static PsppSheetViewColumn *
533 add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer,
534 enum vs_column column_id,
535 const char *title, int width)
537 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
538 int title_width, content_width;
539 PsppSheetViewColumn *column;
541 column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL);
542 g_object_set_data (G_OBJECT (column), "column-number",
543 GINT_TO_POINTER (column_id) + 1);
545 pspp_sheet_view_column_set_cell_data_func (
546 column, renderer, render_var_cell, var_sheet, NULL);
548 title_width = get_string_width (sheet_view, renderer, title);
549 content_width = get_monospace_width (sheet_view, renderer, width);
550 g_object_set_data (G_OBJECT (column), "content-width",
551 GINT_TO_POINTER (content_width));
553 pspp_sheet_view_column_set_fixed_width (column,
554 MAX (title_width, content_width));
555 pspp_sheet_view_column_set_resizable (column, TRUE);
557 pspp_sheet_view_append_column (sheet_view, column);
559 g_signal_connect (renderer, "edited",
560 G_CALLBACK (on_var_column_edited),
562 g_object_set_data (G_OBJECT (renderer), "column-id",
563 GINT_TO_POINTER (column_id));
564 g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet);
569 static PsppSheetViewColumn *
570 add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
571 const char *title, int width)
573 return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (),
574 column_id, title, width);
577 static PsppSheetViewColumn *
578 add_spin_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
579 const char *title, int width)
581 return add_var_sheet_column (var_sheet, gtk_cell_renderer_spin_new (),
582 column_id, title, width);
585 static PsppSheetViewColumn *
586 add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
587 const char *title, int width,
590 GtkCellRenderer *cell;
595 store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
596 va_start (args, width);
597 while ((name = va_arg (args, const char *)) != NULL)
599 int value = va_arg (args, int);
600 gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
607 cell = gtk_cell_renderer_combo_new ();
610 "model", GTK_TREE_MODEL (store),
614 return add_var_sheet_column (var_sheet, cell, column_id, title, width);
619 add_popup_menu (PsppireVarSheet *var_sheet,
620 PsppSheetViewColumn *column,
621 void (*on_click) (PsppireCellRendererButton *,
623 PsppireVarSheet *var_sheet))
625 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
626 const char *button_label = "...";
627 GtkCellRenderer *button_renderer;
630 button_renderer = psppire_cell_renderer_button_new ();
631 g_object_set (button_renderer,
632 "label", button_label,
635 g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click),
637 pspp_sheet_view_column_pack_start (column, button_renderer, FALSE);
638 pspp_sheet_view_column_set_cell_data_func (
639 column, button_renderer, render_popup_cell, var_sheet, NULL);
641 content_width = GPOINTER_TO_INT (g_object_get_data (
642 G_OBJECT (column), "content-width"));
643 content_width += get_string_width (sheet_view, button_renderer,
645 if (content_width > pspp_sheet_view_column_get_fixed_width (column))
646 pspp_sheet_view_column_set_fixed_width (column, content_width);
650 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
651 gint wx, gint wy, size_t *row, size_t *column)
653 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
657 PsppSheetViewColumn *tree_column;
658 GtkTreeModel *tree_model;
662 /* Check that WIDGET is really visible on the screen before we
663 do anything else. This is a bug fix for a sticky situation:
664 when text_data_import_assistant() returns, it frees the data
665 necessary to compose the tool tip message, but there may be
666 a tool tip under preparation at that point (even if there is
667 no visible tool tip) that will call back into us a little
668 bit later. Perhaps the correct solution to this problem is
669 to make the data related to the tool tips part of a GObject
670 that only gets destroyed when all references are released,
671 but this solution appears to be effective too. */
672 if (!gtk_widget_get_mapped (widget))
675 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
677 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
678 &path, &tree_column, NULL, NULL))
681 column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number");
682 if (column_ptr == NULL)
684 *column = GPOINTER_TO_INT (column_ptr) - 1;
686 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
689 tree_model = pspp_sheet_view_get_model (tree_view);
690 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
691 gtk_tree_path_free (path);
695 *row = GPOINTER_TO_INT (iter.user_data);
700 on_query_var_tooltip (GtkWidget *widget, gint wx, gint wy,
701 gboolean keyboard_mode UNUSED,
702 GtkTooltip *tooltip, gpointer *user_data UNUSED)
704 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
706 struct variable *var;
709 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
712 dict = psppire_var_sheet_get_dictionary (var_sheet);
713 g_return_val_if_fail (dict != NULL, FALSE);
715 if (row >= psppire_dict_get_var_cnt (dict))
717 gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a "
722 var = psppire_dict_get_variable (dict, row);
723 g_return_val_if_fail (var != NULL, FALSE);
729 char text[FMT_STRING_LEN_MAX + 1];
731 fmt_to_string (var_get_print_format (var), text);
732 gtk_tooltip_set_text (tooltip, text);
737 if (var_has_value_labels (var))
739 const struct val_labs *vls = var_get_value_labels (var);
740 const struct val_lab **labels = val_labs_sorted (vls);
745 for (i = 0; i < val_labs_count (vls); i++)
747 const struct val_lab *vl = labels[i];
750 if (i >= 10 || ds_length (&s) > 500)
752 ds_put_cstr (&s, "...");
756 vstr = value_to_text (vl->value, var);
757 ds_put_format (&s, _("{%s, %s}\n"), vstr,
758 val_lab_get_escaped_label (vl));
762 ds_chomp_byte (&s, '\n');
764 gtk_tooltip_set_text (tooltip, ds_cstr (&s));
776 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
778 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
781 menu = get_widget_assert (var_sheet->builder, "varsheet-variable-popup");
782 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
786 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
788 do_popup_menu (widget, 0, gtk_get_current_event_time ());
792 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
793 gpointer user_data UNUSED)
795 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
797 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
799 PsppSheetSelection *selection;
801 selection = pspp_sheet_view_get_selection (sheet_view);
802 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
806 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
807 &path, NULL, NULL, NULL))
809 pspp_sheet_selection_unselect_all (selection);
810 pspp_sheet_selection_select_path (selection, path);
811 gtk_tree_path_free (path);
815 do_popup_menu (widget, event->button, event->time);
823 psppire_fmt_use_get_type (void)
825 static GType etype = 0;
828 static const GEnumValue values[] =
830 { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
831 { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
835 etype = g_enum_register_static
836 (g_intern_static_string ("PsppireFmtUse"), values);
845 PROP_MAY_CREATE_VARS,
846 PROP_MAY_DELETE_VARS,
852 psppire_var_sheet_set_property (GObject *object,
857 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
861 case PROP_DICTIONARY:
862 psppire_var_sheet_set_dictionary (obj,
863 PSPPIRE_DICT (g_value_get_object (
867 case PROP_MAY_CREATE_VARS:
868 psppire_var_sheet_set_may_create_vars (obj,
869 g_value_get_boolean (value));
872 case PROP_MAY_DELETE_VARS:
873 psppire_var_sheet_set_may_delete_vars (obj,
874 g_value_get_boolean (value));
877 case PROP_FORMAT_TYPE:
878 obj->format_use = g_value_get_enum (value);
881 case PROP_UI_MANAGER:
883 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
889 psppire_var_sheet_get_property (GObject *object,
894 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
898 case PROP_DICTIONARY:
899 g_value_set_object (value,
900 G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
903 case PROP_MAY_CREATE_VARS:
904 g_value_set_boolean (value, obj->may_create_vars);
907 case PROP_MAY_DELETE_VARS:
908 g_value_set_boolean (value, obj->may_delete_vars);
911 case PROP_FORMAT_TYPE:
912 g_value_set_enum (value, obj->format_use);
915 case PROP_UI_MANAGER:
916 g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj));
920 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
926 psppire_var_sheet_dispose (GObject *obj)
928 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
931 if (var_sheet->dispose_has_run)
934 var_sheet->dispose_has_run = TRUE;
936 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
937 if ( var_sheet->dict_signals[i])
938 g_signal_handler_disconnect (var_sheet->dict,
939 var_sheet->dict_signals[i]);
942 g_object_unref (var_sheet->dict);
945 g_object_unref (var_sheet->uim);
947 /* These dialogs are not GObjects (although they should be!)
948 But for now, unreffing them only causes a GCritical Error
949 so comment them out for now. (and accept the memory leakage)
951 g_object_unref (var_sheet->val_labs_dialog);
952 g_object_unref (var_sheet->missing_val_dialog);
953 g_object_unref (var_sheet->var_type_dialog);
956 G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
960 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
962 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
965 gobject_class->set_property = psppire_var_sheet_set_property;
966 gobject_class->get_property = psppire_var_sheet_get_property;
967 gobject_class->dispose = psppire_var_sheet_dispose;
969 g_signal_new ("var-double-clicked",
970 G_OBJECT_CLASS_TYPE (gobject_class),
973 g_signal_accumulator_true_handled, NULL,
974 psppire_marshal_BOOLEAN__INT,
975 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
977 pspec = g_param_spec_object ("dictionary",
978 "Dictionary displayed by the sheet",
979 "The PsppireDict that the sheet displays "
980 "may allow the user to edit",
983 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
985 pspec = g_param_spec_boolean ("may-create-vars",
986 "May create variables",
987 "Whether the user may create more variables",
990 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
992 pspec = g_param_spec_boolean ("may-delete-vars",
993 "May delete variables",
994 "Whether the user may delete variables",
997 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
999 pspec = g_param_spec_enum ("format-use",
1000 "Use of variable format",
1001 ("Whether variables have input or output "
1003 PSPPIRE_TYPE_FMT_USE,
1006 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
1008 pspec = g_param_spec_object ("ui-manager",
1010 "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.",
1011 GTK_TYPE_UI_MANAGER,
1013 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1017 render_row_number_cell (PsppSheetViewColumn *tree_column,
1018 GtkCellRenderer *cell,
1019 GtkTreeModel *model,
1023 PsppireVarSheet *var_sheet = user_data;
1024 GValue gvalue = { 0, };
1027 row = GPOINTER_TO_INT (iter->user_data);
1029 g_value_init (&gvalue, G_TYPE_INT);
1030 g_value_set_int (&gvalue, row + 1);
1031 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1032 g_value_unset (&gvalue);
1034 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1035 g_object_set (cell, "editable", TRUE, NULL);
1037 g_object_set (cell, "editable", FALSE, NULL);
1041 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1043 PsppireVarSheet *var_sheet)
1047 g_return_if_fail (var_sheet->dict != NULL);
1049 path = gtk_tree_path_new_from_string (path_string);
1050 if (gtk_tree_path_get_depth (path) == 1)
1052 gint *indices = gtk_tree_path_get_indices (path);
1053 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1056 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1057 indices[0], &handled);
1060 gtk_tree_path_free (path);
1063 static PsppSheetViewColumn *
1064 make_row_number_column (PsppireVarSheet *var_sheet)
1066 PsppSheetViewColumn *column;
1067 GtkCellRenderer *renderer;
1069 renderer = psppire_cell_renderer_button_new ();
1070 g_object_set (renderer, "xalign", 1.0, NULL);
1071 g_signal_connect (renderer, "double-clicked",
1072 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1075 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1077 pspp_sheet_view_column_set_cell_data_func (
1078 column, renderer, render_row_number_cell, var_sheet, NULL);
1079 pspp_sheet_view_column_set_fixed_width (column, 50);
1084 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1086 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1087 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1088 PsppireDict *dict = var_sheet->dict;
1089 const struct range_set_node *node;
1090 struct range_set *selected;
1092 selected = pspp_sheet_selection_get_range_set (selection);
1093 for (node = range_set_last (selected); node != NULL;
1094 node = range_set_prev (selected, node))
1098 for (i = 1; i <= range_set_node_get_width (node); i++)
1100 unsigned long row = range_set_node_get_end (node) - i;
1101 if (row >= 0 && row < psppire_dict_get_var_cnt (dict))
1102 psppire_dict_delete_variables (dict, row, 1);
1105 range_set_destroy (selected);
1109 on_selection_changed (PsppSheetSelection *selection,
1110 gpointer user_data UNUSED)
1112 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1113 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1114 gint n_selected_rows;
1115 gboolean may_delete;
1119 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1121 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1122 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1123 && n_selected_rows > 0));
1125 switch (n_selected_rows)
1132 /* The row used for inserting new variables cannot be deleted. */
1133 path = gtk_tree_path_new_from_indices (
1134 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1135 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1136 gtk_tree_path_free (path);
1143 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1144 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1148 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1150 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1151 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1152 PsppireDict *dict = var_sheet->dict;
1153 struct range_set *selected;
1156 selected = pspp_sheet_selection_get_range_set (selection);
1157 row = range_set_scan (selected, 0);
1158 range_set_destroy (selected);
1160 if (row <= psppire_dict_get_var_cnt (dict))
1163 if (psppire_dict_generate_name (dict, name, sizeof name))
1164 psppire_dict_insert_variable (dict, row, name);
1169 psppire_var_sheet_init (PsppireVarSheet *obj)
1171 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1172 PsppSheetViewColumn *column;
1177 obj->format_use = FMT_FOR_OUTPUT;
1178 obj->may_create_vars = TRUE;
1179 obj->may_delete_vars = TRUE;
1181 obj->scroll_to_bottom_signal = 0;
1183 obj->container = NULL;
1184 obj->dispose_has_run = FALSE;
1187 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1189 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1190 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1191 g_signal_connect (list->data, "editing-started",
1192 G_CALLBACK (on_name_column_editing_started), NULL);
1195 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1196 add_popup_menu (obj, column, on_type_click);
1198 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1200 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1202 add_text_column (obj, VS_LABEL, _("Label"), 20);
1204 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1205 add_popup_menu (obj, column, on_value_labels_click);
1207 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1208 add_popup_menu (obj, column, on_missing_values_click);
1210 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1212 add_combo_column (obj, VS_ALIGN, _("Align"), 6,
1213 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1214 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1215 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1218 add_combo_column (obj, VS_MEASURE, _("Measure"), 10,
1219 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1220 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1221 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1224 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1225 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1226 PSPP_SHEET_SELECTION_MULTIPLE);
1228 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1229 g_signal_connect (obj, "query-tooltip",
1230 G_CALLBACK (on_query_var_tooltip), NULL);
1231 g_signal_connect (obj, "button-press-event",
1232 G_CALLBACK (on_button_pressed), NULL);
1233 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1235 obj->builder = builder_new ("var-sheet.ui");
1237 action = get_action_assert (obj->builder, "edit_clear-variables");
1238 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1240 gtk_action_set_sensitive (action, FALSE);
1241 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1242 "changed", G_CALLBACK (on_selection_changed), NULL);
1244 action = get_action_assert (obj->builder, "edit_insert-variable");
1245 gtk_action_set_sensitive (action, FALSE);
1246 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1251 psppire_var_sheet_new (void)
1253 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1257 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1259 return var_sheet->dict;
1263 refresh_model (PsppireVarSheet *var_sheet)
1265 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1267 if (var_sheet->dict != NULL)
1269 PsppireEmptyListStore *store;
1272 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1273 + var_sheet->may_create_vars);
1274 store = psppire_empty_list_store_new (n_rows);
1275 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1276 GTK_TREE_MODEL (store));
1277 g_object_unref (store);
1282 on_var_changed (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1284 PsppireEmptyListStore *store;
1286 g_return_if_fail (dict == var_sheet->dict);
1288 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1289 PSPP_SHEET_VIEW (var_sheet)));
1290 g_return_if_fail (store != NULL);
1292 psppire_empty_list_store_row_changed (store, row);
1296 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1298 PsppireEmptyListStore *store;
1301 g_return_if_fail (dict == var_sheet->dict);
1303 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1304 PSPP_SHEET_VIEW (var_sheet)));
1305 g_return_if_fail (store != NULL);
1307 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1308 + var_sheet->may_create_vars);
1309 psppire_empty_list_store_set_n_rows (store, n_rows);
1310 psppire_empty_list_store_row_inserted (store, row);
1314 on_var_deleted (PsppireDict *dict,
1315 const struct variable *var, int dict_idx, int case_idx,
1316 PsppireVarSheet *var_sheet)
1318 PsppireEmptyListStore *store;
1321 g_return_if_fail (dict == var_sheet->dict);
1323 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1324 PSPP_SHEET_VIEW (var_sheet)));
1325 g_return_if_fail (store != NULL);
1327 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1328 + var_sheet->may_create_vars);
1329 psppire_empty_list_store_set_n_rows (store, n_rows);
1330 psppire_empty_list_store_row_deleted (store, dict_idx);
1334 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1336 g_return_if_fail (dict == var_sheet->dict);
1337 refresh_model (var_sheet);
1341 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1344 if (var_sheet->dict != NULL)
1348 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1350 if (var_sheet->dict_signals[i])
1351 g_signal_handler_disconnect (var_sheet->dict,
1352 var_sheet->dict_signals[i]);
1354 var_sheet->dict_signals[i] = 0;
1357 g_object_unref (var_sheet->dict);
1360 var_sheet->dict = dict;
1364 g_object_ref (dict);
1366 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1367 = g_signal_connect (dict, "backend-changed",
1368 G_CALLBACK (on_backend_changed), var_sheet);
1370 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED]
1371 = g_signal_connect (dict, "variable-changed",
1372 G_CALLBACK (on_var_changed), var_sheet);
1374 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1375 = g_signal_connect (dict, "variable-inserted",
1376 G_CALLBACK (on_var_inserted), var_sheet);
1378 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1379 = g_signal_connect (dict, "variable-deleted",
1380 G_CALLBACK (on_var_deleted), var_sheet);
1383 refresh_model (var_sheet);
1387 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1389 return var_sheet->may_create_vars;
1393 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1394 gboolean may_create_vars)
1396 if (var_sheet->may_create_vars != may_create_vars)
1398 PsppireEmptyListStore *store;
1401 var_sheet->may_create_vars = may_create_vars;
1403 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1404 PSPP_SHEET_VIEW (var_sheet)));
1405 g_return_if_fail (store != NULL);
1407 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1408 + var_sheet->may_create_vars);
1409 psppire_empty_list_store_set_n_rows (store, n_rows);
1411 if (may_create_vars)
1412 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1414 psppire_empty_list_store_row_deleted (store, n_rows);
1416 on_selection_changed (pspp_sheet_view_get_selection (
1417 PSPP_SHEET_VIEW (var_sheet)), NULL);
1422 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1424 return var_sheet->may_delete_vars;
1428 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1429 gboolean may_delete_vars)
1431 if (var_sheet->may_delete_vars != may_delete_vars)
1433 var_sheet->may_delete_vars = may_delete_vars;
1434 on_selection_changed (pspp_sheet_view_get_selection (
1435 PSPP_SHEET_VIEW (var_sheet)), NULL);
1440 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1442 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1445 path = gtk_tree_path_new_from_indices (dict_index, -1);
1446 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1447 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1448 gtk_tree_path_free (path);
1452 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1454 if (var_sheet->uim == NULL)
1456 var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1458 GTK_TYPE_UI_MANAGER));
1459 g_object_ref (var_sheet->uim);
1462 return var_sheet->uim;