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/gui/weight-cases-dialog.h"
45 #include "ui/syntax-gen.h"
47 #include "gl/c-strcase.h"
48 #include "gl/c-strcasestr.h"
49 #include "gl/xvasprintf.h"
52 #define _(msgid) gettext (msgid)
53 #define N_(msgid) msgid
55 struct session *the_session;
56 struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
58 static void psppire_data_window_class_init (PsppireDataWindowClass *class);
59 static void psppire_data_window_init (PsppireDataWindow *data_editor);
62 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
64 static void psppire_data_window_dispose (GObject *object);
65 static void psppire_data_window_finalize (GObject *object);
66 static void psppire_data_window_set_property (GObject *object,
70 static void psppire_data_window_get_property (GObject *object,
75 static guint psppire_data_window_add_ui (PsppireDataWindow *, GtkUIManager *);
76 static void psppire_data_window_remove_ui (PsppireDataWindow *,
77 GtkUIManager *, guint);
80 psppire_data_window_get_type (void)
82 static GType psppire_data_window_type = 0;
84 if (!psppire_data_window_type)
86 static const GTypeInfo psppire_data_window_info =
88 sizeof (PsppireDataWindowClass),
91 (GClassInitFunc)psppire_data_window_class_init,
92 (GClassFinalizeFunc) NULL,
94 sizeof (PsppireDataWindow),
96 (GInstanceInitFunc) psppire_data_window_init,
99 static const GInterfaceInfo window_interface_info =
101 (GInterfaceInitFunc) psppire_data_window_iface_init,
106 psppire_data_window_type =
107 g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
108 &psppire_data_window_info, 0);
111 g_type_add_interface_static (psppire_data_window_type,
112 PSPPIRE_TYPE_WINDOW_MODEL,
113 &window_interface_info);
116 return psppire_data_window_type;
119 static GObjectClass *parent_class ;
126 psppire_data_window_class_init (PsppireDataWindowClass *class)
128 GObjectClass *object_class = G_OBJECT_CLASS (class);
130 parent_class = g_type_class_peek_parent (class);
132 object_class->dispose = psppire_data_window_dispose;
133 object_class->finalize = psppire_data_window_finalize;
134 object_class->set_property = psppire_data_window_set_property;
135 object_class->get_property = psppire_data_window_get_property;
137 g_object_class_install_property (
138 object_class, PROP_DATASET,
139 g_param_spec_pointer ("dataset", "Dataset",
140 "'struct datset *' represented by the window",
141 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
144 /* Run the EXECUTE command. */
146 execute (PsppireDataWindow *dw)
148 execute_const_syntax_string (dw, "EXECUTE.");
152 transformation_change_callback (bool transformations_pending,
155 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
157 GtkWidget *status_label =
158 get_widget_assert (de->builder, "case-counter-area");
160 { /* Set the sensitivity of the "Transformations Pending" menuitem */
162 GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
164 GtkWidget *menuitem =
165 gtk_ui_manager_get_widget (uim,"/ui/menubar/transform/transform_run-pending");
167 gtk_widget_set_sensitive (menuitem, transformations_pending);
171 if ( transformations_pending)
172 gtk_label_set_text (GTK_LABEL (status_label),
173 _("Transformations Pending"));
175 gtk_label_set_text (GTK_LABEL (status_label), "");
178 /* Callback for when the dictionary changes its filter variable */
180 on_filter_change (GObject *o, gint filter_index, gpointer data)
182 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
184 GtkWidget *filter_status_area =
185 get_widget_assert (de->builder, "filter-use-status-area");
187 if ( filter_index == -1 )
189 gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
193 PsppireDict *dict = NULL;
194 struct variable *var ;
197 g_object_get (de->data_editor, "dictionary", &dict, NULL);
199 var = psppire_dict_get_variable (dict, filter_index);
201 text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
203 gtk_label_set_text (GTK_LABEL (filter_status_area), text);
209 /* Callback for when the dictionary changes its split variables */
211 on_split_change (PsppireDict *dict, gpointer data)
213 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
215 size_t n_split_vars = dict_get_split_cnt (dict->dict);
217 GtkWidget *split_status_area =
218 get_widget_assert (de->builder, "split-file-status-area");
220 if ( n_split_vars == 0 )
222 gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
228 const struct variable *const * split_vars =
229 dict_get_split_vars (dict->dict);
231 text = g_string_new (_("Split by "));
233 for (i = 0 ; i < n_split_vars - 1; ++i )
235 g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
237 g_string_append (text, var_get_name (split_vars[i]));
239 gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
241 g_string_free (text, TRUE);
248 /* Callback for when the dictionary changes its weights */
250 on_weight_change (GObject *o, gint weight_index, gpointer data)
252 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
254 GtkWidget *weight_status_area =
255 get_widget_assert (de->builder, "weight-status-area");
257 if ( weight_index == -1 )
259 gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
263 struct variable *var ;
264 PsppireDict *dict = NULL;
267 g_object_get (de->data_editor, "dictionary", &dict, NULL);
269 var = psppire_dict_get_variable (dict, weight_index);
271 text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
273 gtk_label_set_text (GTK_LABEL (weight_status_area), text);
281 dump_rm (GtkRecentManager *rm)
283 GList *items = gtk_recent_manager_get_items (rm);
287 g_print ("Recent Items:\n");
288 for (i = items; i; i = i->next)
290 GtkRecentInfo *ri = i->data;
292 g_print ("Item: %s (Mime: %s) (Desc: %s) (URI: %s)\n",
293 gtk_recent_info_get_short_name (ri),
294 gtk_recent_info_get_mime_type (ri),
295 gtk_recent_info_get_description (ri),
296 gtk_recent_info_get_uri (ri)
300 gtk_recent_info_unref (ri);
308 has_suffix (const gchar *name, const gchar *suffix)
310 size_t name_len = strlen (name);
311 size_t suffix_len = strlen (suffix);
312 return (name_len > suffix_len
313 && !c_strcasecmp (&name[name_len - suffix_len], suffix));
317 name_has_por_suffix (const gchar *name)
319 return has_suffix (name, ".por");
323 name_has_sav_suffix (const gchar *name)
325 return has_suffix (name, ".sav") || has_suffix (name, ".zsav");
328 /* Returns true if NAME has a suffix which might denote a PSPP file */
330 name_has_suffix (const gchar *name)
332 return name_has_por_suffix (name) || name_has_sav_suffix (name);
336 load_file (PsppireWindow *de, const gchar *file_name, const char *encoding,
339 const char *mime_type = NULL;
340 gchar *syntax = NULL;
345 gchar *utf8_file_name;
346 struct string filename;
348 utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
350 if (NULL == utf8_file_name)
353 ds_init_empty (&filename);
354 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
356 g_free (utf8_file_name);
358 if (encoding && encoding[0])
359 syntax = g_strdup_printf ("GET FILE=%s ENCODING='%s'.",
360 ds_cstr (&filename), encoding);
362 syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
363 ds_destroy (&filename);
370 ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
371 lex_reader_for_string (syntax, "UTF-8"));
374 if (ok && syn == NULL)
376 if (name_has_por_suffix (file_name))
377 mime_type = "application/x-spss-por";
378 else if (name_has_sav_suffix (file_name))
379 mime_type = "application/x-spss-sav";
381 add_most_recent (file_name, mime_type, encoding);
388 psppire_data_window_format_to_string (enum PsppireDataWindowFormat format)
390 if (format == PSPPIRE_DATA_WINDOW_SAV)
392 else if (format == PSPPIRE_DATA_WINDOW_ZSAV)
398 /* Save DE to file */
400 save_file (PsppireWindow *w)
402 const gchar *file_name = NULL;
403 gchar *utf8_file_name = NULL;
405 struct string filename ;
406 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
409 file_name = psppire_window_get_filename (w);
411 fnx = g_string_new (file_name);
413 if ( ! name_has_suffix (fnx->str))
414 g_string_append (fnx, psppire_data_window_format_to_string (de->format));
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 if (de->format == PSPPIRE_DATA_WINDOW_SAV)
426 syntax = g_strdup_printf ("SAVE OUTFILE=%s.", ds_cstr (&filename));
427 else if (de->format == PSPPIRE_DATA_WINDOW_ZSAV)
428 syntax = g_strdup_printf ("SAVE /ZCOMPRESSED /OUTFILE=%s.",
429 ds_cstr (&filename));
431 syntax = g_strdup_printf ("EXPORT OUTFILE=%s.", ds_cstr (&filename));
433 ds_destroy (&filename);
435 g_free (execute_syntax_string (de, syntax));
440 display_dict (PsppireDataWindow *de)
442 execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
446 sysfile_info (PsppireDataWindow *de)
448 GtkWidget *dialog = psppire_window_file_chooser_dialog (PSPPIRE_WINDOW (de));
450 if ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
452 struct string filename;
454 gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
455 gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
458 const gchar *encoding = psppire_encoding_selector_get_encoding (
459 gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
463 ds_init_empty (&filename);
465 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
467 g_free (utf8_file_name);
470 syntax = g_strdup_printf ("SYSFILE INFO %s ENCODING='%s'.",
471 ds_cstr (&filename), encoding);
473 syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
474 g_free (execute_syntax_string (de, syntax));
477 gtk_widget_destroy (dialog);
481 /* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
483 data_pick_filename (PsppireWindow *window)
485 GtkListStore *list_store;
486 GtkWidget *combo_box;
488 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
489 GtkFileFilter *filter;
491 gtk_file_chooser_dialog_new (_("Save"),
493 GTK_FILE_CHOOSER_ACTION_SAVE,
494 _("Cancel"), GTK_RESPONSE_CANCEL,
495 _("Save"), GTK_RESPONSE_ACCEPT,
498 g_object_set (dialog, "local-only", FALSE, NULL);
500 filter = gtk_file_filter_new ();
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, _("Compressed System Files (*.zsav)"));
507 gtk_file_filter_add_pattern (filter, "*.zsav");
508 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
510 filter = gtk_file_filter_new ();
511 gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
512 gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
513 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
515 filter = gtk_file_filter_new ();
516 gtk_file_filter_set_name (filter, _("All Files"));
517 gtk_file_filter_add_pattern (filter, "*");
518 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
519 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
522 GtkCellRenderer *cell;
527 list_store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
528 combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store));
530 gtk_list_store_append (list_store, &iter);
531 gtk_list_store_set (list_store, &iter,
532 0, PSPPIRE_DATA_WINDOW_SAV,
535 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
537 gtk_list_store_append (list_store, &iter);
538 gtk_list_store_set (list_store, &iter,
539 0, PSPPIRE_DATA_WINDOW_ZSAV,
540 1, _("Compressed System File"),
543 gtk_list_store_append (list_store, &iter);
544 gtk_list_store_set (list_store, &iter,
545 0, PSPPIRE_DATA_WINDOW_POR,
546 1, _("Portable File"),
549 label = gtk_label_new (_("Format:"));
551 cell = gtk_cell_renderer_text_new ();
552 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, FALSE);
553 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box), cell,
556 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
557 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
558 gtk_box_pack_start (GTK_BOX (hbox), combo_box, FALSE, FALSE, 0);
559 gtk_widget_show_all (hbox);
561 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), hbox);
564 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
567 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
569 case GTK_RESPONSE_ACCEPT:
574 gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
580 gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter);
581 gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter,
586 if ( ! name_has_suffix (filename->str))
587 g_string_append (filename,
588 psppire_data_window_format_to_string (format));
590 psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
592 g_string_free (filename, TRUE);
599 gtk_widget_destroy (dialog);
603 confirm_delete_dataset (PsppireDataWindow *de,
604 const char *old_dataset,
605 const char *new_dataset,
606 const char *existing_dataset)
611 dialog = gtk_message_dialog_new (
612 GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
613 _("Delete Existing Dataset?"));
615 gtk_message_dialog_format_secondary_text (
616 GTK_MESSAGE_DIALOG (dialog),
617 _("Renaming \"%s\" to \"%s\" will destroy the existing "
618 "dataset named \"%s\". Are you sure that you want to do this?"),
619 old_dataset, new_dataset, existing_dataset);
621 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
622 _("Cancel"), GTK_RESPONSE_CANCEL,
623 _("Delete"), GTK_RESPONSE_OK,
626 g_object_set (dialog, "icon-name", "pspp", NULL);
628 result = gtk_dialog_run (GTK_DIALOG (dialog));
630 gtk_widget_destroy (dialog);
632 return result == GTK_RESPONSE_OK;
636 on_rename_dataset (PsppireDataWindow *de)
638 struct dataset *ds = de->dataset;
639 struct session *session = dataset_session (ds);
640 const char *old_name = dataset_name (ds);
641 struct dataset *existing_dataset;
645 prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
647 new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
651 if (new_name == NULL)
654 existing_dataset = session_lookup_dataset (session, new_name);
655 if (existing_dataset == NULL || existing_dataset == ds
656 || confirm_delete_dataset (de, old_name, new_name,
657 dataset_name (existing_dataset)))
658 g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
665 status_bar_activate (PsppireDataWindow *de, GtkToggleAction *action)
667 GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
669 if ( gtk_toggle_action_get_active (action))
670 gtk_widget_show (statusbar);
672 gtk_widget_hide (statusbar);
677 grid_lines_activate (PsppireDataWindow *de, GtkToggleAction *action)
679 const gboolean grid_visible = gtk_toggle_action_get_active (action);
681 psppire_data_editor_show_grid (de->data_editor, grid_visible);
685 data_view_activate (PsppireDataWindow *de)
687 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
692 variable_view_activate (PsppireDataWindow *de)
694 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
699 fonts_activate (PsppireDataWindow *de)
701 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
702 GtkWidget *dialog = gtk_font_chooser_dialog_new (NULL, GTK_WINDOW (toplevel));
703 GtkStyle *style = gtk_widget_get_style (GTK_WIDGET(de->data_editor));
704 PangoFontDescription *current_font = style->font_desc;
706 gtk_font_chooser_set_font_desc (GTK_FONT_CHOOSER (dialog), current_font);
708 gtk_window_set_transient_for (GTK_WINDOW (dialog),
709 GTK_WINDOW (toplevel));
711 if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
713 PangoFontDescription* font_desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (dialog));
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.
748 on_recent_data_select (GtkMenuShell *menushell,
749 PsppireWindow *window)
754 gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
756 file = g_filename_from_uri (uri, NULL, NULL);
760 open_data_window (window, file, NULL, NULL);
766 charset_from_mime_type (const char *mime_type)
772 if (mime_type == NULL)
775 charset = c_strcasestr (mime_type, "charset=");
783 /* Parse a "quoted-string" as defined by RFC 822. */
784 for (p++; *p != '\0' && *p != '"'; p++)
787 ds_put_byte (&s, *p);
788 else if (*++p != '\0')
789 ds_put_byte (&s, *p);
794 /* Parse a "token" as defined by RFC 2045. */
795 while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
796 ds_put_byte (&s, *p++);
798 if (!ds_is_empty (&s))
799 return ds_steal_cstr (&s);
806 on_recent_files_select (GtkMenuShell *menushell, gpointer user_data)
813 /* Get the file name and its encoding. */
814 item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
815 file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
816 encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
817 gtk_recent_info_unref (item);
819 se = psppire_syntax_window_new (encoding);
823 if ( psppire_window_load (PSPPIRE_WINDOW (se), file, encoding, NULL) )
824 gtk_widget_show (se);
826 gtk_widget_destroy (se);
832 set_unsaved (gpointer w)
834 psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
838 on_switch_page (PsppireDataEditor *de, gpointer p,
839 gint pagenum, PsppireDataWindow *dw)
841 /* Set the appropriate ui_manager according to the selected page.
842 This is necessary, because the menus for the variable view and
843 the data view are different (slightly). */
845 gboolean is_ds = pagenum == PSPPIRE_DATA_EDITOR_DATA_VIEW;
846 const char *path = (is_ds
847 ? "/ui/menubar/view/view_data"
848 : "/ui/menubar/view/view_variables");
850 GtkWidget *page_menu_item = gtk_ui_manager_get_widget (dw->ui_manager, path);
851 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (page_menu_item), TRUE);
855 on_ui_manager_changed (PsppireDataEditor *de,
856 GParamSpec *pspec UNUSED,
857 PsppireDataWindow *dw)
859 GtkUIManager *uim = psppire_data_editor_get_ui_manager (de);
865 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
866 g_object_unref (dw->uim);
873 g_object_ref (dw->uim);
874 dw->merge_id = psppire_data_window_add_ui (dw, dw->uim);
878 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
879 Returns a pointer to the action
882 connect_action (PsppireDataWindow *dw, const char *action_name,
885 GtkAction *action = get_action_assert (dw->builder, action_name);
887 g_signal_connect_swapped (action, "activate", handler, dw);
892 /* Only a data file with at least one variable can be saved. */
894 enable_save (PsppireDataWindow *dw)
896 gboolean enable = psppire_dict_get_var_cnt (dw->dict) > 0;
898 gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save"),
900 gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save_as"),
904 /* Initializes as much of a PsppireDataWindow as we can and must before the
905 dataset has been set.
907 In particular, the 'menu' member is required in case the "filename" property
908 is set before the "dataset" property: otherwise PsppireWindow will try to
909 modify the menu as part of the "filename" property_set() function and end up
910 with a Gtk-CRITICAL since 'menu' is NULL. */
912 psppire_data_window_init (PsppireDataWindow *de)
915 de->builder = builder_new ("data-editor.ui");
917 de->ui_manager = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
919 w = gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/windows/windows_minimise_all");
921 PSPPIRE_WINDOW (de)->menu = GTK_MENU_SHELL (gtk_widget_get_parent (w));
928 file_import (PsppireDataWindow *dw)
930 GtkWidget *w = psppire_import_assistant_new (GTK_WINDOW (dw));
931 PsppireImportAssistant *asst = PSPPIRE_IMPORT_ASSISTANT (w);
932 gtk_widget_show_all (w);
934 asst->main_loop = g_main_loop_new (NULL, TRUE);
935 g_main_loop_run (asst->main_loop);
936 g_main_loop_unref (asst->main_loop);
938 if (!asst->file_name)
941 switch (asst->response)
943 case GTK_RESPONSE_APPLY:
946 gchar *fn = g_path_get_basename (asst->file_name);
947 open_data_window (PSPPIRE_WINDOW (dw), fn, NULL, psppire_import_assistant_generate_syntax (asst));
951 case PSPPIRE_RESPONSE_PASTE:
953 free (paste_syntax_to_window (psppire_import_assistant_generate_syntax (asst)));
960 gtk_widget_destroy (GTK_WIDGET (asst));
964 psppire_data_window_finish_init (PsppireDataWindow *de,
967 static const struct dataset_callbacks cbs =
969 set_unsaved, /* changed */
970 transformation_change_callback, /* transformations_changed */
977 GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
980 de->dict = psppire_dict_new_from_dict (dataset_dict (ds));
981 de->data_store = psppire_data_store_new (de->dict);
982 psppire_data_store_set_reader (de->data_store, NULL);
984 menubar = get_widget_assert (de->builder, "menubar");
985 hb = get_widget_assert (de->builder, "toolbar");
986 sb = get_widget_assert (de->builder, "status-bar");
992 PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store));
993 g_signal_connect (de->data_editor, "switch-page",
994 G_CALLBACK (on_switch_page), de);
996 g_signal_connect_swapped (de->data_store, "case-changed",
997 G_CALLBACK (set_unsaved), de);
999 g_signal_connect_swapped (de->data_store, "case-inserted",
1000 G_CALLBACK (set_unsaved), de);
1002 g_signal_connect_swapped (de->data_store, "cases-deleted",
1003 G_CALLBACK (set_unsaved), de);
1005 dataset_set_callbacks (de->dataset, &cbs, de);
1007 connect_help (de->builder);
1009 gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
1010 gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
1011 gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
1012 gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
1014 gtk_container_add (GTK_CONTAINER (de), box);
1016 g_signal_connect (de->dict, "weight-changed",
1017 G_CALLBACK (on_weight_change),
1020 g_signal_connect (de->dict, "filter-changed",
1021 G_CALLBACK (on_filter_change),
1024 g_signal_connect (de->dict, "split-changed",
1025 G_CALLBACK (on_split_change),
1028 g_signal_connect_swapped (de->dict, "backend-changed",
1029 G_CALLBACK (enable_save), de);
1030 g_signal_connect_swapped (de->dict, "variable-inserted",
1031 G_CALLBACK (enable_save), de);
1032 g_signal_connect_swapped (de->dict, "variable-deleted",
1033 G_CALLBACK (enable_save), de);
1036 connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1037 connect_action (de, "file_import", G_CALLBACK (file_import));
1039 connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1040 connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1041 connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1042 connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1043 connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1044 connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1046 g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1048 connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1049 connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1050 connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1051 connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1052 connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1053 connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1056 GtkWidget *recent_data =
1057 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-data");
1059 GtkWidget *recent_files =
1060 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-files");
1063 GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1064 gtk_recent_manager_get_default ());
1066 GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1067 gtk_recent_manager_get_default ());
1069 g_object_set (menu_data, "show-tips", TRUE, NULL);
1070 g_object_set (menu_files, "show-tips", TRUE, NULL);
1073 GtkRecentFilter *filter = gtk_recent_filter_new ();
1075 gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1076 gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1078 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1080 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1083 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1086 g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1089 GtkRecentFilter *filter = gtk_recent_filter_new ();
1091 gtk_recent_filter_add_pattern (filter, "*.sps");
1092 gtk_recent_filter_add_pattern (filter, "*.SPS");
1094 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1096 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1099 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1101 g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1105 connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1108 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1109 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1111 connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1113 connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1115 connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1117 connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1119 connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1121 connect_action (de, "file_quit", G_CALLBACK (file_quit));
1123 connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1125 connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1127 g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1129 gtk_menu_shell_append (GTK_MENU_SHELL (menubar), create_help_menu (GTK_WINDOW (de)));
1131 g_signal_connect (de->data_editor, "notify::ui-manager",
1132 G_CALLBACK (on_ui_manager_changed), de);
1133 on_ui_manager_changed (de->data_editor, NULL, de);
1135 gtk_widget_show (GTK_WIDGET (de->data_editor));
1136 gtk_widget_show (box);
1138 ll_push_head (&all_data_windows, &de->ll);
1142 psppire_data_window_dispose (GObject *object)
1144 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1148 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
1149 g_object_unref (dw->uim);
1153 if (dw->builder != NULL)
1155 g_object_unref (dw->builder);
1161 g_signal_handlers_disconnect_by_func (dw->dict,
1162 G_CALLBACK (enable_save), dw);
1163 g_signal_handlers_disconnect_by_func (dw->dict,
1164 G_CALLBACK (on_weight_change), dw);
1165 g_signal_handlers_disconnect_by_func (dw->dict,
1166 G_CALLBACK (on_filter_change), dw);
1167 g_signal_handlers_disconnect_by_func (dw->dict,
1168 G_CALLBACK (on_split_change), dw);
1170 g_object_unref (dw->dict);
1176 g_object_unref (dw->data_store);
1177 dw->data_store = NULL;
1180 if (dw->ll.next != NULL)
1182 ll_remove (&dw->ll);
1186 if (G_OBJECT_CLASS (parent_class)->dispose)
1187 G_OBJECT_CLASS (parent_class)->dispose (object);
1191 psppire_data_window_finalize (GObject *object)
1193 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1197 struct dataset *dataset = dw->dataset;
1198 struct session *session = dataset_session (dataset);
1202 dataset_set_callbacks (dataset, NULL, NULL);
1203 session_set_active_dataset (session, NULL);
1204 dataset_destroy (dataset);
1207 if (G_OBJECT_CLASS (parent_class)->finalize)
1208 G_OBJECT_CLASS (parent_class)->finalize (object);
1212 psppire_data_window_set_property (GObject *object,
1214 const GValue *value,
1217 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1222 psppire_data_window_finish_init (window, g_value_get_pointer (value));
1225 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1231 psppire_data_window_get_property (GObject *object,
1236 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1241 g_value_set_pointer (value, window->dataset);
1244 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1250 psppire_data_window_add_ui (PsppireDataWindow *pdw, GtkUIManager *uim)
1256 ui_string = gtk_ui_manager_get_ui (uim);
1257 merge_id = gtk_ui_manager_add_ui_from_string (pdw->ui_manager, ui_string,
1261 g_return_val_if_fail (merge_id != 0, 0);
1263 list = gtk_ui_manager_get_action_groups (uim);
1264 for (; list != NULL; list = list->next)
1266 GtkActionGroup *action_group = list->data;
1267 GList *actions = gtk_action_group_list_actions (action_group);
1270 for (action = actions; action != NULL; action = action->next)
1272 GtkAction *a = action->data;
1274 if (PSPPIRE_IS_DIALOG_ACTION (a))
1275 g_object_set (a, "manager", pdw->ui_manager, NULL);
1278 gtk_ui_manager_insert_action_group (pdw->ui_manager, action_group, 0);
1281 gtk_window_add_accel_group (GTK_WINDOW (pdw),
1282 gtk_ui_manager_get_accel_group (uim));
1288 psppire_data_window_remove_ui (PsppireDataWindow *pdw,
1289 GtkUIManager *uim, guint merge_id)
1293 g_return_if_fail (merge_id != 0);
1295 gtk_ui_manager_remove_ui (pdw->ui_manager, merge_id);
1297 list = gtk_ui_manager_get_action_groups (uim);
1298 for (; list != NULL; list = list->next)
1300 GtkActionGroup *action_group = list->data;
1301 gtk_ui_manager_remove_action_group (pdw->ui_manager, action_group);
1304 gtk_window_remove_accel_group (GTK_WINDOW (pdw),
1305 gtk_ui_manager_get_accel_group (uim));
1309 psppire_data_window_new (struct dataset *ds)
1313 if (the_session == NULL)
1314 the_session = session_create (NULL);
1318 char *dataset_name = session_generate_dataset_name (the_session);
1319 ds = dataset_create (the_session, dataset_name);
1320 free (dataset_name);
1322 assert (dataset_session (ds) == the_session);
1326 psppire_data_window_get_type (),
1327 "description", _("Data Editor"),
1331 if (dataset_name (ds) != NULL)
1332 g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1338 psppire_data_window_is_empty (PsppireDataWindow *dw)
1340 return psppire_dict_get_var_cnt (dw->dict) == 0;
1344 psppire_data_window_iface_init (PsppireWindowIface *iface)
1346 iface->save = save_file;
1347 iface->pick_filename = data_pick_filename;
1348 iface->load = load_file;
1352 psppire_default_data_window (void)
1354 if (ll_is_empty (&all_data_windows))
1355 create_data_window ();
1356 return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1360 psppire_data_window_set_default (PsppireDataWindow *pdw)
1362 ll_remove (&pdw->ll);
1363 ll_push_head (&all_data_windows, &pdw->ll);
1367 psppire_data_window_undefault (PsppireDataWindow *pdw)
1369 ll_remove (&pdw->ll);
1370 ll_push_tail (&all_data_windows, &pdw->ll);
1374 psppire_data_window_for_dataset (struct dataset *ds)
1376 PsppireDataWindow *pdw;
1378 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1379 if (pdw->dataset == ds)
1386 psppire_data_window_for_data_store (PsppireDataStore *data_store)
1388 PsppireDataWindow *pdw;
1390 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1391 if (pdw->data_store == data_store)
1398 create_data_window (void)
1400 gtk_widget_show (psppire_data_window_new (NULL));
1404 open_data_window (PsppireWindow *victim, const char *file_name,
1405 const char *encoding, gpointer hint)
1409 if (PSPPIRE_IS_DATA_WINDOW (victim)
1410 && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1412 window = GTK_WIDGET (victim);
1413 gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
1416 window = psppire_data_window_new (NULL);
1418 psppire_window_load (PSPPIRE_WINDOW (window), file_name, encoding, hint);
1419 gtk_widget_show_all (window);