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/psppire-var-store.h"
35 #include "ui/gui/val-labs-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);
401 char *text = missing_values_to_string (var_sheet->dict, var, NULL);
411 set_spin_cell (cell, var_get_display_width (var), 1, 2 * MAX_STRING, 1);
416 "text", alignment_to_string (var_get_alignment (var)),
423 "text", measure_to_string (var_get_measure (var)),
430 static struct variable *
431 path_string_to_variable (PsppireVarSheet *var_sheet, gchar *path_string)
437 path = gtk_tree_path_new_from_string (path_string);
438 row = gtk_tree_path_get_indices (path)[0];
439 gtk_tree_path_free (path);
441 dict = psppire_var_sheet_get_dictionary (var_sheet);
442 g_return_val_if_fail (dict != NULL, NULL);
444 return psppire_dict_get_variable (dict, row);
448 on_type_click (PsppireCellRendererButton *cell,
450 PsppireVarSheet *var_sheet)
452 var_sheet->var_type_dialog->pv = path_string_to_variable (var_sheet, path);
453 var_type_dialog_show (var_sheet->var_type_dialog);
457 on_value_labels_click (PsppireCellRendererButton *cell,
459 PsppireVarSheet *var_sheet)
461 struct variable *var = path_string_to_variable (var_sheet, path);
462 val_labs_dialog_set_target_variable (var_sheet->val_labs_dialog, var);
463 val_labs_dialog_show (var_sheet->val_labs_dialog);
467 on_missing_values_click (PsppireCellRendererButton *cell,
469 PsppireVarSheet *var_sheet)
471 var_sheet->missing_val_dialog->pv = path_string_to_variable (var_sheet,
473 missing_val_dialog_show (var_sheet->missing_val_dialog);
477 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
481 g_object_set (G_OBJECT (renderer),
482 PSPPIRE_IS_CELL_RENDERER_BUTTON (renderer) ? "label" : "text",
483 string, (void *) NULL);
484 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
485 NULL, NULL, NULL, &width, NULL);
490 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
497 ds_put_byte_multiple (&s, '0', char_cnt);
498 ds_put_byte (&s, ' ');
499 width = get_string_width (treeview, renderer, ds_cstr (&s));
505 static PsppSheetViewColumn *
506 add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer,
507 enum vs_column column_id,
508 const char *title, int width)
510 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
511 int title_width, content_width;
512 PsppSheetViewColumn *column;
514 column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL);
515 g_object_set_data (G_OBJECT (column), "column-number",
516 GINT_TO_POINTER (column_id) + 1);
518 pspp_sheet_view_column_set_cell_data_func (
519 column, renderer, render_var_cell, var_sheet, NULL);
521 title_width = get_string_width (sheet_view, renderer, title);
522 content_width = get_monospace_width (sheet_view, renderer, width);
523 g_object_set_data (G_OBJECT (column), "content-width",
524 GINT_TO_POINTER (content_width));
526 pspp_sheet_view_column_set_fixed_width (column,
527 MAX (title_width, content_width));
528 pspp_sheet_view_column_set_resizable (column, TRUE);
530 pspp_sheet_view_append_column (sheet_view, column);
532 g_signal_connect (renderer, "edited",
533 G_CALLBACK (on_var_column_edited),
535 g_object_set_data (G_OBJECT (renderer), "column-id",
536 GINT_TO_POINTER (column_id));
537 g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet);
542 static PsppSheetViewColumn *
543 add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
544 const char *title, int width)
546 return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (),
547 column_id, title, width);
550 static PsppSheetViewColumn *
551 add_spin_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
552 const char *title, int width)
554 return add_var_sheet_column (var_sheet, gtk_cell_renderer_spin_new (),
555 column_id, title, width);
558 static PsppSheetViewColumn *
559 add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
560 const char *title, int width,
563 GtkCellRenderer *cell;
568 store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
569 va_start (args, width);
570 while ((name = va_arg (args, const char *)) != NULL)
572 int value = va_arg (args, int);
573 gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
580 cell = gtk_cell_renderer_combo_new ();
583 "model", GTK_TREE_MODEL (store),
587 return add_var_sheet_column (var_sheet, cell, column_id, title, width);
592 add_popup_menu (PsppireVarSheet *var_sheet,
593 PsppSheetViewColumn *column,
594 void (*on_click) (PsppireCellRendererButton *,
596 PsppireVarSheet *var_sheet))
598 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
599 const char *button_label = "...";
600 GtkCellRenderer *button_renderer;
603 button_renderer = psppire_cell_renderer_button_new ();
604 g_object_set (button_renderer,
605 "label", button_label,
608 g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click),
610 pspp_sheet_view_column_pack_start (column, button_renderer, FALSE);
611 pspp_sheet_view_column_set_cell_data_func (
612 column, button_renderer, render_popup_cell, var_sheet, NULL);
614 content_width = GPOINTER_TO_INT (g_object_get_data (
615 G_OBJECT (column), "content-width"));
616 content_width += get_string_width (sheet_view, button_renderer,
618 if (content_width > pspp_sheet_view_column_get_fixed_width (column))
619 pspp_sheet_view_column_set_fixed_width (column, content_width);
623 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
624 gint wx, gint wy, size_t *row, size_t *column)
626 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
630 PsppSheetViewColumn *tree_column;
631 GtkTreeModel *tree_model;
635 /* Check that WIDGET is really visible on the screen before we
636 do anything else. This is a bug fix for a sticky situation:
637 when text_data_import_assistant() returns, it frees the data
638 necessary to compose the tool tip message, but there may be
639 a tool tip under preparation at that point (even if there is
640 no visible tool tip) that will call back into us a little
641 bit later. Perhaps the correct solution to this problem is
642 to make the data related to the tool tips part of a GObject
643 that only gets destroyed when all references are released,
644 but this solution appears to be effective too. */
645 if (!gtk_widget_get_mapped (widget))
648 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
650 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
651 &path, &tree_column, NULL, NULL))
654 column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number");
655 if (column_ptr == NULL)
657 *column = GPOINTER_TO_INT (column_ptr) - 1;
659 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
662 tree_model = pspp_sheet_view_get_model (tree_view);
663 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
664 gtk_tree_path_free (path);
668 *row = GPOINTER_TO_INT (iter.user_data);
673 on_query_var_tooltip (GtkWidget *widget, gint wx, gint wy,
674 gboolean keyboard_mode UNUSED,
675 GtkTooltip *tooltip, gpointer *user_data UNUSED)
677 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
679 struct variable *var;
682 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
685 dict = psppire_var_sheet_get_dictionary (var_sheet);
686 g_return_val_if_fail (dict != NULL, FALSE);
688 if (row >= psppire_dict_get_var_cnt (dict))
690 gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a "
695 var = psppire_dict_get_variable (dict, row);
696 g_return_val_if_fail (var != NULL, FALSE);
702 char text[FMT_STRING_LEN_MAX + 1];
704 fmt_to_string (var_get_print_format (var), text);
705 gtk_tooltip_set_text (tooltip, text);
710 if (var_has_value_labels (var))
712 const struct val_labs *vls = var_get_value_labels (var);
713 const struct val_lab **labels = val_labs_sorted (vls);
718 for (i = 0; i < val_labs_count (vls); i++)
720 const struct val_lab *vl = labels[i];
723 if (i >= 10 || ds_length (&s) > 500)
725 ds_put_cstr (&s, "...");
729 vstr = value_to_text (vl->value, var);
730 ds_put_format (&s, _("{%s, %s}\n"), vstr,
731 val_lab_get_escaped_label (vl));
735 ds_chomp_byte (&s, '\n');
737 gtk_tooltip_set_text (tooltip, ds_cstr (&s));
748 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
750 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
753 menu = get_widget_assert (var_sheet->builder, "varsheet-variable-popup");
754 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
758 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
760 do_popup_menu (widget, 0, gtk_get_current_event_time ());
764 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
765 gpointer user_data UNUSED)
767 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
769 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
771 PsppSheetSelection *selection;
773 selection = pspp_sheet_view_get_selection (sheet_view);
774 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
778 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
779 &path, NULL, NULL, NULL))
781 pspp_sheet_selection_unselect_all (selection);
782 pspp_sheet_selection_select_path (selection, path);
783 gtk_tree_path_free (path);
787 do_popup_menu (widget, event->button, event->time);
795 psppire_fmt_use_get_type (void)
797 static GType etype = 0;
800 static const GEnumValue values[] =
802 { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
803 { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
807 etype = g_enum_register_static
808 (g_intern_static_string ("PsppireFmtUse"), values);
817 PROP_MAY_CREATE_VARS,
818 PROP_MAY_DELETE_VARS,
824 psppire_var_sheet_set_property (GObject *object,
829 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
833 case PROP_DICTIONARY:
834 psppire_var_sheet_set_dictionary (obj,
835 PSPPIRE_DICT (g_value_get_object (
839 case PROP_MAY_CREATE_VARS:
840 psppire_var_sheet_set_may_create_vars (obj,
841 g_value_get_boolean (value));
844 case PROP_MAY_DELETE_VARS:
845 psppire_var_sheet_set_may_delete_vars (obj,
846 g_value_get_boolean (value));
849 case PROP_FORMAT_TYPE:
850 obj->format_use = g_value_get_enum (value);
853 case PROP_UI_MANAGER:
855 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
861 psppire_var_sheet_get_property (GObject *object,
866 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
870 case PROP_DICTIONARY:
871 g_value_set_object (value,
872 G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
875 case PROP_MAY_CREATE_VARS:
876 g_value_set_boolean (value, obj->may_create_vars);
879 case PROP_MAY_DELETE_VARS:
880 g_value_set_boolean (value, obj->may_delete_vars);
883 case PROP_FORMAT_TYPE:
884 g_value_set_enum (value, obj->format_use);
887 case PROP_UI_MANAGER:
888 g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj));
892 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
898 psppire_var_sheet_realize (GtkWidget *w)
900 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (w);
903 GTK_WIDGET_CLASS (psppire_var_sheet_parent_class)->realize (w);
905 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (w));
906 var_sheet->val_labs_dialog = val_labs_dialog_create (toplevel);
907 var_sheet->missing_val_dialog = missing_val_dialog_create (toplevel);
908 var_sheet->var_type_dialog = var_type_dialog_create (toplevel);
912 psppire_var_sheet_destroy (GtkObject *obj)
914 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
916 GTK_OBJECT_CLASS (psppire_var_sheet_parent_class)->destroy (obj);
918 psppire_var_sheet_set_dictionary (var_sheet, NULL);
920 if (var_sheet->val_labs_dialog)
922 g_object_unref (var_sheet->val_labs_dialog);
923 var_sheet->val_labs_dialog = NULL;
926 if (var_sheet->missing_val_dialog)
928 g_object_unref (var_sheet->missing_val_dialog);
929 var_sheet->missing_val_dialog = NULL;
932 if (var_sheet->var_type_dialog)
934 g_object_unref (var_sheet->var_type_dialog);
935 var_sheet->var_type_dialog = NULL;
940 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
942 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
943 GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (class);
944 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
947 gobject_class->set_property = psppire_var_sheet_set_property;
948 gobject_class->get_property = psppire_var_sheet_get_property;
950 widget_class->realize = psppire_var_sheet_realize;
952 gtk_object_class->destroy = psppire_var_sheet_destroy;
954 g_signal_new ("var-double-clicked",
955 G_OBJECT_CLASS_TYPE (gobject_class),
958 g_signal_accumulator_true_handled, NULL,
959 psppire_marshal_BOOLEAN__INT,
960 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
962 pspec = g_param_spec_object ("dictionary",
963 "Dictionary displayed by the sheet",
964 "The PsppireDict that the sheet displays "
965 "may allow the user to edit",
968 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
970 pspec = g_param_spec_boolean ("may-create-vars",
971 "May create variables",
972 "Whether the user may create more variables",
975 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
977 pspec = g_param_spec_boolean ("may-delete-vars",
978 "May delete variables",
979 "Whether the user may delete variables",
982 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
984 pspec = g_param_spec_enum ("format-use",
985 "Use of variable format",
986 ("Whether variables have input or output "
988 PSPPIRE_TYPE_FMT_USE,
991 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
993 pspec = g_param_spec_object ("ui-manager",
995 "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.",
998 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1002 render_row_number_cell (PsppSheetViewColumn *tree_column,
1003 GtkCellRenderer *cell,
1004 GtkTreeModel *model,
1008 PsppireVarSheet *var_sheet = user_data;
1009 GValue gvalue = { 0, };
1012 row = GPOINTER_TO_INT (iter->user_data);
1014 g_value_init (&gvalue, G_TYPE_INT);
1015 g_value_set_int (&gvalue, row + 1);
1016 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1017 g_value_unset (&gvalue);
1019 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1020 g_object_set (cell, "editable", TRUE, NULL);
1022 g_object_set (cell, "editable", FALSE, NULL);
1026 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1028 PsppireVarSheet *var_sheet)
1032 g_return_if_fail (var_sheet->dict != NULL);
1034 path = gtk_tree_path_new_from_string (path_string);
1035 if (gtk_tree_path_get_depth (path) == 1)
1037 gint *indices = gtk_tree_path_get_indices (path);
1038 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1041 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1042 indices[0], &handled);
1045 gtk_tree_path_free (path);
1048 static PsppSheetViewColumn *
1049 make_row_number_column (PsppireVarSheet *var_sheet)
1051 PsppSheetViewColumn *column;
1052 GtkCellRenderer *renderer;
1054 renderer = psppire_cell_renderer_button_new ();
1055 g_object_set (renderer, "xalign", 1.0, NULL);
1056 g_signal_connect (renderer, "double-clicked",
1057 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1060 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1062 pspp_sheet_view_column_set_cell_data_func (
1063 column, renderer, render_row_number_cell, var_sheet, NULL);
1064 pspp_sheet_view_column_set_fixed_width (column, 50);
1069 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1071 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1072 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1073 PsppireDict *dict = var_sheet->dict;
1074 const struct range_set_node *node;
1075 struct range_set *selected;
1077 selected = pspp_sheet_selection_get_range_set (selection);
1078 for (node = range_set_last (selected); node != NULL;
1079 node = range_set_prev (selected, node))
1083 for (i = 1; i <= range_set_node_get_width (node); i++)
1085 unsigned long row = range_set_node_get_end (node) - i;
1086 if (row >= 0 && row < psppire_dict_get_var_cnt (dict))
1087 psppire_dict_delete_variables (dict, row, 1);
1090 range_set_destroy (selected);
1094 on_selection_changed (PsppSheetSelection *selection,
1095 gpointer user_data UNUSED)
1097 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1098 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1099 gint n_selected_rows;
1100 gboolean may_delete;
1104 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1106 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1107 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1108 && n_selected_rows > 0));
1110 switch (n_selected_rows)
1117 /* The row used for inserting new variables cannot be deleted. */
1118 path = gtk_tree_path_new_from_indices (
1119 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1120 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1121 gtk_tree_path_free (path);
1128 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1129 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1133 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1135 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1136 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1137 PsppireDict *dict = var_sheet->dict;
1138 struct range_set *selected;
1141 selected = pspp_sheet_selection_get_range_set (selection);
1142 row = range_set_scan (selected, 0);
1143 range_set_destroy (selected);
1145 if (row <= psppire_dict_get_var_cnt (dict))
1148 if (psppire_dict_generate_name (dict, name, sizeof name))
1149 psppire_dict_insert_variable (dict, row, name);
1154 psppire_var_sheet_init (PsppireVarSheet *obj)
1156 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1157 PsppSheetViewColumn *column;
1162 obj->format_use = PSPPIRE_TYPE_FMT_USE;
1163 obj->may_create_vars = TRUE;
1164 obj->may_delete_vars = TRUE;
1166 obj->scroll_to_bottom_signal = 0;
1168 obj->container = NULL;
1170 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1172 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1173 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1174 g_signal_connect (list->data, "editing-started",
1175 G_CALLBACK (on_name_column_editing_started), NULL);
1178 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1179 add_popup_menu (obj, column, on_type_click);
1181 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1183 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1185 add_text_column (obj, VS_LABEL, _("Label"), 20);
1187 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1188 add_popup_menu (obj, column, on_value_labels_click);
1190 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1191 add_popup_menu (obj, column, on_missing_values_click);
1193 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1195 add_combo_column (obj, VS_ALIGN, _("Align"), 6,
1196 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1197 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1198 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1201 add_combo_column (obj, VS_MEASURE, _("Measure"), 10,
1202 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1203 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1204 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1207 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1208 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1209 PSPP_SHEET_SELECTION_MULTIPLE);
1211 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1212 g_signal_connect (obj, "query-tooltip",
1213 G_CALLBACK (on_query_var_tooltip), NULL);
1214 g_signal_connect (obj, "button-press-event",
1215 G_CALLBACK (on_button_pressed), NULL);
1216 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1218 obj->builder = builder_new ("var-sheet.ui");
1220 action = get_action_assert (obj->builder, "edit_clear-variables");
1221 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1223 gtk_action_set_sensitive (action, FALSE);
1224 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1225 "changed", G_CALLBACK (on_selection_changed), NULL);
1227 action = get_action_assert (obj->builder, "edit_insert-variable");
1228 gtk_action_set_sensitive (action, FALSE);
1229 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1234 psppire_var_sheet_new (void)
1236 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1240 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1242 return var_sheet->dict;
1246 refresh_model (PsppireVarSheet *var_sheet)
1248 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1250 if (var_sheet->dict != NULL)
1252 PsppireEmptyListStore *store;
1255 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1256 + var_sheet->may_create_vars);
1257 store = psppire_empty_list_store_new (n_rows);
1258 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1259 GTK_TREE_MODEL (store));
1260 g_object_unref (store);
1265 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1267 PsppireEmptyListStore *store;
1270 g_return_if_fail (dict == var_sheet->dict);
1272 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1273 PSPP_SHEET_VIEW (var_sheet)));
1274 g_return_if_fail (store != NULL);
1276 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1277 + var_sheet->may_create_vars);
1278 psppire_empty_list_store_set_n_rows (store, n_rows);
1279 psppire_empty_list_store_row_inserted (store, row);
1283 on_var_deleted (PsppireDict *dict,
1284 const struct variable *var, int dict_idx, int case_idx,
1285 PsppireVarSheet *var_sheet)
1287 PsppireEmptyListStore *store;
1290 g_return_if_fail (dict == var_sheet->dict);
1292 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1293 PSPP_SHEET_VIEW (var_sheet)));
1294 g_return_if_fail (store != NULL);
1296 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1297 + var_sheet->may_create_vars);
1298 psppire_empty_list_store_set_n_rows (store, n_rows);
1299 psppire_empty_list_store_row_deleted (store, dict_idx);
1303 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1305 g_return_if_fail (dict == var_sheet->dict);
1306 refresh_model (var_sheet);
1310 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1320 if (var_sheet->dict != NULL)
1322 if (var_sheet->dict_signals)
1326 for (i = 0; i < N_SIGNALS; i++)
1327 g_signal_handler_disconnect (var_sheet->dict,
1328 var_sheet->dict_signals[i]);
1330 g_free (var_sheet->dict_signals);
1331 var_sheet->dict_signals = NULL;
1333 g_object_unref (var_sheet->dict);
1336 var_sheet->dict = dict;
1340 g_object_ref (dict);
1342 var_sheet->dict_signals = g_malloc0 (
1343 N_SIGNALS * sizeof *var_sheet->dict_signals);
1345 var_sheet->dict_signals[BACKEND_CHANGED]
1346 = g_signal_connect (dict, "backend-changed",
1347 G_CALLBACK (on_backend_changed), var_sheet);
1349 var_sheet->dict_signals[VARIABLE_DELETED]
1350 = g_signal_connect (dict, "variable-inserted",
1351 G_CALLBACK (on_var_inserted), var_sheet);
1353 var_sheet->dict_signals[VARIABLE_INSERTED]
1354 = g_signal_connect (dict, "variable-deleted",
1355 G_CALLBACK (on_var_deleted), var_sheet);
1357 refresh_model (var_sheet);
1361 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1363 return var_sheet->may_create_vars;
1367 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1368 gboolean may_create_vars)
1370 if (var_sheet->may_create_vars != may_create_vars)
1372 PsppireEmptyListStore *store;
1375 var_sheet->may_create_vars = may_create_vars;
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 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1382 + var_sheet->may_create_vars);
1383 psppire_empty_list_store_set_n_rows (store, n_rows);
1385 if (may_create_vars)
1386 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1388 psppire_empty_list_store_row_deleted (store, n_rows);
1390 on_selection_changed (pspp_sheet_view_get_selection (
1391 PSPP_SHEET_VIEW (var_sheet)), NULL);
1396 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1398 return var_sheet->may_delete_vars;
1402 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1403 gboolean may_delete_vars)
1405 if (var_sheet->may_delete_vars != may_delete_vars)
1407 var_sheet->may_delete_vars = may_delete_vars;
1408 on_selection_changed (pspp_sheet_view_get_selection (
1409 PSPP_SHEET_VIEW (var_sheet)), NULL);
1414 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1416 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1419 path = gtk_tree_path_new_from_indices (dict_index, -1);
1420 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1421 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1422 gtk_tree_path_free (path);
1426 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1428 return GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1430 GTK_TYPE_UI_MANAGER));