1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 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-display.h"
36 #include "ui/gui/var-type-dialog.h"
38 #include "gl/intprops.h"
41 #define _(msgid) gettext (msgid)
42 #define N_(msgid) msgid
58 G_DEFINE_TYPE (PsppireVarSheet, psppire_var_sheet, PSPP_TYPE_SHEET_VIEW);
61 set_spin_cell (GtkCellRenderer *cell, int value, int min, int max, int step)
63 char text[INT_BUFSIZE_BOUND (int)];
64 GtkAdjustment *adjust;
67 adjust = GTK_ADJUSTMENT (gtk_adjustment_new (value, min, max,
72 sprintf (text, "%d", value);
81 error_dialog (GtkWindow *w, gchar *primary_text, gchar *secondary_text)
84 gtk_message_dialog_new (w,
85 GTK_DIALOG_DESTROY_WITH_PARENT,
87 GTK_BUTTONS_CLOSE, "%s", primary_text);
89 g_object_set (dialog, "icon-name", "psppicon", NULL);
91 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
92 "%s", secondary_text);
94 gtk_dialog_run (GTK_DIALOG (dialog));
96 gtk_widget_destroy (dialog);
100 on_name_column_editing_started (GtkCellRenderer *cell,
101 GtkCellEditable *editable,
105 PsppireVarSheet *var_sheet = g_object_get_data (G_OBJECT (cell), "var-sheet");
106 PsppireDict *dict = psppire_var_sheet_get_dictionary (var_sheet);
108 g_return_if_fail (var_sheet);
109 g_return_if_fail (dict);
111 if (GTK_IS_ENTRY (editable))
113 GtkEntry *entry = GTK_ENTRY (editable);
114 if (gtk_entry_get_text (entry)[0] == '\0')
117 if (psppire_dict_generate_name (dict, name, sizeof name))
118 gtk_entry_set_text (entry, name);
124 scroll_to_bottom (GtkWidget *widget,
125 GtkRequisition *requisition,
126 gpointer unused UNUSED)
128 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
129 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
130 GtkAdjustment *vadjust;
132 vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
133 gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
135 if (var_sheet->scroll_to_bottom_signal)
137 g_signal_handler_disconnect (var_sheet,
138 var_sheet->scroll_to_bottom_signal);
139 var_sheet->scroll_to_bottom_signal = 0;
144 on_var_column_edited (GtkCellRendererText *cell,
149 PsppireVarSheet *var_sheet = user_data;
150 GtkWindow *window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (var_sheet)));
151 struct dictionary *dict = var_sheet->dict->dict;
152 enum vs_column column_id;
153 struct variable *var;
158 path = gtk_tree_path_new_from_string (path_string);
159 row = gtk_tree_path_get_indices (path)[0];
160 gtk_tree_path_free (path);
162 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell),
165 var = psppire_dict_get_variable (var_sheet->dict, row);
168 g_return_if_fail (column_id == VS_NAME);
170 if (!dict_id_is_valid (dict, new_text, false))
171 error_dialog (window,
172 g_strdup (_("Cannot create variable.")),
173 g_strdup_printf (_("\"%s\" is not a valid variable "
174 "name."), new_text));
175 else if (dict_lookup_var (dict, new_text) != NULL)
176 error_dialog (window,
177 g_strdup (_("Cannot create variable.")),
178 g_strdup_printf (_("This dictionary already contains "
179 "a variable named \"%s\"."),
183 dict_create_var (var_sheet->dict->dict, new_text, 0);
184 if (!var_sheet->scroll_to_bottom_signal)
186 gtk_widget_queue_resize (GTK_WIDGET (var_sheet));
187 var_sheet->scroll_to_bottom_signal =
188 g_signal_connect (var_sheet, "size-request",
189 G_CALLBACK (scroll_to_bottom), NULL);
199 if (!dict_id_is_valid (dict, new_text, false))
200 error_dialog (window,
201 g_strdup (_("Cannot rename variable.")),
202 g_strdup_printf (_("\"%s\" is not a valid variable "
203 "name."), new_text));
204 else if (dict_lookup_var (dict, new_text) != NULL
205 && dict_lookup_var (dict, new_text) != var)
206 error_dialog (window,
207 g_strdup (_("Cannot rename variable.")),
208 g_strdup_printf (_("This dictionary already contains "
209 "a variable named \"%s\"."),
212 dict_rename_var (dict, var, new_text);
220 width = atoi (new_text);
223 struct fmt_spec format;
225 format = *var_get_print_format (var);
226 fmt_change_width (&format, width, var_sheet->format_use);
227 var_set_print_format (var, &format);
228 var_set_width (var, fmt_var_width (&format));
233 decimals = atoi (new_text);
236 struct fmt_spec format;
238 format = *var_get_print_format (var);
239 fmt_change_decimals (&format, decimals, var_sheet->format_use);
240 var_set_print_format (var, &format);
245 var_set_label (var, new_text, false);
253 width = atoi (new_text);
254 if (width > 0 && width < 2 * MAX_STRING)
255 var_set_display_width (var, width);
259 if (!strcmp (new_text, alignment_to_string (ALIGN_LEFT)))
260 var_set_alignment (var, ALIGN_LEFT);
261 else if (!strcmp (new_text, alignment_to_string (ALIGN_CENTRE)))
262 var_set_alignment (var, ALIGN_CENTRE);
263 else if (!strcmp (new_text, alignment_to_string (ALIGN_RIGHT)))
264 var_set_alignment (var, ALIGN_RIGHT);
268 if (!strcmp (new_text, measure_to_string (MEASURE_NOMINAL)))
269 var_set_measure (var, MEASURE_NOMINAL);
270 else if (!strcmp (new_text, measure_to_string (MEASURE_ORDINAL)))
271 var_set_measure (var, MEASURE_ORDINAL);
272 else if (!strcmp (new_text, measure_to_string (MEASURE_SCALE)))
273 var_set_measure (var, MEASURE_SCALE);
279 render_popup_cell (PsppSheetViewColumn *tree_column,
280 GtkCellRenderer *cell,
285 PsppireVarSheet *var_sheet = user_data;
288 row = GPOINTER_TO_INT (iter->user_data);
290 "editable", row < psppire_dict_get_var_cnt (var_sheet->dict),
295 render_var_cell (PsppSheetViewColumn *tree_column,
296 GtkCellRenderer *cell,
301 PsppireVarSheet *var_sheet = user_data;
302 const struct fmt_spec *print;
303 enum vs_column column_id;
304 struct variable *var;
307 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
308 "column-number")) - 1;
309 row = GPOINTER_TO_INT (iter->user_data);
311 if (row >= psppire_dict_get_var_cnt (var_sheet->dict))
315 "editable", column_id == VS_NAME,
317 if (column_id == VS_WIDTH
318 || column_id == VS_DECIMALS
319 || column_id == VS_COLUMNS)
320 g_object_set (cell, "adjustment", NULL, NULL);
324 var = psppire_dict_get_variable (var_sheet->dict, row);
326 print = var_get_print_format (var);
331 "text", var_get_name (var),
338 "text", fmt_gui_name (print->type),
345 int step = fmt_step_width (print->type);
346 if (var_is_numeric (var))
347 set_spin_cell (cell, print->w,
348 fmt_min_width (print->type, var_sheet->format_use),
349 fmt_max_width (print->type, var_sheet->format_use),
352 set_spin_cell (cell, print->w, 0, 0, step);
357 if (fmt_takes_decimals (print->type))
359 int max_w = fmt_max_width (print->type, var_sheet->format_use);
360 int max_d = fmt_max_decimals (print->type, max_w,
361 var_sheet->format_use);
362 set_spin_cell (cell, print->d, 0, max_d, 1);
374 "text", var_has_label (var) ? var_get_label (var) : "",
380 g_object_set (cell, "editable", FALSE, NULL);
381 if ( ! var_has_value_labels (var))
382 g_object_set (cell, "text", _("None"), NULL);
385 const struct val_labs *vls = var_get_value_labels (var);
386 const struct val_lab **labels = val_labs_sorted (vls);
387 const struct val_lab *vl = labels[0];
388 gchar *vstr = value_to_text (vl->value, var);
389 char *text = xasprintf (_("{%s, %s}..."), vstr,
390 val_lab_get_escaped_label (vl));
393 g_object_set (cell, "text", text, NULL);
400 char *text = missing_values_to_string (var_sheet->dict, var, NULL);
410 set_spin_cell (cell, var_get_display_width (var), 1, 2 * MAX_STRING, 1);
415 "text", alignment_to_string (var_get_alignment (var)),
422 "text", measure_to_string (var_get_measure (var)),
429 static struct variable *
430 path_string_to_variable (PsppireVarSheet *var_sheet, gchar *path_string)
436 path = gtk_tree_path_new_from_string (path_string);
437 row = gtk_tree_path_get_indices (path)[0];
438 gtk_tree_path_free (path);
440 dict = psppire_var_sheet_get_dictionary (var_sheet);
441 g_return_val_if_fail (dict != NULL, NULL);
443 return psppire_dict_get_variable (dict, row);
447 on_type_click (PsppireCellRendererButton *cell,
449 PsppireVarSheet *var_sheet)
451 var_sheet->var_type_dialog->pv = path_string_to_variable (var_sheet, path);
452 var_type_dialog_show (var_sheet->var_type_dialog);
456 on_value_labels_click (PsppireCellRendererButton *cell,
458 PsppireVarSheet *var_sheet)
460 struct variable *var = path_string_to_variable (var_sheet, path);
461 val_labs_dialog_set_target_variable (var_sheet->val_labs_dialog, var);
462 val_labs_dialog_show (var_sheet->val_labs_dialog);
466 on_missing_values_click (PsppireCellRendererButton *cell,
468 PsppireVarSheet *var_sheet)
470 var_sheet->missing_val_dialog->pv = path_string_to_variable (var_sheet,
472 missing_val_dialog_show (var_sheet->missing_val_dialog);
476 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
480 g_object_set (G_OBJECT (renderer),
481 PSPPIRE_IS_CELL_RENDERER_BUTTON (renderer) ? "label" : "text",
482 string, (void *) NULL);
483 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
484 NULL, NULL, NULL, &width, NULL);
489 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
496 ds_put_byte_multiple (&s, '0', char_cnt);
497 ds_put_byte (&s, ' ');
498 width = get_string_width (treeview, renderer, ds_cstr (&s));
504 static PsppSheetViewColumn *
505 add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer,
506 enum vs_column column_id,
507 const char *title, int width)
509 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
510 int title_width, content_width;
511 PsppSheetViewColumn *column;
513 column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL);
514 g_object_set_data (G_OBJECT (column), "column-number",
515 GINT_TO_POINTER (column_id) + 1);
517 pspp_sheet_view_column_set_cell_data_func (
518 column, renderer, render_var_cell, var_sheet, NULL);
520 title_width = get_string_width (sheet_view, renderer, title);
521 content_width = get_monospace_width (sheet_view, renderer, width);
522 g_object_set_data (G_OBJECT (column), "content-width",
523 GINT_TO_POINTER (content_width));
525 pspp_sheet_view_column_set_fixed_width (column,
526 MAX (title_width, content_width));
527 pspp_sheet_view_column_set_resizable (column, TRUE);
529 pspp_sheet_view_append_column (sheet_view, column);
531 g_signal_connect (renderer, "edited",
532 G_CALLBACK (on_var_column_edited),
534 g_object_set_data (G_OBJECT (renderer), "column-id",
535 GINT_TO_POINTER (column_id));
536 g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet);
541 static PsppSheetViewColumn *
542 add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
543 const char *title, int width)
545 return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (),
546 column_id, title, width);
549 static PsppSheetViewColumn *
550 add_spin_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
551 const char *title, int width)
553 return add_var_sheet_column (var_sheet, gtk_cell_renderer_spin_new (),
554 column_id, title, width);
557 static PsppSheetViewColumn *
558 add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
559 const char *title, int width,
562 GtkCellRenderer *cell;
567 store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
568 va_start (args, width);
569 while ((name = va_arg (args, const char *)) != NULL)
571 int value = va_arg (args, int);
572 gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
579 cell = gtk_cell_renderer_combo_new ();
582 "model", GTK_TREE_MODEL (store),
586 return add_var_sheet_column (var_sheet, cell, column_id, title, width);
591 add_popup_menu (PsppireVarSheet *var_sheet,
592 PsppSheetViewColumn *column,
593 void (*on_click) (PsppireCellRendererButton *,
595 PsppireVarSheet *var_sheet))
597 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
598 const char *button_label = "...";
599 GtkCellRenderer *button_renderer;
602 button_renderer = psppire_cell_renderer_button_new ();
603 g_object_set (button_renderer,
604 "label", button_label,
607 g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click),
609 pspp_sheet_view_column_pack_start (column, button_renderer, FALSE);
610 pspp_sheet_view_column_set_cell_data_func (
611 column, button_renderer, render_popup_cell, var_sheet, NULL);
613 content_width = GPOINTER_TO_INT (g_object_get_data (
614 G_OBJECT (column), "content-width"));
615 content_width += get_string_width (sheet_view, button_renderer,
617 if (content_width > pspp_sheet_view_column_get_fixed_width (column))
618 pspp_sheet_view_column_set_fixed_width (column, content_width);
622 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
623 gint wx, gint wy, size_t *row, size_t *column)
625 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
629 PsppSheetViewColumn *tree_column;
630 GtkTreeModel *tree_model;
634 /* Check that WIDGET is really visible on the screen before we
635 do anything else. This is a bug fix for a sticky situation:
636 when text_data_import_assistant() returns, it frees the data
637 necessary to compose the tool tip message, but there may be
638 a tool tip under preparation at that point (even if there is
639 no visible tool tip) that will call back into us a little
640 bit later. Perhaps the correct solution to this problem is
641 to make the data related to the tool tips part of a GObject
642 that only gets destroyed when all references are released,
643 but this solution appears to be effective too. */
644 if (!gtk_widget_get_mapped (widget))
647 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
649 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
650 &path, &tree_column, NULL, NULL))
653 column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number");
654 if (column_ptr == NULL)
656 *column = GPOINTER_TO_INT (column_ptr) - 1;
658 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
661 tree_model = pspp_sheet_view_get_model (tree_view);
662 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
663 gtk_tree_path_free (path);
667 *row = GPOINTER_TO_INT (iter.user_data);
672 on_query_var_tooltip (GtkWidget *widget, gint wx, gint wy,
673 gboolean keyboard_mode UNUSED,
674 GtkTooltip *tooltip, gpointer *user_data UNUSED)
676 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
678 struct variable *var;
681 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
684 dict = psppire_var_sheet_get_dictionary (var_sheet);
685 g_return_val_if_fail (dict != NULL, FALSE);
687 if (row >= psppire_dict_get_var_cnt (dict))
689 gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a "
694 var = psppire_dict_get_variable (dict, row);
695 g_return_val_if_fail (var != NULL, FALSE);
701 char text[FMT_STRING_LEN_MAX + 1];
703 fmt_to_string (var_get_print_format (var), text);
704 gtk_tooltip_set_text (tooltip, text);
709 if (var_has_value_labels (var))
711 const struct val_labs *vls = var_get_value_labels (var);
712 const struct val_lab **labels = val_labs_sorted (vls);
717 for (i = 0; i < val_labs_count (vls); i++)
719 const struct val_lab *vl = labels[i];
722 if (i >= 10 || ds_length (&s) > 500)
724 ds_put_cstr (&s, "...");
728 vstr = value_to_text (vl->value, var);
729 ds_put_format (&s, _("{%s, %s}\n"), vstr,
730 val_lab_get_escaped_label (vl));
734 ds_chomp_byte (&s, '\n');
736 gtk_tooltip_set_text (tooltip, ds_cstr (&s));
747 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
749 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
752 menu = get_widget_assert (var_sheet->builder, "varsheet-variable-popup");
753 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
757 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
759 do_popup_menu (widget, 0, gtk_get_current_event_time ());
763 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
764 gpointer user_data UNUSED)
766 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
768 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
770 PsppSheetSelection *selection;
772 selection = pspp_sheet_view_get_selection (sheet_view);
773 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
777 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
778 &path, NULL, NULL, NULL))
780 pspp_sheet_selection_unselect_all (selection);
781 pspp_sheet_selection_select_path (selection, path);
782 gtk_tree_path_free (path);
786 do_popup_menu (widget, event->button, event->time);
794 psppire_fmt_use_get_type (void)
796 static GType etype = 0;
799 static const GEnumValue values[] =
801 { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
802 { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
806 etype = g_enum_register_static
807 (g_intern_static_string ("PsppireFmtUse"), values);
816 PROP_MAY_CREATE_VARS,
817 PROP_MAY_DELETE_VARS,
823 psppire_var_sheet_set_property (GObject *object,
828 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
832 case PROP_DICTIONARY:
833 psppire_var_sheet_set_dictionary (obj,
834 PSPPIRE_DICT (g_value_get_object (
838 case PROP_MAY_CREATE_VARS:
839 psppire_var_sheet_set_may_create_vars (obj,
840 g_value_get_boolean (value));
843 case PROP_MAY_DELETE_VARS:
844 psppire_var_sheet_set_may_delete_vars (obj,
845 g_value_get_boolean (value));
848 case PROP_FORMAT_TYPE:
849 obj->format_use = g_value_get_enum (value);
852 case PROP_UI_MANAGER:
854 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
860 psppire_var_sheet_get_property (GObject *object,
865 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
869 case PROP_DICTIONARY:
870 g_value_set_object (value,
871 G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
874 case PROP_MAY_CREATE_VARS:
875 g_value_set_boolean (value, obj->may_create_vars);
878 case PROP_MAY_DELETE_VARS:
879 g_value_set_boolean (value, obj->may_delete_vars);
882 case PROP_FORMAT_TYPE:
883 g_value_set_enum (value, obj->format_use);
886 case PROP_UI_MANAGER:
887 g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj));
891 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
897 psppire_var_sheet_realize (GtkWidget *w)
899 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (w);
902 GTK_WIDGET_CLASS (psppire_var_sheet_parent_class)->realize (w);
904 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (w));
905 var_sheet->val_labs_dialog = val_labs_dialog_create (toplevel);
906 var_sheet->missing_val_dialog = missing_val_dialog_create (toplevel);
907 var_sheet->var_type_dialog = var_type_dialog_create (toplevel);
911 psppire_var_sheet_dispose (GObject *obj)
913 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
916 if (var_sheet->dispose_has_run)
919 var_sheet->dispose_has_run = TRUE;
921 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
922 if ( var_sheet->dict_signals[i])
923 g_signal_handler_disconnect (var_sheet->dict,
924 var_sheet->dict_signals[i]);
927 g_object_unref (var_sheet->dict);
930 g_object_unref (var_sheet->uim);
932 /* These dialogs are not GObjects (although they should be!)
933 But for now, unreffing them only causes a GCritical Error
934 so comment them out for now. (and accept the memory leakage)
936 g_object_unref (var_sheet->val_labs_dialog);
937 g_object_unref (var_sheet->missing_val_dialog);
938 g_object_unref (var_sheet->var_type_dialog);
941 G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
945 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
947 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
948 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
951 gobject_class->set_property = psppire_var_sheet_set_property;
952 gobject_class->get_property = psppire_var_sheet_get_property;
953 gobject_class->dispose = psppire_var_sheet_dispose;
955 widget_class->realize = psppire_var_sheet_realize;
957 g_signal_new ("var-double-clicked",
958 G_OBJECT_CLASS_TYPE (gobject_class),
961 g_signal_accumulator_true_handled, NULL,
962 psppire_marshal_BOOLEAN__INT,
963 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
965 pspec = g_param_spec_object ("dictionary",
966 "Dictionary displayed by the sheet",
967 "The PsppireDict that the sheet displays "
968 "may allow the user to edit",
971 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
973 pspec = g_param_spec_boolean ("may-create-vars",
974 "May create variables",
975 "Whether the user may create more variables",
978 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
980 pspec = g_param_spec_boolean ("may-delete-vars",
981 "May delete variables",
982 "Whether the user may delete variables",
985 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
987 pspec = g_param_spec_enum ("format-use",
988 "Use of variable format",
989 ("Whether variables have input or output "
991 PSPPIRE_TYPE_FMT_USE,
994 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
996 pspec = g_param_spec_object ("ui-manager",
998 "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.",
1001 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1005 render_row_number_cell (PsppSheetViewColumn *tree_column,
1006 GtkCellRenderer *cell,
1007 GtkTreeModel *model,
1011 PsppireVarSheet *var_sheet = user_data;
1012 GValue gvalue = { 0, };
1015 row = GPOINTER_TO_INT (iter->user_data);
1017 g_value_init (&gvalue, G_TYPE_INT);
1018 g_value_set_int (&gvalue, row + 1);
1019 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1020 g_value_unset (&gvalue);
1022 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1023 g_object_set (cell, "editable", TRUE, NULL);
1025 g_object_set (cell, "editable", FALSE, NULL);
1029 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1031 PsppireVarSheet *var_sheet)
1035 g_return_if_fail (var_sheet->dict != NULL);
1037 path = gtk_tree_path_new_from_string (path_string);
1038 if (gtk_tree_path_get_depth (path) == 1)
1040 gint *indices = gtk_tree_path_get_indices (path);
1041 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1044 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1045 indices[0], &handled);
1048 gtk_tree_path_free (path);
1051 static PsppSheetViewColumn *
1052 make_row_number_column (PsppireVarSheet *var_sheet)
1054 PsppSheetViewColumn *column;
1055 GtkCellRenderer *renderer;
1057 renderer = psppire_cell_renderer_button_new ();
1058 g_object_set (renderer, "xalign", 1.0, NULL);
1059 g_signal_connect (renderer, "double-clicked",
1060 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1063 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1065 pspp_sheet_view_column_set_cell_data_func (
1066 column, renderer, render_row_number_cell, var_sheet, NULL);
1067 pspp_sheet_view_column_set_fixed_width (column, 50);
1072 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1074 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1075 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1076 PsppireDict *dict = var_sheet->dict;
1077 const struct range_set_node *node;
1078 struct range_set *selected;
1080 selected = pspp_sheet_selection_get_range_set (selection);
1081 for (node = range_set_last (selected); node != NULL;
1082 node = range_set_prev (selected, node))
1086 for (i = 1; i <= range_set_node_get_width (node); i++)
1088 unsigned long row = range_set_node_get_end (node) - i;
1089 if (row >= 0 && row < psppire_dict_get_var_cnt (dict))
1090 psppire_dict_delete_variables (dict, row, 1);
1093 range_set_destroy (selected);
1097 on_selection_changed (PsppSheetSelection *selection,
1098 gpointer user_data UNUSED)
1100 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1101 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1102 gint n_selected_rows;
1103 gboolean may_delete;
1107 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1109 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1110 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1111 && n_selected_rows > 0));
1113 switch (n_selected_rows)
1120 /* The row used for inserting new variables cannot be deleted. */
1121 path = gtk_tree_path_new_from_indices (
1122 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1123 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1124 gtk_tree_path_free (path);
1131 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1132 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1136 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1138 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1139 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1140 PsppireDict *dict = var_sheet->dict;
1141 struct range_set *selected;
1144 selected = pspp_sheet_selection_get_range_set (selection);
1145 row = range_set_scan (selected, 0);
1146 range_set_destroy (selected);
1148 if (row <= psppire_dict_get_var_cnt (dict))
1151 if (psppire_dict_generate_name (dict, name, sizeof name))
1152 psppire_dict_insert_variable (dict, row, name);
1157 psppire_var_sheet_init (PsppireVarSheet *obj)
1159 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1160 PsppSheetViewColumn *column;
1165 obj->format_use = PSPPIRE_TYPE_FMT_USE;
1166 obj->may_create_vars = TRUE;
1167 obj->may_delete_vars = TRUE;
1169 obj->scroll_to_bottom_signal = 0;
1171 obj->container = NULL;
1172 obj->dispose_has_run = FALSE;
1175 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1177 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1178 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1179 g_signal_connect (list->data, "editing-started",
1180 G_CALLBACK (on_name_column_editing_started), NULL);
1183 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1184 add_popup_menu (obj, column, on_type_click);
1186 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1188 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1190 add_text_column (obj, VS_LABEL, _("Label"), 20);
1192 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1193 add_popup_menu (obj, column, on_value_labels_click);
1195 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1196 add_popup_menu (obj, column, on_missing_values_click);
1198 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1200 add_combo_column (obj, VS_ALIGN, _("Align"), 6,
1201 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1202 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1203 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1206 add_combo_column (obj, VS_MEASURE, _("Measure"), 10,
1207 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1208 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1209 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1212 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1213 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1214 PSPP_SHEET_SELECTION_MULTIPLE);
1216 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1217 g_signal_connect (obj, "query-tooltip",
1218 G_CALLBACK (on_query_var_tooltip), NULL);
1219 g_signal_connect (obj, "button-press-event",
1220 G_CALLBACK (on_button_pressed), NULL);
1221 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1223 obj->builder = builder_new ("var-sheet.ui");
1225 action = get_action_assert (obj->builder, "edit_clear-variables");
1226 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1228 gtk_action_set_sensitive (action, FALSE);
1229 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1230 "changed", G_CALLBACK (on_selection_changed), NULL);
1232 action = get_action_assert (obj->builder, "edit_insert-variable");
1233 gtk_action_set_sensitive (action, FALSE);
1234 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1239 psppire_var_sheet_new (void)
1241 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1245 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1247 return var_sheet->dict;
1251 refresh_model (PsppireVarSheet *var_sheet)
1253 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1255 if (var_sheet->dict != NULL)
1257 PsppireEmptyListStore *store;
1260 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1261 + var_sheet->may_create_vars);
1262 store = psppire_empty_list_store_new (n_rows);
1263 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1264 GTK_TREE_MODEL (store));
1265 g_object_unref (store);
1270 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1272 PsppireEmptyListStore *store;
1275 g_return_if_fail (dict == var_sheet->dict);
1277 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1278 PSPP_SHEET_VIEW (var_sheet)));
1279 g_return_if_fail (store != NULL);
1281 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1282 + var_sheet->may_create_vars);
1283 psppire_empty_list_store_set_n_rows (store, n_rows);
1284 psppire_empty_list_store_row_inserted (store, row);
1288 on_var_deleted (PsppireDict *dict,
1289 const struct variable *var, int dict_idx, int case_idx,
1290 PsppireVarSheet *var_sheet)
1292 PsppireEmptyListStore *store;
1295 g_return_if_fail (dict == var_sheet->dict);
1297 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1298 PSPP_SHEET_VIEW (var_sheet)));
1299 g_return_if_fail (store != NULL);
1301 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1302 + var_sheet->may_create_vars);
1303 psppire_empty_list_store_set_n_rows (store, n_rows);
1304 psppire_empty_list_store_row_deleted (store, dict_idx);
1308 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1310 g_return_if_fail (dict == var_sheet->dict);
1311 refresh_model (var_sheet);
1315 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1318 if (var_sheet->dict != NULL)
1322 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1324 if (var_sheet->dict_signals[i])
1325 g_signal_handler_disconnect (var_sheet->dict,
1326 var_sheet->dict_signals[i]);
1328 var_sheet->dict_signals[i] = 0;
1331 g_object_unref (var_sheet->dict);
1334 var_sheet->dict = dict;
1338 g_object_ref (dict);
1340 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1341 = g_signal_connect (dict, "backend-changed",
1342 G_CALLBACK (on_backend_changed), var_sheet);
1344 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1345 = g_signal_connect (dict, "variable-inserted",
1346 G_CALLBACK (on_var_inserted), var_sheet);
1348 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1349 = g_signal_connect (dict, "variable-deleted",
1350 G_CALLBACK (on_var_deleted), var_sheet);
1353 refresh_model (var_sheet);
1357 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1359 return var_sheet->may_create_vars;
1363 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1364 gboolean may_create_vars)
1366 if (var_sheet->may_create_vars != may_create_vars)
1368 PsppireEmptyListStore *store;
1371 var_sheet->may_create_vars = may_create_vars;
1373 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1374 PSPP_SHEET_VIEW (var_sheet)));
1375 g_return_if_fail (store != NULL);
1377 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1378 + var_sheet->may_create_vars);
1379 psppire_empty_list_store_set_n_rows (store, n_rows);
1381 if (may_create_vars)
1382 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1384 psppire_empty_list_store_row_deleted (store, n_rows);
1386 on_selection_changed (pspp_sheet_view_get_selection (
1387 PSPP_SHEET_VIEW (var_sheet)), NULL);
1392 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1394 return var_sheet->may_delete_vars;
1398 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1399 gboolean may_delete_vars)
1401 if (var_sheet->may_delete_vars != may_delete_vars)
1403 var_sheet->may_delete_vars = may_delete_vars;
1404 on_selection_changed (pspp_sheet_view_get_selection (
1405 PSPP_SHEET_VIEW (var_sheet)), NULL);
1410 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1412 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1415 path = gtk_tree_path_new_from_indices (dict_index, -1);
1416 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1417 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1418 gtk_tree_path_free (path);
1422 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1424 if (var_sheet->uim == NULL)
1426 var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1428 GTK_TYPE_UI_MANAGER));
1429 g_object_ref (var_sheet->uim);
1432 return var_sheet->uim;