1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010, 2011, 2012 Free Software Foundation
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/>. */
22 #include "data/dataset.h"
23 #include "data/session.h"
24 #include "language/lexer/lexer.h"
25 #include "libpspp/message.h"
26 #include "libpspp/str.h"
27 #include "ui/gui/aggregate-dialog.h"
28 #include "ui/gui/autorecode-dialog.h"
29 #include "ui/gui/binomial-dialog.h"
30 #include "ui/gui/builder-wrapper.h"
31 #include "ui/gui/chi-square-dialog.h"
32 #include "ui/gui/comments-dialog.h"
33 #include "ui/gui/compute-dialog.h"
34 #include "ui/gui/count-dialog.h"
35 #include "ui/gui/crosstabs-dialog.h"
36 #include "ui/gui/entry-dialog.h"
37 #include "ui/gui/executor.h"
38 #include "ui/gui/find-dialog.h"
39 #include "ui/gui/frequencies-dialog.h"
40 #include "ui/gui/goto-case-dialog.h"
41 #include "ui/gui/help-menu.h"
42 #include "ui/gui/helper.h"
43 #include "ui/gui/helper.h"
44 #include "ui/gui/k-related-dialog.h"
45 #include "ui/gui/npar-two-sample-related.h"
46 #include "ui/gui/oneway-anova-dialog.h"
47 #include "ui/gui/psppire-data-window.h"
48 #include "ui/gui/psppire-syntax-window.h"
49 #include "ui/gui/psppire-window.h"
50 #include "ui/gui/psppire.h"
51 #include "ui/gui/runs-dialog.h"
52 #include "ui/gui/ks-one-sample-dialog.h"
53 #include "ui/gui/recode-dialog.h"
54 #include "ui/gui/select-cases-dialog.h"
55 #include "ui/gui/split-file-dialog.h"
56 #include "ui/gui/t-test-one-sample.h"
57 #include "ui/gui/t-test-paired-samples.h"
58 #include "ui/gui/text-data-import-dialog.h"
59 #include "ui/gui/transpose-dialog.h"
60 #include "ui/gui/univariate-dialog.h"
61 #include "ui/gui/weight-cases-dialog.h"
62 #include "ui/syntax-gen.h"
64 #include "gl/c-strcase.h"
65 #include "gl/c-strcasestr.h"
66 #include "gl/xvasprintf.h"
69 #define _(msgid) gettext (msgid)
70 #define N_(msgid) msgid
72 struct session *the_session;
73 struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
75 static void psppire_data_window_class_init (PsppireDataWindowClass *class);
76 static void psppire_data_window_init (PsppireDataWindow *data_editor);
79 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
81 static void psppire_data_window_dispose (GObject *object);
82 static void psppire_data_window_finalize (GObject *object);
83 static void psppire_data_window_set_property (GObject *object,
87 static void psppire_data_window_get_property (GObject *object,
93 psppire_data_window_get_type (void)
95 static GType psppire_data_window_type = 0;
97 if (!psppire_data_window_type)
99 static const GTypeInfo psppire_data_window_info =
101 sizeof (PsppireDataWindowClass),
104 (GClassInitFunc)psppire_data_window_class_init,
105 (GClassFinalizeFunc) NULL,
107 sizeof (PsppireDataWindow),
109 (GInstanceInitFunc) psppire_data_window_init,
112 static const GInterfaceInfo window_interface_info =
114 (GInterfaceInitFunc) psppire_data_window_iface_init,
119 psppire_data_window_type =
120 g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
121 &psppire_data_window_info, 0);
124 g_type_add_interface_static (psppire_data_window_type,
125 PSPPIRE_TYPE_WINDOW_MODEL,
126 &window_interface_info);
129 return psppire_data_window_type;
132 static GObjectClass *parent_class ;
139 psppire_data_window_class_init (PsppireDataWindowClass *class)
141 GObjectClass *object_class = G_OBJECT_CLASS (class);
143 parent_class = g_type_class_peek_parent (class);
145 object_class->dispose = psppire_data_window_dispose;
146 object_class->finalize = psppire_data_window_finalize;
147 object_class->set_property = psppire_data_window_set_property;
148 object_class->get_property = psppire_data_window_get_property;
150 g_object_class_install_property (
151 object_class, PROP_DATASET,
152 g_param_spec_pointer ("dataset", "Dataset",
153 "'struct datset *' represented by the window",
154 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
158 set_paste_menuitem_sensitivity (PsppireDataWindow *de, gboolean x)
160 GtkAction *edit_paste = get_action_assert (de->builder, "edit_paste");
162 gtk_action_set_sensitive (edit_paste, x);
166 set_cut_copy_menuitem_sensitivity (PsppireDataWindow *de, gboolean x)
168 GtkAction *edit_copy = get_action_assert (de->builder, "edit_copy");
169 GtkAction *edit_cut = get_action_assert (de->builder, "edit_cut");
171 gtk_action_set_sensitive (edit_copy, x);
172 gtk_action_set_sensitive (edit_cut, x);
175 /* Run the EXECUTE command. */
177 execute (PsppireDataWindow *dw)
179 execute_const_syntax_string (dw, "EXECUTE.");
183 transformation_change_callback (bool transformations_pending,
186 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
188 GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
190 GtkWidget *menuitem =
191 gtk_ui_manager_get_widget (uim,"/ui/menubar/transform/transform_run-pending");
193 GtkWidget *status_label =
194 get_widget_assert (de->builder, "case-counter-area");
196 gtk_widget_set_sensitive (menuitem, transformations_pending);
199 if ( transformations_pending)
200 gtk_label_set_text (GTK_LABEL (status_label),
201 _("Transformations Pending"));
203 gtk_label_set_text (GTK_LABEL (status_label), "");
206 /* Callback for when the dictionary changes its filter variable */
208 on_filter_change (GObject *o, gint filter_index, gpointer data)
210 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
212 GtkWidget *filter_status_area =
213 get_widget_assert (de->builder, "filter-use-status-area");
215 if ( filter_index == -1 )
217 gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
221 PsppireVarStore *vs = NULL;
222 PsppireDict *dict = NULL;
223 struct variable *var ;
226 g_object_get (de->data_editor, "var-store", &vs, NULL);
227 g_object_get (vs, "dictionary", &dict, NULL);
229 var = psppire_dict_get_variable (dict, filter_index);
231 text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
233 gtk_label_set_text (GTK_LABEL (filter_status_area), text);
239 /* Callback for when the dictionary changes its split variables */
241 on_split_change (PsppireDict *dict, gpointer data)
243 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
245 size_t n_split_vars = dict_get_split_cnt (dict->dict);
247 GtkWidget *split_status_area =
248 get_widget_assert (de->builder, "split-file-status-area");
250 if ( n_split_vars == 0 )
252 gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
258 const struct variable *const * split_vars =
259 dict_get_split_vars (dict->dict);
261 text = g_string_new (_("Split by "));
263 for (i = 0 ; i < n_split_vars - 1; ++i )
265 g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
267 g_string_append (text, var_get_name (split_vars[i]));
269 gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
271 g_string_free (text, TRUE);
278 /* Callback for when the dictionary changes its weights */
280 on_weight_change (GObject *o, gint weight_index, gpointer data)
282 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
284 GtkWidget *weight_status_area =
285 get_widget_assert (de->builder, "weight-status-area");
287 if ( weight_index == -1 )
289 gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
293 struct variable *var ;
294 PsppireVarStore *vs = NULL;
295 PsppireDict *dict = NULL;
298 g_object_get (de->data_editor, "var-store", &vs, NULL);
299 g_object_get (vs, "dictionary", &dict, NULL);
301 var = psppire_dict_get_variable (dict, weight_index);
303 text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
305 gtk_label_set_text (GTK_LABEL (weight_status_area), text);
313 dump_rm (GtkRecentManager *rm)
315 GList *items = gtk_recent_manager_get_items (rm);
319 g_print ("Recent Items:\n");
320 for (i = items; i; i = i->next)
322 GtkRecentInfo *ri = i->data;
324 g_print ("Item: %s (Mime: %s) (Desc: %s) (URI: %s)\n",
325 gtk_recent_info_get_short_name (ri),
326 gtk_recent_info_get_mime_type (ri),
327 gtk_recent_info_get_description (ri),
328 gtk_recent_info_get_uri (ri)
332 gtk_recent_info_unref (ri);
340 name_has_por_suffix (const gchar *name)
342 size_t length = strlen (name);
343 return length > 4 && !c_strcasecmp (&name[length - 4], ".por");
347 name_has_sav_suffix (const gchar *name)
349 size_t length = strlen (name);
350 return length > 4 && !c_strcasecmp (&name[length - 4], ".sav");
353 /* Returns true if NAME has a suffix which might denote a PSPP file */
355 name_has_suffix (const gchar *name)
357 return name_has_por_suffix (name) || name_has_sav_suffix (name);
361 load_file (PsppireWindow *de, const gchar *file_name)
363 struct string filename;
364 gchar *utf8_file_name;
365 const char *mime_type;
369 ds_init_empty (&filename);
371 utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
373 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
375 g_free (utf8_file_name);
377 syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
378 ds_destroy (&filename);
380 ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
381 lex_reader_for_string (syntax));
384 mime_type = (name_has_por_suffix (file_name)
385 ? "application/x-spss-por"
386 : "application/x-spss-sav");
388 add_most_recent (file_name, mime_type);
393 /* Save DE to file */
395 save_file (PsppireWindow *w)
397 const gchar *file_name = NULL;
398 gchar *utf8_file_name = NULL;
400 struct string filename ;
401 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
404 file_name = psppire_window_get_filename (w);
406 fnx = g_string_new (file_name);
408 if ( ! name_has_suffix (fnx->str))
410 if ( de->save_as_portable)
411 g_string_append (fnx, ".por");
413 g_string_append (fnx, ".sav");
416 ds_init_empty (&filename);
418 utf8_file_name = g_filename_to_utf8 (fnx->str, -1, NULL, NULL, NULL);
420 g_string_free (fnx, TRUE);
422 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
423 g_free (utf8_file_name);
425 syntax = g_strdup_printf ("%s OUTFILE=%s.",
426 de->save_as_portable ? "EXPORT" : "SAVE",
427 ds_cstr (&filename));
429 ds_destroy (&filename);
431 g_free (execute_syntax_string (de, syntax));
436 insert_case (PsppireDataWindow *dw)
438 psppire_data_editor_insert_case (dw->data_editor);
442 on_insert_variable (PsppireDataWindow *dw)
444 psppire_data_editor_insert_variable (dw->data_editor);
449 display_dict (PsppireDataWindow *de)
451 execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
455 sysfile_info (PsppireDataWindow *de)
457 GtkWidget *dialog = psppire_window_file_chooser_dialog (PSPPIRE_WINDOW (de));
459 if ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
461 struct string filename;
463 gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
465 gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
470 ds_init_empty (&filename);
472 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
474 g_free (utf8_file_name);
476 syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
477 g_free (execute_syntax_string (de, syntax));
480 gtk_widget_destroy (dialog);
484 /* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
486 data_pick_filename (PsppireWindow *window)
488 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
489 GtkFileFilter *filter = gtk_file_filter_new ();
490 GtkWidget *button_sys;
492 gtk_file_chooser_dialog_new (_("Save"),
494 GTK_FILE_CHOOSER_ACTION_SAVE,
495 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
496 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
499 g_object_set (dialog, "local-only", FALSE, NULL);
501 gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
502 gtk_file_filter_add_mime_type (filter, "application/x-spss-sav");
503 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
505 filter = gtk_file_filter_new ();
506 gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
507 gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
508 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
510 filter = gtk_file_filter_new ();
511 gtk_file_filter_set_name (filter, _("All Files"));
512 gtk_file_filter_add_pattern (filter, "*");
513 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
516 GtkWidget *button_por;
517 GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
519 gtk_radio_button_new_with_label (NULL, _("System File"));
522 gtk_radio_button_new_with_label
523 (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
526 psppire_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
527 psppire_box_pack_start_defaults (GTK_BOX (vbox), button_por);
529 gtk_widget_show_all (vbox);
531 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
534 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
537 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
539 case GTK_RESPONSE_ACCEPT:
544 gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
547 de->save_as_portable =
548 ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
550 if ( ! name_has_suffix (filename->str))
552 if ( de->save_as_portable)
553 g_string_append (filename, ".por");
555 g_string_append (filename, ".sav");
558 psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
560 g_string_free (filename, TRUE);
567 gtk_widget_destroy (dialog);
571 confirm_delete_dataset (PsppireDataWindow *de,
572 const char *old_dataset,
573 const char *new_dataset,
574 const char *existing_dataset)
579 dialog = gtk_message_dialog_new (
580 GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
581 _("Delete Existing Dataset?"));
583 gtk_message_dialog_format_secondary_text (
584 GTK_MESSAGE_DIALOG (dialog),
585 _("Renaming \"%s\" to \"%s\" will destroy the existing "
586 "dataset named \"%s\". Are you sure that you want to do this?"),
587 old_dataset, new_dataset, existing_dataset);
589 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
590 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
591 GTK_STOCK_DELETE, GTK_RESPONSE_OK,
594 g_object_set (dialog, "icon-name", "pspp", NULL);
596 result = gtk_dialog_run (GTK_DIALOG (dialog));
598 gtk_widget_destroy (dialog);
600 return result == GTK_RESPONSE_OK;
604 on_rename_dataset (PsppireDataWindow *de)
606 struct dataset *ds = de->dataset;
607 struct session *session = dataset_session (ds);
608 const char *old_name = dataset_name (ds);
609 struct dataset *existing_dataset;
613 prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
615 new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
619 if (new_name == NULL)
622 existing_dataset = session_lookup_dataset (session, new_name);
623 if (existing_dataset == NULL || existing_dataset == ds
624 || confirm_delete_dataset (de, old_name, new_name,
625 dataset_name (existing_dataset)))
626 g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
633 on_edit_paste (PsppireDataWindow *de)
635 psppire_data_editor_clip_paste (de->data_editor);
639 on_edit_copy (PsppireDataWindow *de)
641 psppire_data_editor_clip_copy (de->data_editor);
647 on_edit_cut (PsppireDataWindow *de)
649 psppire_data_editor_clip_cut (de->data_editor);
654 status_bar_activate (PsppireDataWindow *de, GtkToggleAction *action)
656 GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
658 if ( gtk_toggle_action_get_active (action))
659 gtk_widget_show (statusbar);
661 gtk_widget_hide (statusbar);
666 grid_lines_activate (PsppireDataWindow *de, GtkToggleAction *action)
668 const gboolean grid_visible = gtk_toggle_action_get_active (action);
670 psppire_data_editor_show_grid (de->data_editor, grid_visible);
674 data_view_activate (PsppireDataWindow *de)
676 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
681 variable_view_activate (PsppireDataWindow *de)
683 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
688 fonts_activate (PsppireDataWindow *de)
690 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
691 PangoFontDescription *current_font;
694 gtk_font_selection_dialog_new (_("Font Selection"));
697 current_font = GTK_WIDGET(de->data_editor)->style->font_desc;
698 font_name = pango_font_description_to_string (current_font);
700 gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG (dialog), font_name);
704 gtk_window_set_transient_for (GTK_WINDOW (dialog),
705 GTK_WINDOW (toplevel));
707 if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
709 const gchar *font = gtk_font_selection_dialog_get_font_name
710 (GTK_FONT_SELECTION_DIALOG (dialog));
712 PangoFontDescription* font_desc =
713 pango_font_description_from_string (font);
715 psppire_data_editor_set_font (de->data_editor, font_desc);
718 gtk_widget_hide (dialog);
723 /* Callback for the value labels action */
725 toggle_value_labels (PsppireDataWindow *de, GtkToggleAction *ta)
727 g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
731 toggle_split_window (PsppireDataWindow *de, GtkToggleAction *ta)
733 psppire_data_editor_split_window (de->data_editor,
734 gtk_toggle_action_get_active (ta));
739 file_quit (PsppireDataWindow *de)
741 /* FIXME: Need to be more intelligent here.
742 Give the user the opportunity to save any unsaved data.
749 on_recent_data_select (GtkMenuShell *menushell,
750 PsppireWindow *window)
755 gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
757 file = g_filename_from_uri (uri, NULL, NULL);
761 open_data_window (window, file);
767 charset_from_mime_type (const char *mime_type)
773 if (mime_type == NULL)
776 charset = c_strcasestr (mime_type, "charset=");
784 /* Parse a "quoted-string" as defined by RFC 822. */
785 for (p++; *p != '\0' && *p != '"'; p++)
788 ds_put_byte (&s, *p);
789 else if (*++p != '\0')
790 ds_put_byte (&s, *p);
795 /* Parse a "token" as defined by RFC 2045. */
796 while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
797 ds_put_byte (&s, *p++);
799 if (!ds_is_empty (&s))
800 return ds_steal_cstr (&s);
807 on_recent_files_select (GtkMenuShell *menushell, gpointer user_data)
814 /* Get the file name and its encoding. */
815 item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
816 file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
817 encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
818 gtk_recent_info_unref (item);
820 se = psppire_syntax_window_new (encoding);
824 if ( psppire_window_load (PSPPIRE_WINDOW (se), file) )
825 gtk_widget_show (se);
827 gtk_widget_destroy (se);
835 enable_delete_cases (GtkWidget *w, gint case_num, gpointer data)
837 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
839 gtk_action_set_visible (de->delete_cases, case_num != -1);
844 enable_delete_variables (GtkWidget *w, gint var, gpointer data)
846 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
848 gtk_action_set_visible (de->delete_variables, var != -1);
851 /* Callback for when the datasheet/varsheet is selected */
853 on_switch_sheet (GtkNotebook *notebook,
854 GtkNotebookPage *page,
858 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (user_data);
860 GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
862 GtkWidget *view_data =
863 gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_data");
865 GtkWidget *view_variables =
866 gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_variables");
870 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
871 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_variables),
873 gtk_action_set_sensitive (de->insert_variable, TRUE);
874 gtk_action_set_sensitive (de->insert_case, FALSE);
875 gtk_action_set_sensitive (de->invoke_goto_dialog, FALSE);
877 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
878 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (view_data), TRUE);
879 gtk_action_set_sensitive (de->invoke_goto_dialog, TRUE);
880 gtk_action_set_sensitive (de->insert_case, TRUE);
883 g_assert_not_reached ();
888 update_paste_menuitem (de, page_num);
895 set_unsaved (gpointer w)
897 psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
901 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
902 Returns a pointer to the action
905 connect_action (PsppireDataWindow *dw, const char *action_name,
908 GtkAction *action = get_action_assert (dw->builder, action_name);
910 g_signal_connect_swapped (action, "activate", handler, dw);
915 /* Initializes as much of a PsppireDataWindow as we can and must before the
916 dataset has been set.
918 In particular, the 'menu' member is required in case the "filename" property
919 is set before the "dataset" property: otherwise PsppireWindow will try to
920 modify the menu as part of the "filename" property_set() function and end up
921 with a Gtk-CRITICAL since 'menu' is NULL. */
923 psppire_data_window_init (PsppireDataWindow *de)
927 de->builder = builder_new ("data-editor.ui");
929 uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
931 PSPPIRE_WINDOW (de)->menu =
932 GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows/windows_minimise_all")->parent);
936 psppire_data_window_finish_init (PsppireDataWindow *de,
939 static const struct dataset_callbacks cbs =
941 set_unsaved, /* changed */
942 transformation_change_callback, /* transformations_changed */
951 GtkWidget *box = gtk_vbox_new (FALSE, 0);
954 dict = psppire_dict_new_from_dict (dataset_dict (ds));
955 de->var_store = psppire_var_store_new (dict);
956 g_object_unref (dict);
957 de->data_store = psppire_data_store_new (dict);
958 psppire_data_store_set_reader (de->data_store, NULL);
960 menubar = get_widget_assert (de->builder, "menubar");
961 hb = get_widget_assert (de->builder, "handlebox1");
962 sb = get_widget_assert (de->builder, "status-bar");
965 PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de, de->var_store,
968 g_signal_connect_swapped (de->data_store, "case-changed",
969 G_CALLBACK (set_unsaved), de);
971 g_signal_connect_swapped (de->data_store, "case-inserted",
972 G_CALLBACK (set_unsaved), de);
974 g_signal_connect_swapped (de->data_store, "cases-deleted",
975 G_CALLBACK (set_unsaved), de);
977 dataset_set_callbacks (de->dataset, &cbs, de);
979 connect_help (de->builder);
981 gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
982 gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
983 gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
984 gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
986 gtk_container_add (GTK_CONTAINER (de), box);
988 set_cut_copy_menuitem_sensitivity (de, FALSE);
990 g_signal_connect_swapped (de->data_editor, "data-selection-changed",
991 G_CALLBACK (set_cut_copy_menuitem_sensitivity), de);
994 set_paste_menuitem_sensitivity (de, FALSE);
996 g_signal_connect_swapped (de->data_editor, "data-available-changed",
997 G_CALLBACK (set_paste_menuitem_sensitivity), de);
999 g_signal_connect (dict, "weight-changed",
1000 G_CALLBACK (on_weight_change),
1003 g_signal_connect (dict, "filter-changed",
1004 G_CALLBACK (on_filter_change),
1007 g_signal_connect (dict, "split-changed",
1008 G_CALLBACK (on_split_change),
1012 connect_action (de, "edit_copy", G_CALLBACK (on_edit_copy));
1014 connect_action (de, "edit_cut", G_CALLBACK (on_edit_cut));
1016 connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1018 connect_action (de, "file_import-text", G_CALLBACK (text_data_import_assistant));
1020 connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1022 connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1024 connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1026 connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1028 connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1030 connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1032 connect_action (de, "edit_paste", G_CALLBACK (on_edit_paste));
1034 de->insert_case = connect_action (de, "edit_insert-case", G_CALLBACK (insert_case));
1036 de->insert_variable = connect_action (de, "action_insert-variable", G_CALLBACK (on_insert_variable));
1038 de->invoke_goto_dialog = connect_action (de, "edit_goto-case", G_CALLBACK (goto_case_dialog));
1040 g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1043 de->delete_cases = get_action_assert (de->builder, "edit_clear-cases");
1045 g_signal_connect_swapped (de->delete_cases, "activate", G_CALLBACK (psppire_data_editor_delete_cases), de->data_editor);
1047 gtk_action_set_visible (de->delete_cases, FALSE);
1052 de->delete_variables = get_action_assert (de->builder, "edit_clear-variables");
1054 g_signal_connect_swapped (de->delete_variables, "activate", G_CALLBACK (psppire_data_editor_delete_variables), de->data_editor);
1056 gtk_action_set_visible (de->delete_variables, FALSE);
1060 connect_action (de, "data_transpose", G_CALLBACK (transpose_dialog));
1061 connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1062 connect_action (de, "data_aggregate", G_CALLBACK (aggregate_dialog));
1063 connect_action (de, "transform_compute", G_CALLBACK (compute_dialog));
1064 connect_action (de, "transform_autorecode", G_CALLBACK (autorecode_dialog));
1065 connect_action (de, "edit_find", G_CALLBACK (find_dialog));
1066 connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1067 connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1068 connect_action (de, "oneway-anova", G_CALLBACK (oneway_anova_dialog));
1069 connect_action (de, "paired-t-test", G_CALLBACK (t_test_paired_samples_dialog));
1070 connect_action (de, "one-sample-t-test", G_CALLBACK (t_test_one_sample_dialog));
1071 connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1072 connect_action (de, "transform_count", G_CALLBACK (count_dialog));
1073 connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1074 connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1075 connect_action (de, "analyze_frequencies", G_CALLBACK (frequencies_dialog));
1076 connect_action (de, "crosstabs", G_CALLBACK (crosstabs_dialog));
1077 connect_action (de, "univariate", G_CALLBACK (univariate_dialog));
1078 connect_action (de, "chi-square", G_CALLBACK (chisquare_dialog));
1079 connect_action (de, "binomial", G_CALLBACK (binomial_dialog));
1080 connect_action (de, "runs", G_CALLBACK (runs_dialog));
1081 connect_action (de, "ks-one-sample", G_CALLBACK (ks_one_sample_dialog));
1082 connect_action (de, "k-related-samples", G_CALLBACK (k_related_dialog));
1083 connect_action (de, "two-related-samples", G_CALLBACK (two_related_dialog));
1086 GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1088 GtkWidget *recent_data =
1089 gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-data");
1091 GtkWidget *recent_files =
1092 gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-files");
1095 GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1096 gtk_recent_manager_get_default ());
1098 GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1099 gtk_recent_manager_get_default ());
1101 g_object_set (menu_data, "show-tips", TRUE, NULL);
1102 g_object_set (menu_files, "show-tips", TRUE, NULL);
1105 GtkRecentFilter *filter = gtk_recent_filter_new ();
1107 gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1108 gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1110 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1112 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1115 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1118 g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1121 GtkRecentFilter *filter = gtk_recent_filter_new ();
1123 gtk_recent_filter_add_pattern (filter, "*.sps");
1124 gtk_recent_filter_add_pattern (filter, "*.SPS");
1126 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1128 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1131 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1133 g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1137 connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1140 g_signal_connect (de->data_editor,
1142 G_CALLBACK (enable_delete_cases),
1145 g_signal_connect (de->data_editor,
1146 "variables-selected",
1147 G_CALLBACK (enable_delete_variables),
1151 g_signal_connect (de->data_editor,
1153 G_CALLBACK (on_switch_sheet), de);
1155 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1156 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1158 connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1160 connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1162 connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1164 connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1166 connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1168 connect_action (de, "file_quit", G_CALLBACK (file_quit));
1170 connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1172 connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1174 g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1177 GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1179 merge_help_menu (uim);
1183 GtkWidget *data_sheet_cases_popup_menu = get_widget_assert (de->builder,
1184 "datasheet-cases-popup");
1186 GtkWidget *var_sheet_variable_popup_menu = get_widget_assert (de->builder,
1187 "varsheet-variable-popup");
1189 GtkWidget *data_sheet_variable_popup_menu = get_widget_assert (de->builder,
1190 "datasheet-variable-popup");
1192 g_signal_connect_swapped (get_action_assert (de->builder, "sort-up"), "activate",
1193 G_CALLBACK (psppire_data_editor_sort_ascending),
1196 g_signal_connect_swapped (get_action_assert (de->builder, "sort-down"), "activate",
1197 G_CALLBACK (psppire_data_editor_sort_descending),
1200 g_object_set (de->data_editor,
1201 "datasheet-column-menu", data_sheet_variable_popup_menu,
1202 "datasheet-row-menu", data_sheet_cases_popup_menu,
1203 "varsheet-row-menu", var_sheet_variable_popup_menu,
1207 gtk_widget_show (GTK_WIDGET (de->data_editor));
1208 gtk_widget_show (box);
1210 ll_push_head (&all_data_windows, &de->ll);
1214 psppire_data_window_dispose (GObject *object)
1216 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1218 if (dw->builder != NULL)
1220 g_object_unref (dw->builder);
1226 g_object_unref (dw->var_store);
1227 dw->var_store = NULL;
1232 g_object_unref (dw->data_store);
1233 dw->data_store = NULL;
1236 if (dw->ll.next != NULL)
1238 ll_remove (&dw->ll);
1242 if (G_OBJECT_CLASS (parent_class)->dispose)
1243 G_OBJECT_CLASS (parent_class)->dispose (object);
1247 psppire_data_window_finalize (GObject *object)
1249 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1253 struct dataset *dataset = dw->dataset;
1254 struct session *session = dataset_session (dataset);
1258 dataset_set_callbacks (dataset, NULL, NULL);
1259 session_set_active_dataset (session, NULL);
1260 dataset_destroy (dataset);
1263 if (G_OBJECT_CLASS (parent_class)->finalize)
1264 G_OBJECT_CLASS (parent_class)->finalize (object);
1268 psppire_data_window_set_property (GObject *object,
1270 const GValue *value,
1273 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1278 psppire_data_window_finish_init (window, g_value_get_pointer (value));
1281 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1287 psppire_data_window_get_property (GObject *object,
1292 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1297 g_value_set_pointer (value, window->dataset);
1300 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1306 psppire_data_window_new (struct dataset *ds)
1310 if (the_session == NULL)
1311 the_session = session_create ();
1315 char *dataset_name = session_generate_dataset_name (the_session);
1316 ds = dataset_create (the_session, dataset_name);
1317 free (dataset_name);
1319 assert (dataset_session (ds) == the_session);
1323 psppire_data_window_get_type (),
1324 "description", _("Data Editor"),
1328 if (dataset_name (ds) != NULL)
1329 g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1335 psppire_data_window_is_empty (PsppireDataWindow *dw)
1337 return psppire_var_store_get_var_cnt (dw->var_store) == 0;
1341 psppire_data_window_iface_init (PsppireWindowIface *iface)
1343 iface->save = save_file;
1344 iface->pick_filename = data_pick_filename;
1345 iface->load = load_file;
1349 psppire_default_data_window (void)
1351 if (ll_is_empty (&all_data_windows))
1352 create_data_window ();
1353 return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1357 psppire_data_window_set_default (PsppireDataWindow *pdw)
1359 ll_remove (&pdw->ll);
1360 ll_push_head (&all_data_windows, &pdw->ll);
1364 psppire_data_window_undefault (PsppireDataWindow *pdw)
1366 ll_remove (&pdw->ll);
1367 ll_push_tail (&all_data_windows, &pdw->ll);
1371 psppire_data_window_for_dataset (struct dataset *ds)
1373 PsppireDataWindow *pdw;
1375 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1376 if (pdw->dataset == ds)
1383 create_data_window (void)
1385 gtk_widget_show (psppire_data_window_new (NULL));
1389 open_data_window (PsppireWindow *victim, const char *file_name)
1393 if (PSPPIRE_IS_DATA_WINDOW (victim)
1394 && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1395 window = GTK_WIDGET (victim);
1397 window = psppire_data_window_new (NULL);
1399 psppire_window_load (PSPPIRE_WINDOW (window), file_name);
1400 gtk_widget_show (window);