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);
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));
749 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
751 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
754 menu = get_widget_assert (var_sheet->builder, "varsheet-variable-popup");
755 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
759 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
761 do_popup_menu (widget, 0, gtk_get_current_event_time ());
765 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
766 gpointer user_data UNUSED)
768 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
770 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
772 PsppSheetSelection *selection;
774 selection = pspp_sheet_view_get_selection (sheet_view);
775 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
779 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
780 &path, NULL, NULL, NULL))
782 pspp_sheet_selection_unselect_all (selection);
783 pspp_sheet_selection_select_path (selection, path);
784 gtk_tree_path_free (path);
788 do_popup_menu (widget, event->button, event->time);
796 psppire_fmt_use_get_type (void)
798 static GType etype = 0;
801 static const GEnumValue values[] =
803 { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
804 { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
808 etype = g_enum_register_static
809 (g_intern_static_string ("PsppireFmtUse"), values);
818 PROP_MAY_CREATE_VARS,
819 PROP_MAY_DELETE_VARS,
825 psppire_var_sheet_set_property (GObject *object,
830 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
834 case PROP_DICTIONARY:
835 psppire_var_sheet_set_dictionary (obj,
836 PSPPIRE_DICT (g_value_get_object (
840 case PROP_MAY_CREATE_VARS:
841 psppire_var_sheet_set_may_create_vars (obj,
842 g_value_get_boolean (value));
845 case PROP_MAY_DELETE_VARS:
846 psppire_var_sheet_set_may_delete_vars (obj,
847 g_value_get_boolean (value));
850 case PROP_FORMAT_TYPE:
851 obj->format_use = g_value_get_enum (value);
854 case PROP_UI_MANAGER:
856 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
862 psppire_var_sheet_get_property (GObject *object,
867 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
871 case PROP_DICTIONARY:
872 g_value_set_object (value,
873 G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
876 case PROP_MAY_CREATE_VARS:
877 g_value_set_boolean (value, obj->may_create_vars);
880 case PROP_MAY_DELETE_VARS:
881 g_value_set_boolean (value, obj->may_delete_vars);
884 case PROP_FORMAT_TYPE:
885 g_value_set_enum (value, obj->format_use);
888 case PROP_UI_MANAGER:
889 g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj));
893 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
899 psppire_var_sheet_realize (GtkWidget *w)
901 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (w);
904 GTK_WIDGET_CLASS (psppire_var_sheet_parent_class)->realize (w);
906 toplevel = GTK_WINDOW (gtk_widget_get_toplevel (w));
907 var_sheet->val_labs_dialog = val_labs_dialog_create (toplevel);
908 var_sheet->missing_val_dialog = missing_val_dialog_create (toplevel);
909 var_sheet->var_type_dialog = var_type_dialog_create (toplevel);
913 psppire_var_sheet_dispose (GObject *obj)
915 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
918 if (var_sheet->dispose_has_run)
921 var_sheet->dispose_has_run = TRUE;
923 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
924 if ( var_sheet->dict_signals[i])
925 g_signal_handler_disconnect (var_sheet->dict,
926 var_sheet->dict_signals[i]);
929 g_object_unref (var_sheet->dict);
932 g_object_unref (var_sheet->uim);
934 /* These dialogs are not GObjects (although they should be!)
935 But for now, unreffing them only causes a GCritical Error
936 so comment them out for now. (and accept the memory leakage)
938 g_object_unref (var_sheet->val_labs_dialog);
939 g_object_unref (var_sheet->missing_val_dialog);
940 g_object_unref (var_sheet->var_type_dialog);
943 G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
947 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
949 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
950 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
953 gobject_class->set_property = psppire_var_sheet_set_property;
954 gobject_class->get_property = psppire_var_sheet_get_property;
955 gobject_class->dispose = psppire_var_sheet_dispose;
957 widget_class->realize = psppire_var_sheet_realize;
959 g_signal_new ("var-double-clicked",
960 G_OBJECT_CLASS_TYPE (gobject_class),
963 g_signal_accumulator_true_handled, NULL,
964 psppire_marshal_BOOLEAN__INT,
965 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
967 pspec = g_param_spec_object ("dictionary",
968 "Dictionary displayed by the sheet",
969 "The PsppireDict that the sheet displays "
970 "may allow the user to edit",
973 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
975 pspec = g_param_spec_boolean ("may-create-vars",
976 "May create variables",
977 "Whether the user may create more variables",
980 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
982 pspec = g_param_spec_boolean ("may-delete-vars",
983 "May delete variables",
984 "Whether the user may delete variables",
987 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
989 pspec = g_param_spec_enum ("format-use",
990 "Use of variable format",
991 ("Whether variables have input or output "
993 PSPPIRE_TYPE_FMT_USE,
996 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
998 pspec = g_param_spec_object ("ui-manager",
1000 "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 GTK_TYPE_UI_MANAGER,
1003 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1007 render_row_number_cell (PsppSheetViewColumn *tree_column,
1008 GtkCellRenderer *cell,
1009 GtkTreeModel *model,
1013 PsppireVarSheet *var_sheet = user_data;
1014 GValue gvalue = { 0, };
1017 row = GPOINTER_TO_INT (iter->user_data);
1019 g_value_init (&gvalue, G_TYPE_INT);
1020 g_value_set_int (&gvalue, row + 1);
1021 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1022 g_value_unset (&gvalue);
1024 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1025 g_object_set (cell, "editable", TRUE, NULL);
1027 g_object_set (cell, "editable", FALSE, NULL);
1031 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1033 PsppireVarSheet *var_sheet)
1037 g_return_if_fail (var_sheet->dict != NULL);
1039 path = gtk_tree_path_new_from_string (path_string);
1040 if (gtk_tree_path_get_depth (path) == 1)
1042 gint *indices = gtk_tree_path_get_indices (path);
1043 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1046 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1047 indices[0], &handled);
1050 gtk_tree_path_free (path);
1053 static PsppSheetViewColumn *
1054 make_row_number_column (PsppireVarSheet *var_sheet)
1056 PsppSheetViewColumn *column;
1057 GtkCellRenderer *renderer;
1059 renderer = psppire_cell_renderer_button_new ();
1060 g_object_set (renderer, "xalign", 1.0, NULL);
1061 g_signal_connect (renderer, "double-clicked",
1062 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1065 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1067 pspp_sheet_view_column_set_cell_data_func (
1068 column, renderer, render_row_number_cell, var_sheet, NULL);
1069 pspp_sheet_view_column_set_fixed_width (column, 50);
1074 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1076 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1077 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1078 PsppireDict *dict = var_sheet->dict;
1079 const struct range_set_node *node;
1080 struct range_set *selected;
1082 selected = pspp_sheet_selection_get_range_set (selection);
1083 for (node = range_set_last (selected); node != NULL;
1084 node = range_set_prev (selected, node))
1088 for (i = 1; i <= range_set_node_get_width (node); i++)
1090 unsigned long row = range_set_node_get_end (node) - i;
1091 if (row >= 0 && row < psppire_dict_get_var_cnt (dict))
1092 psppire_dict_delete_variables (dict, row, 1);
1095 range_set_destroy (selected);
1099 on_selection_changed (PsppSheetSelection *selection,
1100 gpointer user_data UNUSED)
1102 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1103 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1104 gint n_selected_rows;
1105 gboolean may_delete;
1109 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1111 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1112 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1113 && n_selected_rows > 0));
1115 switch (n_selected_rows)
1122 /* The row used for inserting new variables cannot be deleted. */
1123 path = gtk_tree_path_new_from_indices (
1124 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1125 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1126 gtk_tree_path_free (path);
1133 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1134 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1138 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1140 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1141 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1142 PsppireDict *dict = var_sheet->dict;
1143 struct range_set *selected;
1146 selected = pspp_sheet_selection_get_range_set (selection);
1147 row = range_set_scan (selected, 0);
1148 range_set_destroy (selected);
1150 if (row <= psppire_dict_get_var_cnt (dict))
1153 if (psppire_dict_generate_name (dict, name, sizeof name))
1154 psppire_dict_insert_variable (dict, row, name);
1159 psppire_var_sheet_init (PsppireVarSheet *obj)
1161 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1162 PsppSheetViewColumn *column;
1167 obj->format_use = FMT_FOR_OUTPUT;
1168 obj->may_create_vars = TRUE;
1169 obj->may_delete_vars = TRUE;
1171 obj->scroll_to_bottom_signal = 0;
1173 obj->container = NULL;
1174 obj->dispose_has_run = FALSE;
1177 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1179 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1180 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1181 g_signal_connect (list->data, "editing-started",
1182 G_CALLBACK (on_name_column_editing_started), NULL);
1185 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1186 add_popup_menu (obj, column, on_type_click);
1188 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1190 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1192 add_text_column (obj, VS_LABEL, _("Label"), 20);
1194 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1195 add_popup_menu (obj, column, on_value_labels_click);
1197 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1198 add_popup_menu (obj, column, on_missing_values_click);
1200 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1202 add_combo_column (obj, VS_ALIGN, _("Align"), 6,
1203 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1204 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1205 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1208 add_combo_column (obj, VS_MEASURE, _("Measure"), 10,
1209 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1210 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1211 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1214 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1215 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1216 PSPP_SHEET_SELECTION_MULTIPLE);
1218 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1219 g_signal_connect (obj, "query-tooltip",
1220 G_CALLBACK (on_query_var_tooltip), NULL);
1221 g_signal_connect (obj, "button-press-event",
1222 G_CALLBACK (on_button_pressed), NULL);
1223 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1225 obj->builder = builder_new ("var-sheet.ui");
1227 action = get_action_assert (obj->builder, "edit_clear-variables");
1228 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1230 gtk_action_set_sensitive (action, FALSE);
1231 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1232 "changed", G_CALLBACK (on_selection_changed), NULL);
1234 action = get_action_assert (obj->builder, "edit_insert-variable");
1235 gtk_action_set_sensitive (action, FALSE);
1236 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1241 psppire_var_sheet_new (void)
1243 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1247 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1249 return var_sheet->dict;
1253 refresh_model (PsppireVarSheet *var_sheet)
1255 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1257 if (var_sheet->dict != NULL)
1259 PsppireEmptyListStore *store;
1262 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1263 + var_sheet->may_create_vars);
1264 store = psppire_empty_list_store_new (n_rows);
1265 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1266 GTK_TREE_MODEL (store));
1267 g_object_unref (store);
1272 on_var_changed (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1274 PsppireEmptyListStore *store;
1276 g_return_if_fail (dict == var_sheet->dict);
1278 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1279 PSPP_SHEET_VIEW (var_sheet)));
1280 g_return_if_fail (store != NULL);
1282 psppire_empty_list_store_row_changed (store, row);
1286 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1288 PsppireEmptyListStore *store;
1291 g_return_if_fail (dict == var_sheet->dict);
1293 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1294 PSPP_SHEET_VIEW (var_sheet)));
1295 g_return_if_fail (store != NULL);
1297 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1298 + var_sheet->may_create_vars);
1299 psppire_empty_list_store_set_n_rows (store, n_rows);
1300 psppire_empty_list_store_row_inserted (store, row);
1304 on_var_deleted (PsppireDict *dict,
1305 const struct variable *var, int dict_idx, int case_idx,
1306 PsppireVarSheet *var_sheet)
1308 PsppireEmptyListStore *store;
1311 g_return_if_fail (dict == var_sheet->dict);
1313 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1314 PSPP_SHEET_VIEW (var_sheet)));
1315 g_return_if_fail (store != NULL);
1317 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1318 + var_sheet->may_create_vars);
1319 psppire_empty_list_store_set_n_rows (store, n_rows);
1320 psppire_empty_list_store_row_deleted (store, dict_idx);
1324 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1326 g_return_if_fail (dict == var_sheet->dict);
1327 refresh_model (var_sheet);
1331 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1334 if (var_sheet->dict != NULL)
1338 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1340 if (var_sheet->dict_signals[i])
1341 g_signal_handler_disconnect (var_sheet->dict,
1342 var_sheet->dict_signals[i]);
1344 var_sheet->dict_signals[i] = 0;
1347 g_object_unref (var_sheet->dict);
1350 var_sheet->dict = dict;
1354 g_object_ref (dict);
1356 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1357 = g_signal_connect (dict, "backend-changed",
1358 G_CALLBACK (on_backend_changed), var_sheet);
1360 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED]
1361 = g_signal_connect (dict, "variable-changed",
1362 G_CALLBACK (on_var_changed), var_sheet);
1364 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1365 = g_signal_connect (dict, "variable-inserted",
1366 G_CALLBACK (on_var_inserted), var_sheet);
1368 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1369 = g_signal_connect (dict, "variable-deleted",
1370 G_CALLBACK (on_var_deleted), var_sheet);
1373 refresh_model (var_sheet);
1377 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1379 return var_sheet->may_create_vars;
1383 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1384 gboolean may_create_vars)
1386 if (var_sheet->may_create_vars != may_create_vars)
1388 PsppireEmptyListStore *store;
1391 var_sheet->may_create_vars = may_create_vars;
1393 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1394 PSPP_SHEET_VIEW (var_sheet)));
1395 g_return_if_fail (store != NULL);
1397 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1398 + var_sheet->may_create_vars);
1399 psppire_empty_list_store_set_n_rows (store, n_rows);
1401 if (may_create_vars)
1402 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1404 psppire_empty_list_store_row_deleted (store, n_rows);
1406 on_selection_changed (pspp_sheet_view_get_selection (
1407 PSPP_SHEET_VIEW (var_sheet)), NULL);
1412 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1414 return var_sheet->may_delete_vars;
1418 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1419 gboolean may_delete_vars)
1421 if (var_sheet->may_delete_vars != may_delete_vars)
1423 var_sheet->may_delete_vars = may_delete_vars;
1424 on_selection_changed (pspp_sheet_view_get_selection (
1425 PSPP_SHEET_VIEW (var_sheet)), NULL);
1430 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1432 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1435 path = gtk_tree_path_new_from_indices (dict_index, -1);
1436 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1437 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1438 gtk_tree_path_free (path);
1442 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1444 if (var_sheet->uim == NULL)
1446 var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1448 GTK_TYPE_UI_MANAGER));
1449 g_object_ref (var_sheet->uim);
1452 return var_sheet->uim;