1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 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/builder-wrapper.h"
28 #include "ui/gui/comments-dialog.h"
29 #include "ui/gui/entry-dialog.h"
30 #include "ui/gui/executor.h"
31 #include "ui/gui/help-menu.h"
32 #include "ui/gui/helper.h"
33 #include "ui/gui/helper.h"
34 #include "ui/gui/psppire-import-assistant.h"
35 #include "ui/gui/psppire-data-window.h"
36 #include "ui/gui/psppire-dialog-action.h"
37 #include "ui/gui/psppire-encoding-selector.h"
38 #include "ui/gui/psppire-syntax-window.h"
39 #include "ui/gui/psppire-window.h"
40 #include "ui/gui/psppire.h"
41 #include "ui/gui/recode-dialog.h"
42 #include "ui/gui/select-cases-dialog.h"
43 #include "ui/gui/split-file-dialog.h"
44 #include "ui/syntax-gen.h"
46 #include "gl/c-strcase.h"
47 #include "gl/c-strcasestr.h"
48 #include "gl/xvasprintf.h"
51 #define _(msgid) gettext (msgid)
52 #define N_(msgid) msgid
54 struct session *the_session;
55 struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
57 static void psppire_data_window_class_init (PsppireDataWindowClass *class);
58 static void psppire_data_window_init (PsppireDataWindow *data_editor);
61 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
63 static void psppire_data_window_dispose (GObject *object);
64 static void psppire_data_window_finalize (GObject *object);
65 static void psppire_data_window_set_property (GObject *object,
69 static void psppire_data_window_get_property (GObject *object,
74 static guint psppire_data_window_add_ui (PsppireDataWindow *, GtkUIManager *);
75 static void psppire_data_window_remove_ui (PsppireDataWindow *,
76 GtkUIManager *, guint);
79 psppire_data_window_get_type (void)
81 static GType psppire_data_window_type = 0;
83 if (!psppire_data_window_type)
85 static const GTypeInfo psppire_data_window_info =
87 sizeof (PsppireDataWindowClass),
90 (GClassInitFunc)psppire_data_window_class_init,
91 (GClassFinalizeFunc) NULL,
93 sizeof (PsppireDataWindow),
95 (GInstanceInitFunc) psppire_data_window_init,
98 static const GInterfaceInfo window_interface_info =
100 (GInterfaceInitFunc) psppire_data_window_iface_init,
105 psppire_data_window_type =
106 g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
107 &psppire_data_window_info, 0);
110 g_type_add_interface_static (psppire_data_window_type,
111 PSPPIRE_TYPE_WINDOW_MODEL,
112 &window_interface_info);
115 return psppire_data_window_type;
118 static GObjectClass *parent_class ;
125 psppire_data_window_class_init (PsppireDataWindowClass *class)
127 GObjectClass *object_class = G_OBJECT_CLASS (class);
129 parent_class = g_type_class_peek_parent (class);
131 object_class->dispose = psppire_data_window_dispose;
132 object_class->finalize = psppire_data_window_finalize;
133 object_class->set_property = psppire_data_window_set_property;
134 object_class->get_property = psppire_data_window_get_property;
136 g_object_class_install_property (
137 object_class, PROP_DATASET,
138 g_param_spec_pointer ("dataset", "Dataset",
139 "'struct datset *' represented by the window",
140 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
143 /* Run the EXECUTE command. */
145 execute (PsppireDataWindow *dw)
147 execute_const_syntax_string (dw, "EXECUTE.");
151 transformation_change_callback (bool transformations_pending,
154 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
156 GtkWidget *status_label =
157 get_widget_assert (de->builder, "case-counter-area");
159 { /* Set the sensitivity of the "Transformations Pending" menuitem */
161 GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
163 GtkWidget *menuitem =
164 gtk_ui_manager_get_widget (uim,"/ui/menubar/transform/transform_run-pending");
166 gtk_widget_set_sensitive (menuitem, transformations_pending);
170 if ( transformations_pending)
171 gtk_label_set_text (GTK_LABEL (status_label),
172 _("Transformations Pending"));
174 gtk_label_set_text (GTK_LABEL (status_label), "");
177 /* Callback for when the dictionary changes its filter variable */
179 on_filter_change (GObject *o, gint filter_index, gpointer data)
181 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
183 GtkWidget *filter_status_area =
184 get_widget_assert (de->builder, "filter-use-status-area");
186 if ( filter_index == -1 )
188 gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
192 PsppireDict *dict = NULL;
193 struct variable *var ;
196 g_object_get (de->data_editor, "dictionary", &dict, NULL);
198 var = psppire_dict_get_variable (dict, filter_index);
200 text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
202 gtk_label_set_text (GTK_LABEL (filter_status_area), text);
208 /* Callback for when the dictionary changes its split variables */
210 on_split_change (PsppireDict *dict, gpointer data)
212 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
214 size_t n_split_vars = dict_get_split_cnt (dict->dict);
216 GtkWidget *split_status_area =
217 get_widget_assert (de->builder, "split-file-status-area");
219 if ( n_split_vars == 0 )
221 gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
227 const struct variable *const * split_vars =
228 dict_get_split_vars (dict->dict);
230 text = g_string_new (_("Split by "));
232 for (i = 0 ; i < n_split_vars - 1; ++i )
234 g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
236 g_string_append (text, var_get_name (split_vars[i]));
238 gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
240 g_string_free (text, TRUE);
247 /* Callback for when the dictionary changes its weights */
249 on_weight_change (GObject *o, gint weight_index, gpointer data)
251 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
253 GtkWidget *weight_status_area =
254 get_widget_assert (de->builder, "weight-status-area");
256 if ( weight_index == -1 )
258 gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
262 struct variable *var ;
263 PsppireDict *dict = NULL;
266 g_object_get (de->data_editor, "dictionary", &dict, NULL);
268 var = psppire_dict_get_variable (dict, weight_index);
270 text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
272 gtk_label_set_text (GTK_LABEL (weight_status_area), text);
280 dump_rm (GtkRecentManager *rm)
282 GList *items = gtk_recent_manager_get_items (rm);
286 g_print ("Recent Items:\n");
287 for (i = items; i; i = i->next)
289 GtkRecentInfo *ri = i->data;
291 g_print ("Item: %s (Mime: %s) (Desc: %s) (URI: %s)\n",
292 gtk_recent_info_get_short_name (ri),
293 gtk_recent_info_get_mime_type (ri),
294 gtk_recent_info_get_description (ri),
295 gtk_recent_info_get_uri (ri)
299 gtk_recent_info_unref (ri);
307 has_suffix (const gchar *name, const gchar *suffix)
309 size_t name_len = strlen (name);
310 size_t suffix_len = strlen (suffix);
311 return (name_len > suffix_len
312 && !c_strcasecmp (&name[name_len - suffix_len], suffix));
316 name_has_por_suffix (const gchar *name)
318 return has_suffix (name, ".por");
322 name_has_sav_suffix (const gchar *name)
324 return has_suffix (name, ".sav") || has_suffix (name, ".zsav");
327 /* Returns true if NAME has a suffix which might denote a PSPP file */
329 name_has_suffix (const gchar *name)
331 return name_has_por_suffix (name) || name_has_sav_suffix (name);
335 load_file (PsppireWindow *de, const gchar *file_name, const char *encoding,
338 const char *mime_type = NULL;
339 gchar *syntax = NULL;
344 gchar *utf8_file_name;
345 struct string filename;
347 utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
349 if (NULL == utf8_file_name)
352 ds_init_empty (&filename);
353 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
355 g_free (utf8_file_name);
357 if (encoding && encoding[0])
358 syntax = g_strdup_printf ("GET FILE=%s ENCODING='%s'.",
359 ds_cstr (&filename), encoding);
361 syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
362 ds_destroy (&filename);
369 ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
370 lex_reader_for_string (syntax, "UTF-8"));
373 if (ok && syn == NULL)
375 if (name_has_por_suffix (file_name))
376 mime_type = "application/x-spss-por";
377 else if (name_has_sav_suffix (file_name))
378 mime_type = "application/x-spss-sav";
380 add_most_recent (file_name, mime_type, encoding);
387 psppire_data_window_format_to_string (enum PsppireDataWindowFormat format)
389 if (format == PSPPIRE_DATA_WINDOW_SAV)
391 else if (format == PSPPIRE_DATA_WINDOW_ZSAV)
397 /* Save DE to file */
399 save_file (PsppireWindow *w)
401 const gchar *file_name = NULL;
402 gchar *utf8_file_name = NULL;
404 struct string filename ;
405 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
408 file_name = psppire_window_get_filename (w);
410 fnx = g_string_new (file_name);
412 if ( ! name_has_suffix (fnx->str))
413 g_string_append (fnx, psppire_data_window_format_to_string (de->format));
415 ds_init_empty (&filename);
417 utf8_file_name = g_filename_to_utf8 (fnx->str, -1, NULL, NULL, NULL);
419 g_string_free (fnx, TRUE);
421 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
422 g_free (utf8_file_name);
424 if (de->format == PSPPIRE_DATA_WINDOW_SAV)
425 syntax = g_strdup_printf ("SAVE OUTFILE=%s.", ds_cstr (&filename));
426 else if (de->format == PSPPIRE_DATA_WINDOW_ZSAV)
427 syntax = g_strdup_printf ("SAVE /ZCOMPRESSED /OUTFILE=%s.",
428 ds_cstr (&filename));
430 syntax = g_strdup_printf ("EXPORT OUTFILE=%s.", ds_cstr (&filename));
432 ds_destroy (&filename);
434 g_free (execute_syntax_string (de, syntax));
439 display_dict (PsppireDataWindow *de)
441 execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
445 sysfile_info (PsppireDataWindow *de)
447 GtkWidget *dialog = psppire_window_file_chooser_dialog (PSPPIRE_WINDOW (de));
449 if ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
451 struct string filename;
453 gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
454 gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
457 const gchar *encoding = psppire_encoding_selector_get_encoding (
458 gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
462 ds_init_empty (&filename);
464 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
466 g_free (utf8_file_name);
469 syntax = g_strdup_printf ("SYSFILE INFO %s ENCODING='%s'.",
470 ds_cstr (&filename), encoding);
472 syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
473 g_free (execute_syntax_string (de, syntax));
476 gtk_widget_destroy (dialog);
480 /* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
482 data_pick_filename (PsppireWindow *window)
484 GtkListStore *list_store;
485 GtkWidget *combo_box;
487 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
488 GtkFileFilter *filter;
490 gtk_file_chooser_dialog_new (_("Save"),
492 GTK_FILE_CHOOSER_ACTION_SAVE,
493 _("Cancel"), GTK_RESPONSE_CANCEL,
494 _("Save"), GTK_RESPONSE_ACCEPT,
497 g_object_set (dialog, "local-only", FALSE, NULL);
499 filter = gtk_file_filter_new ();
500 gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
501 gtk_file_filter_add_mime_type (filter, "application/x-spss-sav");
502 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
504 filter = gtk_file_filter_new ();
505 gtk_file_filter_set_name (filter, _("Compressed System Files (*.zsav)"));
506 gtk_file_filter_add_pattern (filter, "*.zsav");
507 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
509 filter = gtk_file_filter_new ();
510 gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
511 gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
512 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
514 filter = gtk_file_filter_new ();
515 gtk_file_filter_set_name (filter, _("All Files"));
516 gtk_file_filter_add_pattern (filter, "*");
517 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
518 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
521 GtkCellRenderer *cell;
526 list_store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
527 combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store));
529 gtk_list_store_append (list_store, &iter);
530 gtk_list_store_set (list_store, &iter,
531 0, PSPPIRE_DATA_WINDOW_SAV,
534 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
536 gtk_list_store_append (list_store, &iter);
537 gtk_list_store_set (list_store, &iter,
538 0, PSPPIRE_DATA_WINDOW_ZSAV,
539 1, _("Compressed System File"),
542 gtk_list_store_append (list_store, &iter);
543 gtk_list_store_set (list_store, &iter,
544 0, PSPPIRE_DATA_WINDOW_POR,
545 1, _("Portable File"),
548 label = gtk_label_new (_("Format:"));
550 cell = gtk_cell_renderer_text_new ();
551 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, FALSE);
552 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box), cell,
555 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
556 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
557 gtk_box_pack_start (GTK_BOX (hbox), combo_box, FALSE, FALSE, 0);
558 gtk_widget_show_all (hbox);
560 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), hbox);
563 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
566 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
568 case GTK_RESPONSE_ACCEPT:
573 gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
579 gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter);
580 gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter,
585 if ( ! name_has_suffix (filename->str))
586 g_string_append (filename,
587 psppire_data_window_format_to_string (format));
589 psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
591 g_string_free (filename, TRUE);
598 gtk_widget_destroy (dialog);
602 confirm_delete_dataset (PsppireDataWindow *de,
603 const char *old_dataset,
604 const char *new_dataset,
605 const char *existing_dataset)
610 dialog = gtk_message_dialog_new (
611 GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
612 _("Delete Existing Dataset?"));
614 gtk_message_dialog_format_secondary_text (
615 GTK_MESSAGE_DIALOG (dialog),
616 _("Renaming \"%s\" to \"%s\" will destroy the existing "
617 "dataset named \"%s\". Are you sure that you want to do this?"),
618 old_dataset, new_dataset, existing_dataset);
620 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
621 _("Cancel"), GTK_RESPONSE_CANCEL,
622 _("Delete"), GTK_RESPONSE_OK,
625 g_object_set (dialog, "icon-name", "pspp", NULL);
627 result = gtk_dialog_run (GTK_DIALOG (dialog));
629 gtk_widget_destroy (dialog);
631 return result == GTK_RESPONSE_OK;
635 on_rename_dataset (PsppireDataWindow *de)
637 struct dataset *ds = de->dataset;
638 struct session *session = dataset_session (ds);
639 const char *old_name = dataset_name (ds);
640 struct dataset *existing_dataset;
644 prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
646 new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
650 if (new_name == NULL)
653 existing_dataset = session_lookup_dataset (session, new_name);
654 if (existing_dataset == NULL || existing_dataset == ds
655 || confirm_delete_dataset (de, old_name, new_name,
656 dataset_name (existing_dataset)))
657 g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
664 status_bar_activate (PsppireDataWindow *de, GtkToggleAction *action)
666 GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
668 if ( gtk_toggle_action_get_active (action))
669 gtk_widget_show (statusbar);
671 gtk_widget_hide (statusbar);
676 grid_lines_activate (PsppireDataWindow *de, GtkToggleAction *action)
678 const gboolean grid_visible = gtk_toggle_action_get_active (action);
680 psppire_data_editor_show_grid (de->data_editor, grid_visible);
684 data_view_activate (PsppireDataWindow *de)
686 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
691 variable_view_activate (PsppireDataWindow *de)
693 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
698 fonts_activate (PsppireDataWindow *de)
700 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
701 GtkWidget *dialog = gtk_font_chooser_dialog_new (NULL, GTK_WINDOW (toplevel));
702 GtkStyle *style = gtk_widget_get_style (GTK_WIDGET(de->data_editor));
703 PangoFontDescription *current_font = style->font_desc;
705 gtk_font_chooser_set_font_desc (GTK_FONT_CHOOSER (dialog), current_font);
707 gtk_window_set_transient_for (GTK_WINDOW (dialog),
708 GTK_WINDOW (toplevel));
710 if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
712 PangoFontDescription* font_desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (dialog));
714 psppire_data_editor_set_font (de->data_editor, font_desc);
717 gtk_widget_hide (dialog);
722 /* Callback for the value labels action */
724 toggle_value_labels (PsppireDataWindow *de, GtkToggleAction *ta)
726 g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
730 toggle_split_window (PsppireDataWindow *de, GtkToggleAction *ta)
732 psppire_data_editor_split_window (de->data_editor,
733 gtk_toggle_action_get_active (ta));
738 file_quit (PsppireDataWindow *de)
740 /* FIXME: Need to be more intelligent here.
741 Give the user the opportunity to save any unsaved data.
747 on_recent_data_select (GtkMenuShell *menushell,
748 PsppireWindow *window)
753 gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
755 file = g_filename_from_uri (uri, NULL, NULL);
759 open_data_window (window, file, NULL, NULL);
765 charset_from_mime_type (const char *mime_type)
771 if (mime_type == NULL)
774 charset = c_strcasestr (mime_type, "charset=");
782 /* Parse a "quoted-string" as defined by RFC 822. */
783 for (p++; *p != '\0' && *p != '"'; p++)
786 ds_put_byte (&s, *p);
787 else if (*++p != '\0')
788 ds_put_byte (&s, *p);
793 /* Parse a "token" as defined by RFC 2045. */
794 while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
795 ds_put_byte (&s, *p++);
797 if (!ds_is_empty (&s))
798 return ds_steal_cstr (&s);
805 on_recent_files_select (GtkMenuShell *menushell, gpointer user_data)
812 /* Get the file name and its encoding. */
813 item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
814 file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
815 encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
816 gtk_recent_info_unref (item);
818 se = psppire_syntax_window_new (encoding);
822 if ( psppire_window_load (PSPPIRE_WINDOW (se), file, encoding, NULL) )
823 gtk_widget_show (se);
825 gtk_widget_destroy (se);
831 set_unsaved (gpointer w)
833 psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
837 on_switch_page (PsppireDataEditor *de, gpointer p,
838 gint pagenum, PsppireDataWindow *dw)
840 /* Set the appropriate ui_manager according to the selected page.
841 This is necessary, because the menus for the variable view and
842 the data view are different (slightly). */
844 gboolean is_ds = pagenum == PSPPIRE_DATA_EDITOR_DATA_VIEW;
845 const char *path = (is_ds
846 ? "/ui/menubar/view/view_data"
847 : "/ui/menubar/view/view_variables");
849 GtkWidget *page_menu_item = gtk_ui_manager_get_widget (dw->ui_manager, path);
850 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (page_menu_item), TRUE);
854 on_ui_manager_changed (PsppireDataEditor *de,
855 GParamSpec *pspec UNUSED,
856 PsppireDataWindow *dw)
858 GtkUIManager *uim = psppire_data_editor_get_ui_manager (de);
864 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
865 g_object_unref (dw->uim);
872 g_object_ref (dw->uim);
873 dw->merge_id = psppire_data_window_add_ui (dw, dw->uim);
877 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
878 Returns a pointer to the action
881 connect_action (PsppireDataWindow *dw, const char *action_name,
884 GtkAction *action = get_action_assert (dw->builder, action_name);
886 g_signal_connect_swapped (action, "activate", handler, dw);
891 /* Only a data file with at least one variable can be saved. */
893 enable_save (PsppireDataWindow *dw)
895 gboolean enable = psppire_dict_get_var_cnt (dw->dict) > 0;
897 gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save"),
899 gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save_as"),
903 /* Initializes as much of a PsppireDataWindow as we can and must before the
904 dataset has been set.
906 In particular, the 'menu' member is required in case the "filename" property
907 is set before the "dataset" property: otherwise PsppireWindow will try to
908 modify the menu as part of the "filename" property_set() function and end up
909 with a Gtk-CRITICAL since 'menu' is NULL. */
911 psppire_data_window_init (PsppireDataWindow *de)
914 de->builder = builder_new ("data-editor.ui");
916 de->ui_manager = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
918 w = gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/windows/windows_minimise_all");
920 PSPPIRE_WINDOW (de)->menu = GTK_MENU_SHELL (gtk_widget_get_parent (w));
927 file_import (PsppireDataWindow *dw)
929 GtkWidget *w = psppire_import_assistant_new (GTK_WINDOW (dw));
930 PsppireImportAssistant *asst = PSPPIRE_IMPORT_ASSISTANT (w);
931 gtk_widget_show_all (w);
933 asst->main_loop = g_main_loop_new (NULL, TRUE);
934 g_main_loop_run (asst->main_loop);
935 g_main_loop_unref (asst->main_loop);
937 if (!asst->file_name)
940 switch (asst->response)
942 case GTK_RESPONSE_APPLY:
945 gchar *fn = g_path_get_basename (asst->file_name);
946 open_data_window (PSPPIRE_WINDOW (dw), fn, NULL, psppire_import_assistant_generate_syntax (asst));
950 case PSPPIRE_RESPONSE_PASTE:
952 free (paste_syntax_to_window (psppire_import_assistant_generate_syntax (asst)));
959 gtk_widget_destroy (GTK_WIDGET (asst));
963 psppire_data_window_finish_init (PsppireDataWindow *de,
966 static const struct dataset_callbacks cbs =
968 set_unsaved, /* changed */
969 transformation_change_callback, /* transformations_changed */
976 GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
979 de->dict = psppire_dict_new_from_dict (dataset_dict (ds));
980 de->data_store = psppire_data_store_new (de->dict);
981 psppire_data_store_set_reader (de->data_store, NULL);
983 menubar = get_widget_assert (de->builder, "menubar");
984 hb = get_widget_assert (de->builder, "toolbar");
985 sb = get_widget_assert (de->builder, "status-bar");
991 PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store));
992 g_signal_connect (de->data_editor, "switch-page",
993 G_CALLBACK (on_switch_page), de);
995 g_signal_connect_swapped (de->data_store, "case-changed",
996 G_CALLBACK (set_unsaved), de);
998 g_signal_connect_swapped (de->data_store, "case-inserted",
999 G_CALLBACK (set_unsaved), de);
1001 g_signal_connect_swapped (de->data_store, "cases-deleted",
1002 G_CALLBACK (set_unsaved), de);
1004 dataset_set_callbacks (de->dataset, &cbs, de);
1006 connect_help (de->builder);
1008 gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
1009 gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
1010 gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
1011 gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
1013 gtk_container_add (GTK_CONTAINER (de), box);
1015 g_signal_connect (de->dict, "weight-changed",
1016 G_CALLBACK (on_weight_change),
1019 g_signal_connect (de->dict, "filter-changed",
1020 G_CALLBACK (on_filter_change),
1023 g_signal_connect (de->dict, "split-changed",
1024 G_CALLBACK (on_split_change),
1027 g_signal_connect_swapped (de->dict, "backend-changed",
1028 G_CALLBACK (enable_save), de);
1029 g_signal_connect_swapped (de->dict, "variable-inserted",
1030 G_CALLBACK (enable_save), de);
1031 g_signal_connect_swapped (de->dict, "variable-deleted",
1032 G_CALLBACK (enable_save), de);
1035 connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1036 connect_action (de, "file_import", G_CALLBACK (file_import));
1038 connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1039 connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1040 connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1041 connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1042 connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1043 connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1045 g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1047 connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1048 connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1049 connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1050 connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1051 connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1054 GtkWidget *recent_data =
1055 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-data");
1057 GtkWidget *recent_files =
1058 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-files");
1061 GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1062 gtk_recent_manager_get_default ());
1064 GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1065 gtk_recent_manager_get_default ());
1067 g_object_set (menu_data, "show-tips", TRUE, NULL);
1068 g_object_set (menu_files, "show-tips", TRUE, NULL);
1071 GtkRecentFilter *filter = gtk_recent_filter_new ();
1073 gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1074 gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1076 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1078 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1081 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1084 g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1087 GtkRecentFilter *filter = gtk_recent_filter_new ();
1089 gtk_recent_filter_add_pattern (filter, "*.sps");
1090 gtk_recent_filter_add_pattern (filter, "*.SPS");
1092 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1094 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1097 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1099 g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1103 connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1106 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1107 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1109 connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1111 connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1113 connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1115 connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1117 connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1119 connect_action (de, "file_quit", G_CALLBACK (file_quit));
1121 connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1123 connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1125 g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1127 gtk_menu_shell_append (GTK_MENU_SHELL (menubar), create_help_menu (GTK_WINDOW (de)));
1129 g_signal_connect (de->data_editor, "notify::ui-manager",
1130 G_CALLBACK (on_ui_manager_changed), de);
1131 on_ui_manager_changed (de->data_editor, NULL, de);
1133 gtk_widget_show (GTK_WIDGET (de->data_editor));
1134 gtk_widget_show (box);
1136 ll_push_head (&all_data_windows, &de->ll);
1140 psppire_data_window_dispose (GObject *object)
1142 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1146 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
1147 g_object_unref (dw->uim);
1151 if (dw->builder != NULL)
1153 g_object_unref (dw->builder);
1159 g_signal_handlers_disconnect_by_func (dw->dict,
1160 G_CALLBACK (enable_save), dw);
1161 g_signal_handlers_disconnect_by_func (dw->dict,
1162 G_CALLBACK (on_weight_change), dw);
1163 g_signal_handlers_disconnect_by_func (dw->dict,
1164 G_CALLBACK (on_filter_change), dw);
1165 g_signal_handlers_disconnect_by_func (dw->dict,
1166 G_CALLBACK (on_split_change), dw);
1168 g_object_unref (dw->dict);
1174 g_object_unref (dw->data_store);
1175 dw->data_store = NULL;
1178 if (dw->ll.next != NULL)
1180 ll_remove (&dw->ll);
1184 if (G_OBJECT_CLASS (parent_class)->dispose)
1185 G_OBJECT_CLASS (parent_class)->dispose (object);
1189 psppire_data_window_finalize (GObject *object)
1191 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1195 struct dataset *dataset = dw->dataset;
1196 struct session *session = dataset_session (dataset);
1200 dataset_set_callbacks (dataset, NULL, NULL);
1201 session_set_active_dataset (session, NULL);
1202 dataset_destroy (dataset);
1205 if (G_OBJECT_CLASS (parent_class)->finalize)
1206 G_OBJECT_CLASS (parent_class)->finalize (object);
1210 psppire_data_window_set_property (GObject *object,
1212 const GValue *value,
1215 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1220 psppire_data_window_finish_init (window, g_value_get_pointer (value));
1223 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1229 psppire_data_window_get_property (GObject *object,
1234 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1239 g_value_set_pointer (value, window->dataset);
1242 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1248 psppire_data_window_add_ui (PsppireDataWindow *pdw, GtkUIManager *uim)
1254 ui_string = gtk_ui_manager_get_ui (uim);
1255 merge_id = gtk_ui_manager_add_ui_from_string (pdw->ui_manager, ui_string,
1259 g_return_val_if_fail (merge_id != 0, 0);
1261 list = gtk_ui_manager_get_action_groups (uim);
1262 for (; list != NULL; list = list->next)
1264 GtkActionGroup *action_group = list->data;
1265 GList *actions = gtk_action_group_list_actions (action_group);
1268 for (action = actions; action != NULL; action = action->next)
1270 GtkAction *a = action->data;
1272 if (PSPPIRE_IS_DIALOG_ACTION (a))
1273 g_object_set (a, "manager", pdw->ui_manager, NULL);
1276 gtk_ui_manager_insert_action_group (pdw->ui_manager, action_group, 0);
1279 gtk_window_add_accel_group (GTK_WINDOW (pdw),
1280 gtk_ui_manager_get_accel_group (uim));
1286 psppire_data_window_remove_ui (PsppireDataWindow *pdw,
1287 GtkUIManager *uim, guint merge_id)
1291 g_return_if_fail (merge_id != 0);
1293 gtk_ui_manager_remove_ui (pdw->ui_manager, merge_id);
1295 list = gtk_ui_manager_get_action_groups (uim);
1296 for (; list != NULL; list = list->next)
1298 GtkActionGroup *action_group = list->data;
1299 gtk_ui_manager_remove_action_group (pdw->ui_manager, action_group);
1302 gtk_window_remove_accel_group (GTK_WINDOW (pdw),
1303 gtk_ui_manager_get_accel_group (uim));
1307 psppire_data_window_new (struct dataset *ds)
1311 if (the_session == NULL)
1312 the_session = session_create (NULL);
1316 char *dataset_name = session_generate_dataset_name (the_session);
1317 ds = dataset_create (the_session, dataset_name);
1318 free (dataset_name);
1320 assert (dataset_session (ds) == the_session);
1324 psppire_data_window_get_type (),
1325 "description", _("Data Editor"),
1329 if (dataset_name (ds) != NULL)
1330 g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1336 psppire_data_window_is_empty (PsppireDataWindow *dw)
1338 return psppire_dict_get_var_cnt (dw->dict) == 0;
1342 psppire_data_window_iface_init (PsppireWindowIface *iface)
1344 iface->save = save_file;
1345 iface->pick_filename = data_pick_filename;
1346 iface->load = load_file;
1350 psppire_default_data_window (void)
1352 if (ll_is_empty (&all_data_windows))
1353 create_data_window ();
1354 return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1358 psppire_data_window_set_default (PsppireDataWindow *pdw)
1360 ll_remove (&pdw->ll);
1361 ll_push_head (&all_data_windows, &pdw->ll);
1365 psppire_data_window_undefault (PsppireDataWindow *pdw)
1367 ll_remove (&pdw->ll);
1368 ll_push_tail (&all_data_windows, &pdw->ll);
1372 psppire_data_window_for_dataset (struct dataset *ds)
1374 PsppireDataWindow *pdw;
1376 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1377 if (pdw->dataset == ds)
1384 psppire_data_window_for_data_store (PsppireDataStore *data_store)
1386 PsppireDataWindow *pdw;
1388 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1389 if (pdw->data_store == data_store)
1396 create_data_window (void)
1398 gtk_widget_show (psppire_data_window_new (NULL));
1402 open_data_window (PsppireWindow *victim, const char *file_name,
1403 const char *encoding, gpointer hint)
1407 if (PSPPIRE_IS_DATA_WINDOW (victim)
1408 && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1410 window = GTK_WIDGET (victim);
1411 gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
1414 window = psppire_data_window_new (NULL);
1416 psppire_window_load (PSPPIRE_WINDOW (window), file_name, encoding, hint);
1417 gtk_widget_show_all (window);