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/entry-dialog.h"
29 #include "ui/gui/executor.h"
30 #include "ui/gui/help-menu.h"
31 #include "ui/gui/helper.h"
32 #include "ui/gui/helper.h"
33 #include "ui/gui/psppire-import-assistant.h"
34 #include "ui/gui/psppire-data-window.h"
35 #include "ui/gui/psppire-dialog-action.h"
36 #include "ui/gui/psppire-encoding-selector.h"
37 #include "ui/gui/psppire-syntax-window.h"
38 #include "ui/gui/psppire-window.h"
39 #include "ui/gui/psppire.h"
40 #include "ui/gui/recode-dialog.h"
41 #include "ui/gui/select-cases-dialog.h"
42 #include "ui/gui/split-file-dialog.h"
43 #include "ui/syntax-gen.h"
45 #include "gl/c-strcase.h"
46 #include "gl/c-strcasestr.h"
47 #include "gl/xvasprintf.h"
50 #define _(msgid) gettext (msgid)
51 #define N_(msgid) msgid
53 struct session *the_session;
54 struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
56 static void psppire_data_window_class_init (PsppireDataWindowClass *class);
57 static void psppire_data_window_init (PsppireDataWindow *data_editor);
60 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
62 static void psppire_data_window_dispose (GObject *object);
63 static void psppire_data_window_finalize (GObject *object);
64 static void psppire_data_window_set_property (GObject *object,
68 static void psppire_data_window_get_property (GObject *object,
73 static guint psppire_data_window_add_ui (PsppireDataWindow *, GtkUIManager *);
74 static void psppire_data_window_remove_ui (PsppireDataWindow *,
75 GtkUIManager *, guint);
78 psppire_data_window_get_type (void)
80 static GType psppire_data_window_type = 0;
82 if (!psppire_data_window_type)
84 static const GTypeInfo psppire_data_window_info =
86 sizeof (PsppireDataWindowClass),
89 (GClassInitFunc)psppire_data_window_class_init,
90 (GClassFinalizeFunc) NULL,
92 sizeof (PsppireDataWindow),
94 (GInstanceInitFunc) psppire_data_window_init,
97 static const GInterfaceInfo window_interface_info =
99 (GInterfaceInitFunc) psppire_data_window_iface_init,
104 psppire_data_window_type =
105 g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
106 &psppire_data_window_info, 0);
109 g_type_add_interface_static (psppire_data_window_type,
110 PSPPIRE_TYPE_WINDOW_MODEL,
111 &window_interface_info);
114 return psppire_data_window_type;
117 static GObjectClass *parent_class ;
124 psppire_data_window_class_init (PsppireDataWindowClass *class)
126 GObjectClass *object_class = G_OBJECT_CLASS (class);
128 parent_class = g_type_class_peek_parent (class);
130 object_class->dispose = psppire_data_window_dispose;
131 object_class->finalize = psppire_data_window_finalize;
132 object_class->set_property = psppire_data_window_set_property;
133 object_class->get_property = psppire_data_window_get_property;
135 g_object_class_install_property (
136 object_class, PROP_DATASET,
137 g_param_spec_pointer ("dataset", "Dataset",
138 "'struct datset *' represented by the window",
139 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
142 /* Run the EXECUTE command. */
144 execute (PsppireDataWindow *dw)
146 execute_const_syntax_string (dw, "EXECUTE.");
150 transformation_change_callback (bool transformations_pending,
153 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
155 GtkWidget *status_label =
156 get_widget_assert (de->builder, "case-counter-area");
158 { /* Set the sensitivity of the "Transformations Pending" menuitem */
160 GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
162 GtkWidget *menuitem =
163 gtk_ui_manager_get_widget (uim,"/ui/menubar/transform/transform_run-pending");
165 gtk_widget_set_sensitive (menuitem, transformations_pending);
169 if ( transformations_pending)
170 gtk_label_set_text (GTK_LABEL (status_label),
171 _("Transformations Pending"));
173 gtk_label_set_text (GTK_LABEL (status_label), "");
176 /* Callback for when the dictionary changes its filter variable */
178 on_filter_change (GObject *o, gint filter_index, gpointer data)
180 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
182 GtkWidget *filter_status_area =
183 get_widget_assert (de->builder, "filter-use-status-area");
185 if ( filter_index == -1 )
187 gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
191 PsppireDict *dict = NULL;
192 struct variable *var ;
195 g_object_get (de->data_editor, "dictionary", &dict, NULL);
197 var = psppire_dict_get_variable (dict, filter_index);
199 text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
201 gtk_label_set_text (GTK_LABEL (filter_status_area), text);
207 /* Callback for when the dictionary changes its split variables */
209 on_split_change (PsppireDict *dict, gpointer data)
211 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
213 size_t n_split_vars = dict_get_split_cnt (dict->dict);
215 GtkWidget *split_status_area =
216 get_widget_assert (de->builder, "split-file-status-area");
218 if ( n_split_vars == 0 )
220 gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
226 const struct variable *const * split_vars =
227 dict_get_split_vars (dict->dict);
229 text = g_string_new (_("Split by "));
231 for (i = 0 ; i < n_split_vars - 1; ++i )
233 g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
235 g_string_append (text, var_get_name (split_vars[i]));
237 gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
239 g_string_free (text, TRUE);
246 /* Callback for when the dictionary changes its weights */
248 on_weight_change (GObject *o, gint weight_index, gpointer data)
250 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
252 GtkWidget *weight_status_area =
253 get_widget_assert (de->builder, "weight-status-area");
255 if ( weight_index == -1 )
257 gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
261 struct variable *var ;
262 PsppireDict *dict = NULL;
265 g_object_get (de->data_editor, "dictionary", &dict, NULL);
267 var = psppire_dict_get_variable (dict, weight_index);
269 text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
271 gtk_label_set_text (GTK_LABEL (weight_status_area), text);
279 dump_rm (GtkRecentManager *rm)
281 GList *items = gtk_recent_manager_get_items (rm);
285 g_print ("Recent Items:\n");
286 for (i = items; i; i = i->next)
288 GtkRecentInfo *ri = i->data;
290 g_print ("Item: %s (Mime: %s) (Desc: %s) (URI: %s)\n",
291 gtk_recent_info_get_short_name (ri),
292 gtk_recent_info_get_mime_type (ri),
293 gtk_recent_info_get_description (ri),
294 gtk_recent_info_get_uri (ri)
298 gtk_recent_info_unref (ri);
306 has_suffix (const gchar *name, const gchar *suffix)
308 size_t name_len = strlen (name);
309 size_t suffix_len = strlen (suffix);
310 return (name_len > suffix_len
311 && !c_strcasecmp (&name[name_len - suffix_len], suffix));
315 name_has_por_suffix (const gchar *name)
317 return has_suffix (name, ".por");
321 name_has_sav_suffix (const gchar *name)
323 return has_suffix (name, ".sav") || has_suffix (name, ".zsav");
326 /* Returns true if NAME has a suffix which might denote a PSPP file */
328 name_has_suffix (const gchar *name)
330 return name_has_por_suffix (name) || name_has_sav_suffix (name);
334 load_file (PsppireWindow *de, const gchar *file_name, const char *encoding,
337 const char *mime_type = NULL;
338 gchar *syntax = NULL;
343 gchar *utf8_file_name;
344 struct string filename;
346 utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
348 if (NULL == utf8_file_name)
351 ds_init_empty (&filename);
352 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
354 g_free (utf8_file_name);
356 if (encoding && encoding[0])
357 syntax = g_strdup_printf ("GET FILE=%s ENCODING='%s'.",
358 ds_cstr (&filename), encoding);
360 syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
361 ds_destroy (&filename);
368 ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
369 lex_reader_for_string (syntax, "UTF-8"));
372 if (ok && syn == NULL)
374 if (name_has_por_suffix (file_name))
375 mime_type = "application/x-spss-por";
376 else if (name_has_sav_suffix (file_name))
377 mime_type = "application/x-spss-sav";
379 add_most_recent (file_name, mime_type, encoding);
386 psppire_data_window_format_to_string (enum PsppireDataWindowFormat format)
388 if (format == PSPPIRE_DATA_WINDOW_SAV)
390 else if (format == PSPPIRE_DATA_WINDOW_ZSAV)
396 /* Save DE to file */
398 save_file (PsppireWindow *w)
400 const gchar *file_name = NULL;
401 gchar *utf8_file_name = NULL;
403 struct string filename ;
404 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
407 file_name = psppire_window_get_filename (w);
409 fnx = g_string_new (file_name);
411 if ( ! name_has_suffix (fnx->str))
412 g_string_append (fnx, psppire_data_window_format_to_string (de->format));
414 ds_init_empty (&filename);
416 utf8_file_name = g_filename_to_utf8 (fnx->str, -1, NULL, NULL, NULL);
418 g_string_free (fnx, TRUE);
420 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
421 g_free (utf8_file_name);
423 if (de->format == PSPPIRE_DATA_WINDOW_SAV)
424 syntax = g_strdup_printf ("SAVE OUTFILE=%s.", ds_cstr (&filename));
425 else if (de->format == PSPPIRE_DATA_WINDOW_ZSAV)
426 syntax = g_strdup_printf ("SAVE /ZCOMPRESSED /OUTFILE=%s.",
427 ds_cstr (&filename));
429 syntax = g_strdup_printf ("EXPORT OUTFILE=%s.", ds_cstr (&filename));
431 ds_destroy (&filename);
433 g_free (execute_syntax_string (de, syntax));
438 display_dict (PsppireDataWindow *de)
440 execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
444 sysfile_info (PsppireDataWindow *de)
446 GtkWidget *dialog = psppire_window_file_chooser_dialog (PSPPIRE_WINDOW (de));
448 if ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
450 struct string filename;
452 gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
453 gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
456 const gchar *encoding = psppire_encoding_selector_get_encoding (
457 gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
461 ds_init_empty (&filename);
463 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
465 g_free (utf8_file_name);
468 syntax = g_strdup_printf ("SYSFILE INFO %s ENCODING='%s'.",
469 ds_cstr (&filename), encoding);
471 syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
472 g_free (execute_syntax_string (de, syntax));
475 gtk_widget_destroy (dialog);
479 /* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
481 data_pick_filename (PsppireWindow *window)
483 GtkListStore *list_store;
484 GtkWidget *combo_box;
486 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
487 GtkFileFilter *filter;
489 gtk_file_chooser_dialog_new (_("Save"),
491 GTK_FILE_CHOOSER_ACTION_SAVE,
492 _("Cancel"), GTK_RESPONSE_CANCEL,
493 _("Save"), GTK_RESPONSE_ACCEPT,
496 g_object_set (dialog, "local-only", FALSE, NULL);
498 filter = gtk_file_filter_new ();
499 gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
500 gtk_file_filter_add_mime_type (filter, "application/x-spss-sav");
501 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
503 filter = gtk_file_filter_new ();
504 gtk_file_filter_set_name (filter, _("Compressed System Files (*.zsav)"));
505 gtk_file_filter_add_pattern (filter, "*.zsav");
506 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
508 filter = gtk_file_filter_new ();
509 gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
510 gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
511 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
513 filter = gtk_file_filter_new ();
514 gtk_file_filter_set_name (filter, _("All Files"));
515 gtk_file_filter_add_pattern (filter, "*");
516 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
517 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
520 GtkCellRenderer *cell;
525 list_store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
526 combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store));
528 gtk_list_store_append (list_store, &iter);
529 gtk_list_store_set (list_store, &iter,
530 0, PSPPIRE_DATA_WINDOW_SAV,
533 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
535 gtk_list_store_append (list_store, &iter);
536 gtk_list_store_set (list_store, &iter,
537 0, PSPPIRE_DATA_WINDOW_ZSAV,
538 1, _("Compressed System File"),
541 gtk_list_store_append (list_store, &iter);
542 gtk_list_store_set (list_store, &iter,
543 0, PSPPIRE_DATA_WINDOW_POR,
544 1, _("Portable File"),
547 label = gtk_label_new (_("Format:"));
549 cell = gtk_cell_renderer_text_new ();
550 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, FALSE);
551 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box), cell,
554 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
555 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
556 gtk_box_pack_start (GTK_BOX (hbox), combo_box, FALSE, FALSE, 0);
557 gtk_widget_show_all (hbox);
559 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), hbox);
562 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
565 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
567 case GTK_RESPONSE_ACCEPT:
572 gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
578 gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter);
579 gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter,
584 if ( ! name_has_suffix (filename->str))
585 g_string_append (filename,
586 psppire_data_window_format_to_string (format));
588 psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
590 g_string_free (filename, TRUE);
597 gtk_widget_destroy (dialog);
601 confirm_delete_dataset (PsppireDataWindow *de,
602 const char *old_dataset,
603 const char *new_dataset,
604 const char *existing_dataset)
609 dialog = gtk_message_dialog_new (
610 GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
611 _("Delete Existing Dataset?"));
613 gtk_message_dialog_format_secondary_text (
614 GTK_MESSAGE_DIALOG (dialog),
615 _("Renaming \"%s\" to \"%s\" will destroy the existing "
616 "dataset named \"%s\". Are you sure that you want to do this?"),
617 old_dataset, new_dataset, existing_dataset);
619 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
620 _("Cancel"), GTK_RESPONSE_CANCEL,
621 _("Delete"), GTK_RESPONSE_OK,
624 g_object_set (dialog, "icon-name", "pspp", NULL);
626 result = gtk_dialog_run (GTK_DIALOG (dialog));
628 gtk_widget_destroy (dialog);
630 return result == GTK_RESPONSE_OK;
634 on_rename_dataset (PsppireDataWindow *de)
636 struct dataset *ds = de->dataset;
637 struct session *session = dataset_session (ds);
638 const char *old_name = dataset_name (ds);
639 struct dataset *existing_dataset;
643 prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
645 new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
649 if (new_name == NULL)
652 existing_dataset = session_lookup_dataset (session, new_name);
653 if (existing_dataset == NULL || existing_dataset == ds
654 || confirm_delete_dataset (de, old_name, new_name,
655 dataset_name (existing_dataset)))
656 g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
663 status_bar_activate (PsppireDataWindow *de, GtkToggleAction *action)
665 GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
667 if ( gtk_toggle_action_get_active (action))
668 gtk_widget_show (statusbar);
670 gtk_widget_hide (statusbar);
675 grid_lines_activate (PsppireDataWindow *de, GtkToggleAction *action)
677 const gboolean grid_visible = gtk_toggle_action_get_active (action);
679 psppire_data_editor_show_grid (de->data_editor, grid_visible);
683 data_view_activate (PsppireDataWindow *de)
685 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
690 variable_view_activate (PsppireDataWindow *de)
692 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
697 fonts_activate (PsppireDataWindow *de)
699 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
700 GtkWidget *dialog = gtk_font_chooser_dialog_new (NULL, GTK_WINDOW (toplevel));
701 GtkStyle *style = gtk_widget_get_style (GTK_WIDGET(de->data_editor));
702 PangoFontDescription *current_font = style->font_desc;
704 gtk_font_chooser_set_font_desc (GTK_FONT_CHOOSER (dialog), current_font);
706 gtk_window_set_transient_for (GTK_WINDOW (dialog),
707 GTK_WINDOW (toplevel));
709 if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
711 PangoFontDescription* font_desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (dialog));
713 psppire_data_editor_set_font (de->data_editor, font_desc);
716 gtk_widget_hide (dialog);
721 /* Callback for the value labels action */
723 toggle_value_labels (PsppireDataWindow *de, GtkToggleAction *ta)
725 g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
729 toggle_split_window (PsppireDataWindow *de, GtkToggleAction *ta)
731 psppire_data_editor_split_window (de->data_editor,
732 gtk_toggle_action_get_active (ta));
737 file_quit (PsppireDataWindow *de)
739 /* FIXME: Need to be more intelligent here.
740 Give the user the opportunity to save any unsaved data.
746 on_recent_data_select (GtkMenuShell *menushell,
747 PsppireWindow *window)
752 gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
754 file = g_filename_from_uri (uri, NULL, NULL);
758 open_data_window (window, file, NULL, NULL);
764 charset_from_mime_type (const char *mime_type)
770 if (mime_type == NULL)
773 charset = c_strcasestr (mime_type, "charset=");
781 /* Parse a "quoted-string" as defined by RFC 822. */
782 for (p++; *p != '\0' && *p != '"'; p++)
785 ds_put_byte (&s, *p);
786 else if (*++p != '\0')
787 ds_put_byte (&s, *p);
792 /* Parse a "token" as defined by RFC 2045. */
793 while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
794 ds_put_byte (&s, *p++);
796 if (!ds_is_empty (&s))
797 return ds_steal_cstr (&s);
804 on_recent_files_select (GtkMenuShell *menushell, gpointer user_data)
811 /* Get the file name and its encoding. */
812 item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
813 file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
814 encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
815 gtk_recent_info_unref (item);
817 se = psppire_syntax_window_new (encoding);
821 if ( psppire_window_load (PSPPIRE_WINDOW (se), file, encoding, NULL) )
822 gtk_widget_show (se);
824 gtk_widget_destroy (se);
830 set_unsaved (gpointer w)
832 psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
836 on_switch_page (PsppireDataEditor *de, gpointer p,
837 gint pagenum, PsppireDataWindow *dw)
839 /* Set the appropriate ui_manager according to the selected page.
840 This is necessary, because the menus for the variable view and
841 the data view are different (slightly). */
843 gboolean is_ds = pagenum == PSPPIRE_DATA_EDITOR_DATA_VIEW;
844 const char *path = (is_ds
845 ? "/ui/menubar/view/view_data"
846 : "/ui/menubar/view/view_variables");
848 GtkWidget *page_menu_item = gtk_ui_manager_get_widget (dw->ui_manager, path);
849 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (page_menu_item), TRUE);
853 on_ui_manager_changed (PsppireDataEditor *de,
854 GParamSpec *pspec UNUSED,
855 PsppireDataWindow *dw)
857 GtkUIManager *uim = psppire_data_editor_get_ui_manager (de);
863 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
864 g_object_unref (dw->uim);
871 g_object_ref (dw->uim);
872 dw->merge_id = psppire_data_window_add_ui (dw, dw->uim);
876 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
877 Returns a pointer to the action
880 connect_action (PsppireDataWindow *dw, const char *action_name,
883 GtkAction *action = get_action_assert (dw->builder, action_name);
885 g_signal_connect_swapped (action, "activate", handler, dw);
890 /* Only a data file with at least one variable can be saved. */
892 enable_save (PsppireDataWindow *dw)
894 gboolean enable = psppire_dict_get_var_cnt (dw->dict) > 0;
896 gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save"),
898 gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save_as"),
902 /* Initializes as much of a PsppireDataWindow as we can and must before the
903 dataset has been set.
905 In particular, the 'menu' member is required in case the "filename" property
906 is set before the "dataset" property: otherwise PsppireWindow will try to
907 modify the menu as part of the "filename" property_set() function and end up
908 with a Gtk-CRITICAL since 'menu' is NULL. */
910 psppire_data_window_init (PsppireDataWindow *de)
913 de->builder = builder_new ("data-editor.ui");
915 de->ui_manager = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
917 w = gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/windows/windows_minimise_all");
919 PSPPIRE_WINDOW (de)->menu = GTK_MENU_SHELL (gtk_widget_get_parent (w));
926 file_import (PsppireDataWindow *dw)
928 GtkWidget *w = psppire_import_assistant_new (GTK_WINDOW (dw));
929 PsppireImportAssistant *asst = PSPPIRE_IMPORT_ASSISTANT (w);
930 gtk_widget_show_all (w);
932 asst->main_loop = g_main_loop_new (NULL, TRUE);
933 g_main_loop_run (asst->main_loop);
934 g_main_loop_unref (asst->main_loop);
936 if (!asst->file_name)
939 switch (asst->response)
941 case GTK_RESPONSE_APPLY:
944 gchar *fn = g_path_get_basename (asst->file_name);
945 open_data_window (PSPPIRE_WINDOW (dw), fn, NULL, psppire_import_assistant_generate_syntax (asst));
949 case PSPPIRE_RESPONSE_PASTE:
951 free (paste_syntax_to_window (psppire_import_assistant_generate_syntax (asst)));
958 gtk_widget_destroy (GTK_WIDGET (asst));
962 psppire_data_window_finish_init (PsppireDataWindow *de,
965 static const struct dataset_callbacks cbs =
967 set_unsaved, /* changed */
968 transformation_change_callback, /* transformations_changed */
975 GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
978 de->dict = psppire_dict_new_from_dict (dataset_dict (ds));
979 de->data_store = psppire_data_store_new (de->dict);
980 psppire_data_store_set_reader (de->data_store, NULL);
982 menubar = get_widget_assert (de->builder, "menubar");
983 hb = get_widget_assert (de->builder, "toolbar");
984 sb = get_widget_assert (de->builder, "status-bar");
990 PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store));
991 g_signal_connect (de->data_editor, "switch-page",
992 G_CALLBACK (on_switch_page), de);
994 g_signal_connect_swapped (de->data_store, "case-changed",
995 G_CALLBACK (set_unsaved), de);
997 g_signal_connect_swapped (de->data_store, "case-inserted",
998 G_CALLBACK (set_unsaved), de);
1000 g_signal_connect_swapped (de->data_store, "cases-deleted",
1001 G_CALLBACK (set_unsaved), de);
1003 dataset_set_callbacks (de->dataset, &cbs, de);
1005 connect_help (de->builder);
1007 gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
1008 gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
1009 gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
1010 gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
1012 gtk_container_add (GTK_CONTAINER (de), box);
1014 g_signal_connect (de->dict, "weight-changed",
1015 G_CALLBACK (on_weight_change),
1018 g_signal_connect (de->dict, "filter-changed",
1019 G_CALLBACK (on_filter_change),
1022 g_signal_connect (de->dict, "split-changed",
1023 G_CALLBACK (on_split_change),
1026 g_signal_connect_swapped (de->dict, "backend-changed",
1027 G_CALLBACK (enable_save), de);
1028 g_signal_connect_swapped (de->dict, "variable-inserted",
1029 G_CALLBACK (enable_save), de);
1030 g_signal_connect_swapped (de->dict, "variable-deleted",
1031 G_CALLBACK (enable_save), de);
1034 connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1035 connect_action (de, "file_import", G_CALLBACK (file_import));
1037 connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1038 connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1039 connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1040 connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1041 connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1042 connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1044 g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1046 connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1047 connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1048 connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1049 connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1052 GtkWidget *recent_data =
1053 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-data");
1055 GtkWidget *recent_files =
1056 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-files");
1059 GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1060 gtk_recent_manager_get_default ());
1062 GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1063 gtk_recent_manager_get_default ());
1065 g_object_set (menu_data, "show-tips", TRUE, NULL);
1066 g_object_set (menu_files, "show-tips", TRUE, NULL);
1069 GtkRecentFilter *filter = gtk_recent_filter_new ();
1071 gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1072 gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1074 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1076 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1079 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1082 g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1085 GtkRecentFilter *filter = gtk_recent_filter_new ();
1087 gtk_recent_filter_add_pattern (filter, "*.sps");
1088 gtk_recent_filter_add_pattern (filter, "*.SPS");
1090 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1092 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1095 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1097 g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1101 connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1104 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1105 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1107 connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1109 connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1111 connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1113 connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1115 connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1117 connect_action (de, "file_quit", G_CALLBACK (file_quit));
1119 connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1121 connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1123 g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1125 gtk_menu_shell_append (GTK_MENU_SHELL (menubar), create_help_menu (GTK_WINDOW (de)));
1127 g_signal_connect (de->data_editor, "notify::ui-manager",
1128 G_CALLBACK (on_ui_manager_changed), de);
1129 on_ui_manager_changed (de->data_editor, NULL, de);
1131 gtk_widget_show (GTK_WIDGET (de->data_editor));
1132 gtk_widget_show (box);
1134 ll_push_head (&all_data_windows, &de->ll);
1138 psppire_data_window_dispose (GObject *object)
1140 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1144 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
1145 g_object_unref (dw->uim);
1149 if (dw->builder != NULL)
1151 g_object_unref (dw->builder);
1157 g_signal_handlers_disconnect_by_func (dw->dict,
1158 G_CALLBACK (enable_save), dw);
1159 g_signal_handlers_disconnect_by_func (dw->dict,
1160 G_CALLBACK (on_weight_change), dw);
1161 g_signal_handlers_disconnect_by_func (dw->dict,
1162 G_CALLBACK (on_filter_change), dw);
1163 g_signal_handlers_disconnect_by_func (dw->dict,
1164 G_CALLBACK (on_split_change), dw);
1166 g_object_unref (dw->dict);
1172 g_object_unref (dw->data_store);
1173 dw->data_store = NULL;
1176 if (dw->ll.next != NULL)
1178 ll_remove (&dw->ll);
1182 if (G_OBJECT_CLASS (parent_class)->dispose)
1183 G_OBJECT_CLASS (parent_class)->dispose (object);
1187 psppire_data_window_finalize (GObject *object)
1189 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1193 struct dataset *dataset = dw->dataset;
1194 struct session *session = dataset_session (dataset);
1198 dataset_set_callbacks (dataset, NULL, NULL);
1199 session_set_active_dataset (session, NULL);
1200 dataset_destroy (dataset);
1203 if (G_OBJECT_CLASS (parent_class)->finalize)
1204 G_OBJECT_CLASS (parent_class)->finalize (object);
1208 psppire_data_window_set_property (GObject *object,
1210 const GValue *value,
1213 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1218 psppire_data_window_finish_init (window, g_value_get_pointer (value));
1221 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1227 psppire_data_window_get_property (GObject *object,
1232 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1237 g_value_set_pointer (value, window->dataset);
1240 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1246 psppire_data_window_add_ui (PsppireDataWindow *pdw, GtkUIManager *uim)
1252 ui_string = gtk_ui_manager_get_ui (uim);
1253 merge_id = gtk_ui_manager_add_ui_from_string (pdw->ui_manager, ui_string,
1257 g_return_val_if_fail (merge_id != 0, 0);
1259 list = gtk_ui_manager_get_action_groups (uim);
1260 for (; list != NULL; list = list->next)
1262 GtkActionGroup *action_group = list->data;
1263 GList *actions = gtk_action_group_list_actions (action_group);
1266 for (action = actions; action != NULL; action = action->next)
1268 GtkAction *a = action->data;
1270 if (PSPPIRE_IS_DIALOG_ACTION (a))
1271 g_object_set (a, "manager", pdw->ui_manager, NULL);
1274 gtk_ui_manager_insert_action_group (pdw->ui_manager, action_group, 0);
1277 gtk_window_add_accel_group (GTK_WINDOW (pdw),
1278 gtk_ui_manager_get_accel_group (uim));
1284 psppire_data_window_remove_ui (PsppireDataWindow *pdw,
1285 GtkUIManager *uim, guint merge_id)
1289 g_return_if_fail (merge_id != 0);
1291 gtk_ui_manager_remove_ui (pdw->ui_manager, merge_id);
1293 list = gtk_ui_manager_get_action_groups (uim);
1294 for (; list != NULL; list = list->next)
1296 GtkActionGroup *action_group = list->data;
1297 gtk_ui_manager_remove_action_group (pdw->ui_manager, action_group);
1300 gtk_window_remove_accel_group (GTK_WINDOW (pdw),
1301 gtk_ui_manager_get_accel_group (uim));
1305 psppire_data_window_new (struct dataset *ds)
1309 if (the_session == NULL)
1310 the_session = session_create (NULL);
1314 char *dataset_name = session_generate_dataset_name (the_session);
1315 ds = dataset_create (the_session, dataset_name);
1316 free (dataset_name);
1318 assert (dataset_session (ds) == the_session);
1322 psppire_data_window_get_type (),
1323 "description", _("Data Editor"),
1327 if (dataset_name (ds) != NULL)
1328 g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1334 psppire_data_window_is_empty (PsppireDataWindow *dw)
1336 return psppire_dict_get_var_cnt (dw->dict) == 0;
1340 psppire_data_window_iface_init (PsppireWindowIface *iface)
1342 iface->save = save_file;
1343 iface->pick_filename = data_pick_filename;
1344 iface->load = load_file;
1348 psppire_default_data_window (void)
1350 if (ll_is_empty (&all_data_windows))
1351 create_data_window ();
1352 return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1356 psppire_data_window_set_default (PsppireDataWindow *pdw)
1358 ll_remove (&pdw->ll);
1359 ll_push_head (&all_data_windows, &pdw->ll);
1363 psppire_data_window_undefault (PsppireDataWindow *pdw)
1365 ll_remove (&pdw->ll);
1366 ll_push_tail (&all_data_windows, &pdw->ll);
1370 psppire_data_window_for_dataset (struct dataset *ds)
1372 PsppireDataWindow *pdw;
1374 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1375 if (pdw->dataset == ds)
1382 psppire_data_window_for_data_store (PsppireDataStore *data_store)
1384 PsppireDataWindow *pdw;
1386 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1387 if (pdw->data_store == data_store)
1394 create_data_window (void)
1396 gtk_widget_show (psppire_data_window_new (NULL));
1400 open_data_window (PsppireWindow *victim, const char *file_name,
1401 const char *encoding, gpointer hint)
1405 if (PSPPIRE_IS_DATA_WINDOW (victim)
1406 && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1408 window = GTK_WIDGET (victim);
1409 gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
1412 window = psppire_data_window_new (NULL);
1414 psppire_window_load (PSPPIRE_WINDOW (window), file_name, encoding, hint);
1415 gtk_widget_show_all (window);