1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2011, 2012, 2013 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "ui/gui/psppire-var-sheet.h"
21 #include "data/format.h"
22 #include "data/value-labels.h"
23 #include "libpspp/range-set.h"
24 #include "ui/gui/builder-wrapper.h"
25 #include "ui/gui/helper.h"
26 #include "ui/gui/missing-val-dialog.h"
27 #include "ui/gui/pspp-sheet-selection.h"
28 #include "ui/gui/psppire-cell-renderer-button.h"
29 #include "ui/gui/psppire-data-editor.h"
30 #include "ui/gui/psppire-data-window.h"
31 #include "ui/gui/psppire-dialog-action-var-info.h"
32 #include "ui/gui/psppire-dictview.h"
33 #include "ui/gui/psppire-empty-list-store.h"
34 #include "ui/gui/psppire-marshal.h"
35 #include "ui/gui/val-labs-dialog.h"
36 #include "ui/gui/var-type-dialog.h"
37 #include "ui/gui/var-display.h"
38 #include "ui/gui/var-type-dialog.h"
40 #include "gl/intprops.h"
43 #define _(msgid) gettext (msgid)
44 #define N_(msgid) msgid
61 G_DEFINE_TYPE (PsppireVarSheet, psppire_var_sheet, PSPP_TYPE_SHEET_VIEW);
64 set_spin_cell (GtkCellRenderer *cell, int value, int min, int max, int step)
66 char text[INT_BUFSIZE_BOUND (int)];
67 GtkAdjustment *adjust;
70 adjust = GTK_ADJUSTMENT (gtk_adjustment_new (value, min, max,
75 sprintf (text, "%d", value);
84 error_dialog (GtkWindow *w, gchar *primary_text, gchar *secondary_text)
87 gtk_message_dialog_new (w,
88 GTK_DIALOG_DESTROY_WITH_PARENT,
90 GTK_BUTTONS_CLOSE, "%s", primary_text);
92 g_object_set (dialog, "icon-name", "psppicon", NULL);
94 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
95 "%s", secondary_text);
97 gtk_dialog_run (GTK_DIALOG (dialog));
99 gtk_widget_destroy (dialog);
103 on_name_column_editing_started (GtkCellRenderer *cell,
104 GtkCellEditable *editable,
108 PsppireVarSheet *var_sheet = g_object_get_data (G_OBJECT (cell), "var-sheet");
109 PsppireDict *dict = psppire_var_sheet_get_dictionary (var_sheet);
111 g_return_if_fail (var_sheet);
112 g_return_if_fail (dict);
114 if (GTK_IS_ENTRY (editable))
116 GtkEntry *entry = GTK_ENTRY (editable);
117 if (gtk_entry_get_text (entry)[0] == '\0')
120 if (psppire_dict_generate_name (dict, name, sizeof name))
121 gtk_entry_set_text (entry, name);
127 scroll_to_bottom (GtkWidget *widget,
128 GtkRequisition *requisition,
129 gpointer unused UNUSED)
131 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
132 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
133 GtkAdjustment *vadjust;
135 vadjust = pspp_sheet_view_get_vadjustment (sheet_view);
136 gtk_adjustment_set_value (vadjust, gtk_adjustment_get_upper (vadjust));
138 if (var_sheet->scroll_to_bottom_signal)
140 g_signal_handler_disconnect (var_sheet,
141 var_sheet->scroll_to_bottom_signal);
142 var_sheet->scroll_to_bottom_signal = 0;
146 static struct variable *
147 path_string_to_var (PsppireVarSheet *var_sheet, gchar *path_string)
152 path = gtk_tree_path_new_from_string (path_string);
153 row = gtk_tree_path_get_indices (path)[0];
154 gtk_tree_path_free (path);
156 return psppire_dict_get_variable (var_sheet->dict, row);
160 on_var_column_edited (GtkCellRendererText *cell,
165 PsppireVarSheet *var_sheet = user_data;
166 GtkWindow *window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (var_sheet)));
167 struct dictionary *dict = var_sheet->dict->dict;
168 enum vs_column column_id;
169 struct variable *var;
172 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell),
175 var = path_string_to_var (var_sheet, path_string);
178 g_return_if_fail (column_id == VS_NAME);
180 if (!dict_id_is_valid (dict, new_text, false))
181 error_dialog (window,
182 g_strdup (_("Cannot create variable.")),
183 g_strdup_printf (_("\"%s\" is not a valid variable "
184 "name."), new_text));
185 else if (dict_lookup_var (dict, new_text) != NULL)
186 error_dialog (window,
187 g_strdup (_("Cannot create variable.")),
188 g_strdup_printf (_("This dictionary already contains "
189 "a variable named \"%s\"."),
193 dict_create_var (var_sheet->dict->dict, new_text, 0);
194 if (!var_sheet->scroll_to_bottom_signal)
196 gtk_widget_queue_resize (GTK_WIDGET (var_sheet));
197 var_sheet->scroll_to_bottom_signal =
198 g_signal_connect (var_sheet, "size-request",
199 G_CALLBACK (scroll_to_bottom), NULL);
209 if (!dict_id_is_valid (dict, new_text, false))
210 error_dialog (window,
211 g_strdup (_("Cannot rename variable.")),
212 g_strdup_printf (_("\"%s\" is not a valid variable "
213 "name."), new_text));
214 else if (dict_lookup_var (dict, new_text) != NULL
215 && dict_lookup_var (dict, new_text) != var)
216 error_dialog (window,
217 g_strdup (_("Cannot rename variable.")),
218 g_strdup_printf (_("This dictionary already contains "
219 "a variable named \"%s\"."),
222 dict_rename_var (dict, var, new_text);
230 width = atoi (new_text);
233 struct fmt_spec format;
235 format = *var_get_print_format (var);
236 fmt_change_width (&format, width, var_sheet->format_use);
237 var_set_width (var, fmt_var_width (&format));
238 var_set_both_formats (var, &format);
243 decimals = atoi (new_text);
246 struct fmt_spec format;
248 format = *var_get_print_format (var);
249 fmt_change_decimals (&format, decimals, var_sheet->format_use);
250 var_set_print_format (var, &format);
255 var_set_label (var, new_text, false);
263 width = atoi (new_text);
264 if (width > 0 && width < 2 * MAX_STRING)
265 var_set_display_width (var, width);
269 if (!strcmp (new_text, alignment_to_string (ALIGN_LEFT)))
270 var_set_alignment (var, ALIGN_LEFT);
271 else if (!strcmp (new_text, alignment_to_string (ALIGN_CENTRE)))
272 var_set_alignment (var, ALIGN_CENTRE);
273 else if (!strcmp (new_text, alignment_to_string (ALIGN_RIGHT)))
274 var_set_alignment (var, ALIGN_RIGHT);
278 if (!strcmp (new_text, measure_to_string (MEASURE_NOMINAL)))
279 var_set_measure (var, MEASURE_NOMINAL);
280 else if (!strcmp (new_text, measure_to_string (MEASURE_ORDINAL)))
281 var_set_measure (var, MEASURE_ORDINAL);
282 else if (!strcmp (new_text, measure_to_string (MEASURE_SCALE)))
283 var_set_measure (var, MEASURE_SCALE);
287 if (!strcmp (new_text, var_role_to_string (ROLE_INPUT)))
288 var_set_role (var, ROLE_INPUT);
289 else if (!strcmp (new_text, var_role_to_string (ROLE_OUTPUT)))
290 var_set_role (var, ROLE_OUTPUT);
291 else if (!strcmp (new_text, var_role_to_string (ROLE_BOTH)))
292 var_set_role (var, ROLE_BOTH);
293 else if (!strcmp (new_text, var_role_to_string (ROLE_NONE)))
294 var_set_role (var, ROLE_NONE);
295 else if (!strcmp (new_text, var_role_to_string (ROLE_PARTITION)))
296 var_set_role (var, ROLE_PARTITION);
297 else if (!strcmp (new_text, var_role_to_string (ROLE_SPLIT)))
298 var_set_role (var, ROLE_SPLIT);
304 render_popup_cell (PsppSheetViewColumn *tree_column,
305 GtkCellRenderer *cell,
310 PsppireVarSheet *var_sheet = user_data;
313 row = GPOINTER_TO_INT (iter->user_data);
315 "editable", row < psppire_dict_get_var_cnt (var_sheet->dict),
320 get_var_align_stock_id (enum alignment alignment)
325 return GTK_STOCK_JUSTIFY_LEFT;
328 return GTK_STOCK_JUSTIFY_CENTER;
331 return GTK_STOCK_JUSTIFY_RIGHT;
334 g_return_val_if_reached ("");
339 render_var_cell (PsppSheetViewColumn *tree_column,
340 GtkCellRenderer *cell,
345 PsppireVarSheet *var_sheet = user_data;
346 const struct fmt_spec *print;
347 enum vs_column column_id;
348 struct variable *var;
351 column_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tree_column),
352 "column-number")) - 1;
353 row = GPOINTER_TO_INT (iter->user_data);
355 if (row >= psppire_dict_get_var_cnt (var_sheet->dict))
357 if (GTK_IS_CELL_RENDERER_TEXT (cell))
361 "editable", column_id == VS_NAME,
363 if (column_id == VS_WIDTH
364 || column_id == VS_DECIMALS
365 || column_id == VS_COLUMNS)
366 g_object_set (cell, "adjustment", NULL, NULL);
369 g_object_set (cell, "stock-id", "", NULL);
373 var = psppire_dict_get_variable (var_sheet->dict, row);
375 print = var_get_print_format (var);
380 "text", var_get_name (var),
387 "text", fmt_gui_name (print->type),
393 set_spin_cell (cell, print->w,
394 fmt_min_width (print->type, var_sheet->format_use),
395 fmt_max_width (print->type, var_sheet->format_use),
396 fmt_step_width (print->type));
400 if (fmt_takes_decimals (print->type))
402 int max_w = fmt_max_width (print->type, var_sheet->format_use);
403 int max_d = fmt_max_decimals (print->type, max_w,
404 var_sheet->format_use);
405 set_spin_cell (cell, print->d, 0, max_d, 1);
417 "text", var_has_label (var) ? var_get_label (var) : "",
423 g_object_set (cell, "editable", FALSE, NULL);
424 if ( ! var_has_value_labels (var))
425 g_object_set (cell, "text", _("None"), NULL);
428 const struct val_labs *vls = var_get_value_labels (var);
429 const struct val_lab **labels = val_labs_sorted (vls);
430 const struct val_lab *vl = labels[0];
431 gchar *vstr = value_to_text (vl->value, var);
432 char *text = xasprintf (_("{%s, %s}..."), vstr,
433 val_lab_get_escaped_label (vl));
436 g_object_set (cell, "text", text, NULL);
444 char *text = missing_values_to_string (var_sheet->dict, var, NULL);
454 set_spin_cell (cell, var_get_display_width (var), 1, 2 * MAX_STRING, 1);
458 if (GTK_IS_CELL_RENDERER_TEXT (cell))
460 "text", alignment_to_string (var_get_alignment (var)),
464 g_object_set (cell, "stock-id",
465 get_var_align_stock_id (var_get_alignment (var)), NULL);
469 if (GTK_IS_CELL_RENDERER_TEXT (cell))
471 "text", measure_to_string (var_get_measure (var)),
476 enum fmt_type type = var_get_print_format (var)->type;
477 enum measure measure = var_get_measure (var);
479 g_object_set (cell, "stock-id",
480 get_var_measurement_stock_id (type, measure),
487 "text", var_role_to_string (var_get_role (var)),
494 static struct variable *
495 path_string_to_variable (PsppireVarSheet *var_sheet, gchar *path_string)
501 path = gtk_tree_path_new_from_string (path_string);
502 row = gtk_tree_path_get_indices (path)[0];
503 gtk_tree_path_free (path);
505 dict = psppire_var_sheet_get_dictionary (var_sheet);
506 g_return_val_if_fail (dict != NULL, NULL);
508 return psppire_dict_get_variable (dict, row);
512 on_type_click (PsppireCellRendererButton *cell,
514 PsppireVarSheet *var_sheet)
516 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
517 struct fmt_spec format;
518 struct variable *var;
520 var = path_string_to_variable (var_sheet, path);
521 g_return_if_fail (var != NULL);
523 format = *var_get_print_format (var);
524 psppire_var_type_dialog_run (GTK_WINDOW (toplevel), &format);
526 var_set_width_and_formats (var, fmt_var_width (&format), &format, &format);
530 on_value_labels_click (PsppireCellRendererButton *cell,
532 PsppireVarSheet *var_sheet)
534 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
535 struct val_labs *labels;
536 struct variable *var;
538 var = path_string_to_variable (var_sheet, path);
539 g_return_if_fail (var != NULL);
541 labels = psppire_val_labs_dialog_run (GTK_WINDOW (toplevel), var);
544 var_set_value_labels (var, labels);
545 val_labs_destroy (labels);
550 on_missing_values_click (PsppireCellRendererButton *cell,
552 PsppireVarSheet *var_sheet)
554 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (var_sheet));
555 struct missing_values mv;
556 struct variable *var;
558 var = path_string_to_variable (var_sheet, path);
559 g_return_if_fail (var != NULL);
561 psppire_missing_val_dialog_run (GTK_WINDOW (toplevel), var, &mv);
562 var_set_missing_values (var, &mv);
567 get_string_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
571 g_object_set (G_OBJECT (renderer),
572 PSPPIRE_IS_CELL_RENDERER_BUTTON (renderer) ? "label" : "text",
573 string, (void *) NULL);
574 gtk_cell_renderer_get_size (renderer, GTK_WIDGET (treeview),
575 NULL, NULL, NULL, &width, NULL);
580 get_monospace_width (PsppSheetView *treeview, GtkCellRenderer *renderer,
587 ds_put_byte_multiple (&s, '0', char_cnt);
588 ds_put_byte (&s, ' ');
589 width = get_string_width (treeview, renderer, ds_cstr (&s));
595 static PsppSheetViewColumn *
596 add_var_sheet_column (PsppireVarSheet *var_sheet, GtkCellRenderer *renderer,
597 enum vs_column column_id,
598 const char *title, int width)
600 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
601 int title_width, content_width;
602 PsppSheetViewColumn *column;
604 column = pspp_sheet_view_column_new_with_attributes (title, renderer, NULL);
605 g_object_set_data (G_OBJECT (column), "column-number",
606 GINT_TO_POINTER (column_id) + 1);
608 pspp_sheet_view_column_set_cell_data_func (
609 column, renderer, render_var_cell, var_sheet, NULL);
611 title_width = get_string_width (sheet_view, renderer, title);
612 content_width = get_monospace_width (sheet_view, renderer, width);
613 g_object_set_data (G_OBJECT (column), "content-width",
614 GINT_TO_POINTER (content_width));
616 pspp_sheet_view_column_set_fixed_width (column,
617 MAX (title_width, content_width));
618 pspp_sheet_view_column_set_resizable (column, TRUE);
620 pspp_sheet_view_append_column (sheet_view, column);
622 g_signal_connect (renderer, "edited",
623 G_CALLBACK (on_var_column_edited),
625 g_object_set_data (G_OBJECT (renderer), "column-id",
626 GINT_TO_POINTER (column_id));
627 g_object_set_data (G_OBJECT (renderer), "var-sheet", var_sheet);
632 static PsppSheetViewColumn *
633 add_text_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
634 const char *title, int width)
636 return add_var_sheet_column (var_sheet, gtk_cell_renderer_text_new (),
637 column_id, title, width);
640 static PsppSheetViewColumn *
641 add_spin_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
642 const char *title, int width)
644 return add_var_sheet_column (var_sheet, gtk_cell_renderer_spin_new (),
645 column_id, title, width);
649 measure_to_stock_id (enum fmt_type type, int measure)
651 return get_var_measurement_stock_id (type, measure);
655 alignment_to_stock_id (enum fmt_type type, int alignment)
657 return get_var_align_stock_id (alignment);
661 render_measure (GtkCellLayout *cell_layout,
662 GtkCellRenderer *cell,
663 GtkTreeModel *tree_model,
667 const char *(*value_to_stock_id) (enum fmt_type, int value);
668 enum fmt_type type = GPOINTER_TO_INT (data);
671 value_to_stock_id = g_object_get_data (G_OBJECT (cell), "value-to-stock-id");
673 gtk_tree_model_get (tree_model, iter, 0, &value, -1);
674 g_object_set (cell, "stock-id", value_to_stock_id (type, value), NULL);
678 on_combo_editing_started (GtkCellRenderer *renderer,
679 GtkCellEditable *editable,
683 PsppireVarSheet *var_sheet = user_data;
685 if (GTK_IS_COMBO_BOX (editable))
687 struct variable *var = path_string_to_variable (var_sheet, path_string);
688 const struct fmt_spec *format = var_get_print_format (var);
689 const char *(*value_to_stock_id) (enum fmt_type, int value);
690 GtkCellRenderer *cell;
692 value_to_stock_id = g_object_get_data (G_OBJECT (renderer),
693 "value-to-stock-id");
695 cell = gtk_cell_renderer_pixbuf_new ();
696 g_object_set (cell, "width", 16, "height", 16, NULL);
697 g_object_set_data (G_OBJECT (cell),
698 "value-to-stock-id", value_to_stock_id);
699 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (editable), cell, FALSE);
700 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (editable), cell,
702 GINT_TO_POINTER (format->type),
707 static PsppSheetViewColumn *
708 add_combo_column (PsppireVarSheet *var_sheet, enum vs_column column_id,
709 const char *title, int width,
710 const char *(*value_to_stock_id) (enum fmt_type, int value),
713 GtkCellRenderer *cell;
718 store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
719 va_start (args, value_to_stock_id);
720 while ((name = va_arg (args, const char *)) != NULL)
722 int value = va_arg (args, int);
723 gtk_list_store_insert_with_values (store, NULL, G_MAXINT,
730 cell = gtk_cell_renderer_combo_new ();
733 "model", GTK_TREE_MODEL (store),
736 if (value_to_stock_id != NULL)
738 g_object_set_data (G_OBJECT (cell),
739 "value-to-stock-id", value_to_stock_id);
740 g_signal_connect (cell, "editing-started",
741 G_CALLBACK (on_combo_editing_started),
745 return add_var_sheet_column (var_sheet, cell, column_id, title, width);
749 add_popup_menu (PsppireVarSheet *var_sheet,
750 PsppSheetViewColumn *column,
751 void (*on_click) (PsppireCellRendererButton *,
753 PsppireVarSheet *var_sheet))
755 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
756 const char *button_label = "...";
757 GtkCellRenderer *button_renderer;
760 button_renderer = psppire_cell_renderer_button_new ();
761 g_object_set (button_renderer,
762 "label", button_label,
765 g_signal_connect (button_renderer, "clicked", G_CALLBACK (on_click),
767 pspp_sheet_view_column_pack_start (column, button_renderer, FALSE);
768 pspp_sheet_view_column_set_cell_data_func (
769 column, button_renderer, render_popup_cell, var_sheet, NULL);
771 content_width = GPOINTER_TO_INT (g_object_get_data (
772 G_OBJECT (column), "content-width"));
773 content_width += get_string_width (sheet_view, button_renderer,
775 if (content_width > pspp_sheet_view_column_get_fixed_width (column))
776 pspp_sheet_view_column_set_fixed_width (column, content_width);
780 get_tooltip_location (GtkWidget *widget, GtkTooltip *tooltip,
781 gint wx, gint wy, size_t *row, size_t *column)
783 PsppSheetView *tree_view = PSPP_SHEET_VIEW (widget);
787 PsppSheetViewColumn *tree_column;
788 GtkTreeModel *tree_model;
792 /* Check that WIDGET is really visible on the screen before we
793 do anything else. This is a bug fix for a sticky situation:
794 when text_data_import_assistant() returns, it frees the data
795 necessary to compose the tool tip message, but there may be
796 a tool tip under preparation at that point (even if there is
797 no visible tool tip) that will call back into us a little
798 bit later. Perhaps the correct solution to this problem is
799 to make the data related to the tool tips part of a GObject
800 that only gets destroyed when all references are released,
801 but this solution appears to be effective too. */
802 if (!gtk_widget_get_mapped (widget))
805 pspp_sheet_view_convert_widget_to_bin_window_coords (tree_view,
807 if (!pspp_sheet_view_get_path_at_pos (tree_view, bx, by,
808 &path, &tree_column, NULL, NULL))
811 column_ptr = g_object_get_data (G_OBJECT (tree_column), "column-number");
812 if (column_ptr == NULL)
814 *column = GPOINTER_TO_INT (column_ptr) - 1;
816 pspp_sheet_view_set_tooltip_cell (tree_view, tooltip, path, tree_column,
819 tree_model = pspp_sheet_view_get_model (tree_view);
820 ok = gtk_tree_model_get_iter (tree_model, &iter, path);
821 gtk_tree_path_free (path);
825 *row = GPOINTER_TO_INT (iter.user_data);
830 on_query_var_tooltip (GtkWidget *widget, gint wx, gint wy,
831 gboolean keyboard_mode UNUSED,
832 GtkTooltip *tooltip, gpointer *user_data UNUSED)
834 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
836 struct variable *var;
839 if (!get_tooltip_location (widget, tooltip, wx, wy, &row, &column))
842 dict = psppire_var_sheet_get_dictionary (var_sheet);
843 g_return_val_if_fail (dict != NULL, FALSE);
845 if (row >= psppire_dict_get_var_cnt (dict))
847 gtk_tooltip_set_text (tooltip, _("Enter a variable name to add a "
852 var = psppire_dict_get_variable (dict, row);
853 g_return_val_if_fail (var != NULL, FALSE);
859 char text[FMT_STRING_LEN_MAX + 1];
861 fmt_to_string (var_get_print_format (var), text);
862 gtk_tooltip_set_text (tooltip, text);
867 if (var_has_value_labels (var))
869 const struct val_labs *vls = var_get_value_labels (var);
870 const struct val_lab **labels = val_labs_sorted (vls);
875 for (i = 0; i < val_labs_count (vls); i++)
877 const struct val_lab *vl = labels[i];
880 if (i >= 10 || ds_length (&s) > 500)
882 ds_put_cstr (&s, "...");
886 vstr = value_to_text (vl->value, var);
887 ds_put_format (&s, _("{%s, %s}\n"), vstr,
888 val_lab_get_escaped_label (vl));
892 ds_chomp_byte (&s, '\n');
894 gtk_tooltip_set_text (tooltip, ds_cstr (&s));
906 do_popup_menu (GtkWidget *widget, guint button, guint32 time)
908 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (widget);
911 menu = get_widget_assert (var_sheet->builder, "varsheet-variable-popup");
912 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time);
916 on_popup_menu (GtkWidget *widget, gpointer user_data UNUSED)
918 do_popup_menu (widget, 0, gtk_get_current_event_time ());
922 on_button_pressed (GtkWidget *widget, GdkEventButton *event,
923 gpointer user_data UNUSED)
925 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (widget);
927 if (event->type == GDK_BUTTON_PRESS && event->button == 3)
929 PsppSheetSelection *selection;
931 selection = pspp_sheet_view_get_selection (sheet_view);
932 if (pspp_sheet_selection_count_selected_rows (selection) <= 1)
936 if (pspp_sheet_view_get_path_at_pos (sheet_view, event->x, event->y,
937 &path, NULL, NULL, NULL))
939 pspp_sheet_selection_unselect_all (selection);
940 pspp_sheet_selection_select_path (selection, path);
941 gtk_tree_path_free (path);
945 do_popup_menu (widget, event->button, event->time);
953 psppire_fmt_use_get_type (void)
955 static GType etype = 0;
958 static const GEnumValue values[] =
960 { FMT_FOR_INPUT, "FMT_FOR_INPUT", "input" },
961 { FMT_FOR_OUTPUT, "FMT_FOR_OUTPUT", "output" },
965 etype = g_enum_register_static
966 (g_intern_static_string ("PsppireFmtUse"), values);
975 PROP_MAY_CREATE_VARS,
976 PROP_MAY_DELETE_VARS,
982 psppire_var_sheet_set_property (GObject *object,
987 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
991 case PROP_DICTIONARY:
992 psppire_var_sheet_set_dictionary (obj,
993 PSPPIRE_DICT (g_value_get_object (
997 case PROP_MAY_CREATE_VARS:
998 psppire_var_sheet_set_may_create_vars (obj,
999 g_value_get_boolean (value));
1002 case PROP_MAY_DELETE_VARS:
1003 psppire_var_sheet_set_may_delete_vars (obj,
1004 g_value_get_boolean (value));
1007 case PROP_FORMAT_TYPE:
1008 obj->format_use = g_value_get_enum (value);
1011 case PROP_UI_MANAGER:
1013 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1019 psppire_var_sheet_get_property (GObject *object,
1024 PsppireVarSheet *obj = PSPPIRE_VAR_SHEET (object);
1028 case PROP_DICTIONARY:
1029 g_value_set_object (value,
1030 G_OBJECT (psppire_var_sheet_get_dictionary (obj)));
1033 case PROP_MAY_CREATE_VARS:
1034 g_value_set_boolean (value, obj->may_create_vars);
1037 case PROP_MAY_DELETE_VARS:
1038 g_value_set_boolean (value, obj->may_delete_vars);
1041 case PROP_FORMAT_TYPE:
1042 g_value_set_enum (value, obj->format_use);
1045 case PROP_UI_MANAGER:
1046 g_value_set_object (value, psppire_var_sheet_get_ui_manager (obj));
1050 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1056 psppire_var_sheet_dispose (GObject *obj)
1058 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (obj);
1061 if (var_sheet->dispose_has_run)
1064 var_sheet->dispose_has_run = TRUE;
1066 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1067 if ( var_sheet->dict_signals[i])
1068 g_signal_handler_disconnect (var_sheet->dict,
1069 var_sheet->dict_signals[i]);
1071 if (var_sheet->dict)
1072 g_object_unref (var_sheet->dict);
1075 g_object_unref (var_sheet->uim);
1077 /* These dialogs are not GObjects (although they should be!)
1078 But for now, unreffing them only causes a GCritical Error
1079 so comment them out for now. (and accept the memory leakage)
1081 g_object_unref (var_sheet->val_labs_dialog);
1082 g_object_unref (var_sheet->missing_val_dialog);
1083 g_object_unref (var_sheet->var_type_dialog);
1086 G_OBJECT_CLASS (psppire_var_sheet_parent_class)->dispose (obj);
1090 psppire_var_sheet_class_init (PsppireVarSheetClass *class)
1092 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1095 gobject_class->set_property = psppire_var_sheet_set_property;
1096 gobject_class->get_property = psppire_var_sheet_get_property;
1097 gobject_class->dispose = psppire_var_sheet_dispose;
1099 g_signal_new ("var-double-clicked",
1100 G_OBJECT_CLASS_TYPE (gobject_class),
1103 g_signal_accumulator_true_handled, NULL,
1104 psppire_marshal_BOOLEAN__INT,
1105 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
1107 pspec = g_param_spec_object ("dictionary",
1108 "Dictionary displayed by the sheet",
1109 "The PsppireDict that the sheet displays "
1110 "may allow the user to edit",
1113 g_object_class_install_property (gobject_class, PROP_DICTIONARY, pspec);
1115 pspec = g_param_spec_boolean ("may-create-vars",
1116 "May create variables",
1117 "Whether the user may create more variables",
1120 g_object_class_install_property (gobject_class, PROP_MAY_CREATE_VARS, pspec);
1122 pspec = g_param_spec_boolean ("may-delete-vars",
1123 "May delete variables",
1124 "Whether the user may delete variables",
1127 g_object_class_install_property (gobject_class, PROP_MAY_DELETE_VARS, pspec);
1129 pspec = g_param_spec_enum ("format-use",
1130 "Use of variable format",
1131 ("Whether variables have input or output "
1133 PSPPIRE_TYPE_FMT_USE,
1136 g_object_class_install_property (gobject_class, PROP_FORMAT_TYPE, pspec);
1138 pspec = g_param_spec_object ("ui-manager",
1140 "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.",
1141 GTK_TYPE_UI_MANAGER,
1143 g_object_class_install_property (gobject_class, PROP_UI_MANAGER, pspec);
1147 render_row_number_cell (PsppSheetViewColumn *tree_column,
1148 GtkCellRenderer *cell,
1149 GtkTreeModel *model,
1153 PsppireVarSheet *var_sheet = user_data;
1154 GValue gvalue = { 0, };
1157 row = GPOINTER_TO_INT (iter->user_data);
1159 g_value_init (&gvalue, G_TYPE_INT);
1160 g_value_set_int (&gvalue, row + 1);
1161 g_object_set_property (G_OBJECT (cell), "label", &gvalue);
1162 g_value_unset (&gvalue);
1164 if (!var_sheet->dict || row < psppire_dict_get_var_cnt (var_sheet->dict))
1165 g_object_set (cell, "editable", TRUE, NULL);
1167 g_object_set (cell, "editable", FALSE, NULL);
1171 psppire_var_sheet_row_number_double_clicked (PsppireCellRendererButton *button,
1173 PsppireVarSheet *var_sheet)
1177 g_return_if_fail (var_sheet->dict != NULL);
1179 path = gtk_tree_path_new_from_string (path_string);
1180 if (gtk_tree_path_get_depth (path) == 1)
1182 gint *indices = gtk_tree_path_get_indices (path);
1183 if (indices[0] < psppire_dict_get_var_cnt (var_sheet->dict))
1186 g_signal_emit_by_name (var_sheet, "var-double-clicked",
1187 indices[0], &handled);
1190 gtk_tree_path_free (path);
1194 psppire_var_sheet_variables_column_clicked (PsppSheetViewColumn *column,
1195 PsppireVarSheet *var_sheet)
1197 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1198 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1200 pspp_sheet_selection_select_all (selection);
1203 static PsppSheetViewColumn *
1204 make_row_number_column (PsppireVarSheet *var_sheet)
1206 PsppSheetViewColumn *column;
1207 GtkCellRenderer *renderer;
1209 renderer = psppire_cell_renderer_button_new ();
1210 g_object_set (renderer, "xalign", 1.0, NULL);
1211 g_signal_connect (renderer, "double-clicked",
1212 G_CALLBACK (psppire_var_sheet_row_number_double_clicked),
1215 column = pspp_sheet_view_column_new_with_attributes (_("Variable"),
1217 pspp_sheet_view_column_set_clickable (column, TRUE);
1218 pspp_sheet_view_column_set_cell_data_func (
1219 column, renderer, render_row_number_cell, var_sheet, NULL);
1220 pspp_sheet_view_column_set_fixed_width (column, 50);
1221 g_signal_connect (column, "clicked",
1222 G_CALLBACK (psppire_var_sheet_variables_column_clicked),
1229 on_edit_clear_variables (GtkAction *action, PsppireVarSheet *var_sheet)
1231 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1232 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1233 PsppireDict *dict = var_sheet->dict;
1234 const struct range_set_node *node;
1235 struct range_set *selected;
1237 selected = pspp_sheet_selection_get_range_set (selection);
1238 for (node = range_set_last (selected); node != NULL;
1239 node = range_set_prev (selected, node))
1243 for (i = 1; i <= range_set_node_get_width (node); i++)
1245 unsigned long row = range_set_node_get_end (node) - i;
1246 if (row >= 0 && row < psppire_dict_get_var_cnt (dict))
1247 psppire_dict_delete_variables (dict, row, 1);
1250 range_set_destroy (selected);
1254 on_selection_changed (PsppSheetSelection *selection,
1255 gpointer user_data UNUSED)
1257 PsppSheetView *sheet_view = pspp_sheet_selection_get_tree_view (selection);
1258 PsppireVarSheet *var_sheet = PSPPIRE_VAR_SHEET (sheet_view);
1259 gint n_selected_rows;
1260 gboolean may_delete;
1264 n_selected_rows = pspp_sheet_selection_count_selected_rows (selection);
1266 action = get_action_assert (var_sheet->builder, "edit_insert-variable");
1267 gtk_action_set_sensitive (action, (var_sheet->may_create_vars
1268 && n_selected_rows > 0));
1270 switch (n_selected_rows)
1277 /* The row used for inserting new variables cannot be deleted. */
1278 path = gtk_tree_path_new_from_indices (
1279 psppire_dict_get_var_cnt (var_sheet->dict), -1);
1280 may_delete = !pspp_sheet_selection_path_is_selected (selection, path);
1281 gtk_tree_path_free (path);
1288 action = get_action_assert (var_sheet->builder, "edit_clear-variables");
1289 gtk_action_set_sensitive (action, var_sheet->may_delete_vars && may_delete);
1293 on_edit_insert_variable (GtkAction *action, PsppireVarSheet *var_sheet)
1295 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1296 PsppSheetSelection *selection = pspp_sheet_view_get_selection (sheet_view);
1297 PsppireDict *dict = var_sheet->dict;
1298 struct range_set *selected;
1301 selected = pspp_sheet_selection_get_range_set (selection);
1302 row = range_set_scan (selected, 0);
1303 range_set_destroy (selected);
1305 if (row <= psppire_dict_get_var_cnt (dict))
1308 if (psppire_dict_generate_name (dict, name, sizeof name))
1309 psppire_dict_insert_variable (dict, row, name);
1314 psppire_var_sheet_init (PsppireVarSheet *obj)
1316 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (obj);
1317 PsppSheetViewColumn *column;
1318 GtkCellRenderer *cell;
1323 obj->format_use = FMT_FOR_OUTPUT;
1324 obj->may_create_vars = TRUE;
1325 obj->may_delete_vars = TRUE;
1327 obj->scroll_to_bottom_signal = 0;
1329 obj->container = NULL;
1330 obj->dispose_has_run = FALSE;
1333 pspp_sheet_view_append_column (sheet_view, make_row_number_column (obj));
1335 column = add_text_column (obj, VS_NAME, _("Name"), 12);
1336 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
1337 g_signal_connect (list->data, "editing-started",
1338 G_CALLBACK (on_name_column_editing_started), NULL);
1341 column = add_text_column (obj, VS_TYPE, _("Type"), 8);
1342 add_popup_menu (obj, column, on_type_click);
1344 add_spin_column (obj, VS_WIDTH, _("Width"), 5);
1346 add_spin_column (obj, VS_DECIMALS, _("Decimals"), 2);
1348 add_text_column (obj, VS_LABEL, _("Label"), 20);
1350 column = add_text_column (obj, VS_VALUES, _("Value Labels"), 20);
1351 add_popup_menu (obj, column, on_value_labels_click);
1353 column = add_text_column (obj, VS_MISSING, _("Missing Values"), 20);
1354 add_popup_menu (obj, column, on_missing_values_click);
1356 add_spin_column (obj, VS_COLUMNS, _("Columns"), 3);
1359 = add_combo_column (obj, VS_ALIGN, _("Align"), 8, alignment_to_stock_id,
1360 alignment_to_string (ALIGN_LEFT), ALIGN_LEFT,
1361 alignment_to_string (ALIGN_CENTRE), ALIGN_CENTRE,
1362 alignment_to_string (ALIGN_RIGHT), ALIGN_RIGHT,
1364 cell = gtk_cell_renderer_pixbuf_new ();
1365 g_object_set (cell, "width", 16, "height", 16, NULL);
1366 pspp_sheet_view_column_pack_end (column, cell, FALSE);
1367 pspp_sheet_view_column_set_cell_data_func (
1368 column, cell, render_var_cell, obj, NULL);
1371 = add_combo_column (obj, VS_MEASURE, _("Measure"), 12, measure_to_stock_id,
1372 measure_to_string (MEASURE_NOMINAL), MEASURE_NOMINAL,
1373 measure_to_string (MEASURE_ORDINAL), MEASURE_ORDINAL,
1374 measure_to_string (MEASURE_SCALE), MEASURE_SCALE,
1376 cell = gtk_cell_renderer_pixbuf_new ();
1377 g_object_set (cell, "width", 16, "height", 16, NULL);
1378 pspp_sheet_view_column_pack_end (column, cell, FALSE);
1379 pspp_sheet_view_column_set_cell_data_func (
1380 column, cell, render_var_cell, obj, NULL);
1382 add_combo_column (obj, VS_ROLE, _("Role"), 12, NULL,
1383 var_role_to_string (ROLE_INPUT), ROLE_INPUT,
1384 var_role_to_string (ROLE_OUTPUT), ROLE_OUTPUT,
1385 var_role_to_string (ROLE_BOTH), ROLE_BOTH,
1386 var_role_to_string (ROLE_NONE), ROLE_NONE,
1387 var_role_to_string (ROLE_PARTITION), ROLE_PARTITION,
1388 var_role_to_string (ROLE_SPLIT), ROLE_SPLIT,
1391 pspp_sheet_view_set_rubber_banding (sheet_view, TRUE);
1392 pspp_sheet_selection_set_mode (pspp_sheet_view_get_selection (sheet_view),
1393 PSPP_SHEET_SELECTION_MULTIPLE);
1395 g_object_set (G_OBJECT (obj), "has-tooltip", TRUE, NULL);
1396 g_signal_connect (obj, "query-tooltip",
1397 G_CALLBACK (on_query_var_tooltip), NULL);
1398 g_signal_connect (obj, "button-press-event",
1399 G_CALLBACK (on_button_pressed), NULL);
1400 g_signal_connect (obj, "popup-menu", G_CALLBACK (on_popup_menu), NULL);
1402 obj->builder = builder_new ("var-sheet.ui");
1404 action = get_action_assert (obj->builder, "edit_clear-variables");
1405 g_signal_connect (action, "activate", G_CALLBACK (on_edit_clear_variables),
1407 gtk_action_set_sensitive (action, FALSE);
1408 g_signal_connect (pspp_sheet_view_get_selection (sheet_view),
1409 "changed", G_CALLBACK (on_selection_changed), NULL);
1411 action = get_action_assert (obj->builder, "edit_insert-variable");
1412 gtk_action_set_sensitive (action, FALSE);
1413 g_signal_connect (action, "activate", G_CALLBACK (on_edit_insert_variable),
1418 psppire_var_sheet_new (void)
1420 return g_object_new (PSPPIRE_VAR_SHEET_TYPE, NULL);
1424 psppire_var_sheet_get_dictionary (PsppireVarSheet *var_sheet)
1426 return var_sheet->dict;
1430 refresh_model (PsppireVarSheet *var_sheet)
1432 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet), NULL);
1434 if (var_sheet->dict != NULL)
1436 PsppireEmptyListStore *store;
1439 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1440 + var_sheet->may_create_vars);
1441 store = psppire_empty_list_store_new (n_rows);
1442 pspp_sheet_view_set_model (PSPP_SHEET_VIEW (var_sheet),
1443 GTK_TREE_MODEL (store));
1444 g_object_unref (store);
1449 on_var_changed (PsppireDict *dict, glong row,
1450 guint what, const struct variable *oldvar,
1451 PsppireVarSheet *var_sheet)
1453 PsppireEmptyListStore *store;
1455 g_return_if_fail (dict == var_sheet->dict);
1457 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1458 PSPP_SHEET_VIEW (var_sheet)));
1459 g_return_if_fail (store != NULL);
1461 psppire_empty_list_store_row_changed (store, row);
1465 on_var_inserted (PsppireDict *dict, glong row, PsppireVarSheet *var_sheet)
1467 PsppireEmptyListStore *store;
1470 g_return_if_fail (dict == var_sheet->dict);
1472 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1473 PSPP_SHEET_VIEW (var_sheet)));
1474 g_return_if_fail (store != NULL);
1476 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1477 + var_sheet->may_create_vars);
1478 psppire_empty_list_store_set_n_rows (store, n_rows);
1479 psppire_empty_list_store_row_inserted (store, row);
1483 on_var_deleted (PsppireDict *dict,
1484 const struct variable *var, int dict_idx, int case_idx,
1485 PsppireVarSheet *var_sheet)
1487 PsppireEmptyListStore *store;
1490 g_return_if_fail (dict == var_sheet->dict);
1492 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1493 PSPP_SHEET_VIEW (var_sheet)));
1494 g_return_if_fail (store != NULL);
1496 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1497 + var_sheet->may_create_vars);
1498 psppire_empty_list_store_set_n_rows (store, n_rows);
1499 psppire_empty_list_store_row_deleted (store, dict_idx);
1503 on_backend_changed (PsppireDict *dict, PsppireVarSheet *var_sheet)
1505 g_return_if_fail (dict == var_sheet->dict);
1506 refresh_model (var_sheet);
1510 psppire_var_sheet_set_dictionary (PsppireVarSheet *var_sheet,
1513 if (var_sheet->dict != NULL)
1517 for (i = 0; i < PSPPIRE_VAR_SHEET_N_SIGNALS; i++)
1519 if (var_sheet->dict_signals[i])
1520 g_signal_handler_disconnect (var_sheet->dict,
1521 var_sheet->dict_signals[i]);
1523 var_sheet->dict_signals[i] = 0;
1526 g_object_unref (var_sheet->dict);
1529 var_sheet->dict = dict;
1533 g_object_ref (dict);
1535 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_BACKEND_CHANGED]
1536 = g_signal_connect (dict, "backend-changed",
1537 G_CALLBACK (on_backend_changed), var_sheet);
1539 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_CHANGED]
1540 = g_signal_connect (dict, "variable-changed",
1541 G_CALLBACK (on_var_changed), var_sheet);
1543 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_DELETED]
1544 = g_signal_connect (dict, "variable-inserted",
1545 G_CALLBACK (on_var_inserted), var_sheet);
1547 var_sheet->dict_signals[PSPPIRE_VAR_SHEET_VARIABLE_INSERTED]
1548 = g_signal_connect (dict, "variable-deleted",
1549 G_CALLBACK (on_var_deleted), var_sheet);
1552 refresh_model (var_sheet);
1556 psppire_var_sheet_get_may_create_vars (PsppireVarSheet *var_sheet)
1558 return var_sheet->may_create_vars;
1562 psppire_var_sheet_set_may_create_vars (PsppireVarSheet *var_sheet,
1563 gboolean may_create_vars)
1565 if (var_sheet->may_create_vars != may_create_vars)
1567 PsppireEmptyListStore *store;
1570 var_sheet->may_create_vars = may_create_vars;
1572 store = PSPPIRE_EMPTY_LIST_STORE (pspp_sheet_view_get_model (
1573 PSPP_SHEET_VIEW (var_sheet)));
1574 g_return_if_fail (store != NULL);
1576 n_rows = (psppire_dict_get_var_cnt (var_sheet->dict)
1577 + var_sheet->may_create_vars);
1578 psppire_empty_list_store_set_n_rows (store, n_rows);
1580 if (may_create_vars)
1581 psppire_empty_list_store_row_inserted (store, n_rows - 1);
1583 psppire_empty_list_store_row_deleted (store, n_rows);
1585 on_selection_changed (pspp_sheet_view_get_selection (
1586 PSPP_SHEET_VIEW (var_sheet)), NULL);
1591 psppire_var_sheet_get_may_delete_vars (PsppireVarSheet *var_sheet)
1593 return var_sheet->may_delete_vars;
1597 psppire_var_sheet_set_may_delete_vars (PsppireVarSheet *var_sheet,
1598 gboolean may_delete_vars)
1600 if (var_sheet->may_delete_vars != may_delete_vars)
1602 var_sheet->may_delete_vars = may_delete_vars;
1603 on_selection_changed (pspp_sheet_view_get_selection (
1604 PSPP_SHEET_VIEW (var_sheet)), NULL);
1609 psppire_var_sheet_goto_variable (PsppireVarSheet *var_sheet, int dict_index)
1611 PsppSheetView *sheet_view = PSPP_SHEET_VIEW (var_sheet);
1614 path = gtk_tree_path_new_from_indices (dict_index, -1);
1615 pspp_sheet_view_scroll_to_cell (sheet_view, path, NULL, FALSE, 0.0, 0.0);
1616 pspp_sheet_view_set_cursor (sheet_view, path, NULL, FALSE);
1617 gtk_tree_path_free (path);
1621 psppire_var_sheet_get_ui_manager (PsppireVarSheet *var_sheet)
1623 if (var_sheet->uim == NULL)
1625 var_sheet->uim = GTK_UI_MANAGER (get_object_assert (var_sheet->builder,
1627 GTK_TYPE_UI_MANAGER));
1628 g_object_ref (var_sheet->uim);
1631 return var_sheet->uim;