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/syntax-gen.h"
44 #include "gl/c-strcase.h"
45 #include "gl/c-strcasestr.h"
46 #include "gl/xvasprintf.h"
49 #define _(msgid) gettext (msgid)
50 #define N_(msgid) msgid
52 struct session *the_session;
53 struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
55 static void psppire_data_window_class_init (PsppireDataWindowClass *class);
56 static void psppire_data_window_init (PsppireDataWindow *data_editor);
59 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
61 static void psppire_data_window_dispose (GObject *object);
62 static void psppire_data_window_finalize (GObject *object);
63 static void psppire_data_window_set_property (GObject *object,
67 static void psppire_data_window_get_property (GObject *object,
72 static guint psppire_data_window_add_ui (PsppireDataWindow *, GtkUIManager *);
73 static void psppire_data_window_remove_ui (PsppireDataWindow *,
74 GtkUIManager *, guint);
77 psppire_data_window_get_type (void)
79 static GType psppire_data_window_type = 0;
81 if (!psppire_data_window_type)
83 static const GTypeInfo psppire_data_window_info =
85 sizeof (PsppireDataWindowClass),
88 (GClassInitFunc)psppire_data_window_class_init,
89 (GClassFinalizeFunc) NULL,
91 sizeof (PsppireDataWindow),
93 (GInstanceInitFunc) psppire_data_window_init,
96 static const GInterfaceInfo window_interface_info =
98 (GInterfaceInitFunc) psppire_data_window_iface_init,
103 psppire_data_window_type =
104 g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
105 &psppire_data_window_info, 0);
108 g_type_add_interface_static (psppire_data_window_type,
109 PSPPIRE_TYPE_WINDOW_MODEL,
110 &window_interface_info);
113 return psppire_data_window_type;
116 static GObjectClass *parent_class ;
123 psppire_data_window_class_init (PsppireDataWindowClass *class)
125 GObjectClass *object_class = G_OBJECT_CLASS (class);
127 parent_class = g_type_class_peek_parent (class);
129 object_class->dispose = psppire_data_window_dispose;
130 object_class->finalize = psppire_data_window_finalize;
131 object_class->set_property = psppire_data_window_set_property;
132 object_class->get_property = psppire_data_window_get_property;
134 g_object_class_install_property (
135 object_class, PROP_DATASET,
136 g_param_spec_pointer ("dataset", "Dataset",
137 "'struct datset *' represented by the window",
138 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
141 /* Run the EXECUTE command. */
143 execute (PsppireDataWindow *dw)
145 execute_const_syntax_string (dw, "EXECUTE.");
149 transformation_change_callback (bool transformations_pending,
152 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
154 GtkWidget *status_label =
155 get_widget_assert (de->builder, "case-counter-area");
157 { /* Set the sensitivity of the "Transformations Pending" menuitem */
159 GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
161 GtkWidget *menuitem =
162 gtk_ui_manager_get_widget (uim,"/ui/menubar/transform/transform_run-pending");
164 gtk_widget_set_sensitive (menuitem, transformations_pending);
168 if ( transformations_pending)
169 gtk_label_set_text (GTK_LABEL (status_label),
170 _("Transformations Pending"));
172 gtk_label_set_text (GTK_LABEL (status_label), "");
175 /* Callback for when the dictionary changes its filter variable */
177 on_filter_change (GObject *o, gint filter_index, gpointer data)
179 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
181 GtkWidget *filter_status_area =
182 get_widget_assert (de->builder, "filter-use-status-area");
184 if ( filter_index == -1 )
186 gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
190 PsppireDict *dict = NULL;
191 struct variable *var ;
194 g_object_get (de->data_editor, "dictionary", &dict, NULL);
196 var = psppire_dict_get_variable (dict, filter_index);
198 text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
200 gtk_label_set_text (GTK_LABEL (filter_status_area), text);
206 /* Callback for when the dictionary changes its split variables */
208 on_split_change (PsppireDict *dict, gpointer data)
210 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
212 size_t n_split_vars = dict_get_split_cnt (dict->dict);
214 GtkWidget *split_status_area =
215 get_widget_assert (de->builder, "split-file-status-area");
217 if ( n_split_vars == 0 )
219 gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
225 const struct variable *const * split_vars =
226 dict_get_split_vars (dict->dict);
228 text = g_string_new (_("Split by "));
230 for (i = 0 ; i < n_split_vars - 1; ++i )
232 g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
234 g_string_append (text, var_get_name (split_vars[i]));
236 gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
238 g_string_free (text, TRUE);
245 /* Callback for when the dictionary changes its weights */
247 on_weight_change (GObject *o, gint weight_index, gpointer data)
249 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
251 GtkWidget *weight_status_area =
252 get_widget_assert (de->builder, "weight-status-area");
254 if ( weight_index == -1 )
256 gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
260 struct variable *var ;
261 PsppireDict *dict = NULL;
264 g_object_get (de->data_editor, "dictionary", &dict, NULL);
266 var = psppire_dict_get_variable (dict, weight_index);
268 text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
270 gtk_label_set_text (GTK_LABEL (weight_status_area), text);
278 dump_rm (GtkRecentManager *rm)
280 GList *items = gtk_recent_manager_get_items (rm);
284 g_print ("Recent Items:\n");
285 for (i = items; i; i = i->next)
287 GtkRecentInfo *ri = i->data;
289 g_print ("Item: %s (Mime: %s) (Desc: %s) (URI: %s)\n",
290 gtk_recent_info_get_short_name (ri),
291 gtk_recent_info_get_mime_type (ri),
292 gtk_recent_info_get_description (ri),
293 gtk_recent_info_get_uri (ri)
297 gtk_recent_info_unref (ri);
305 has_suffix (const gchar *name, const gchar *suffix)
307 size_t name_len = strlen (name);
308 size_t suffix_len = strlen (suffix);
309 return (name_len > suffix_len
310 && !c_strcasecmp (&name[name_len - suffix_len], suffix));
314 name_has_por_suffix (const gchar *name)
316 return has_suffix (name, ".por");
320 name_has_sav_suffix (const gchar *name)
322 return has_suffix (name, ".sav") || has_suffix (name, ".zsav");
325 /* Returns true if NAME has a suffix which might denote a PSPP file */
327 name_has_suffix (const gchar *name)
329 return name_has_por_suffix (name) || name_has_sav_suffix (name);
333 load_file (PsppireWindow *de, const gchar *file_name, const char *encoding,
336 const char *mime_type = NULL;
337 gchar *syntax = NULL;
342 gchar *utf8_file_name;
343 struct string filename;
345 utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
347 if (NULL == utf8_file_name)
350 ds_init_empty (&filename);
351 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
353 g_free (utf8_file_name);
355 if (encoding && encoding[0])
356 syntax = g_strdup_printf ("GET FILE=%s ENCODING='%s'.",
357 ds_cstr (&filename), encoding);
359 syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
360 ds_destroy (&filename);
367 ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
368 lex_reader_for_string (syntax, "UTF-8"));
371 if (ok && syn == NULL)
373 if (name_has_por_suffix (file_name))
374 mime_type = "application/x-spss-por";
375 else if (name_has_sav_suffix (file_name))
376 mime_type = "application/x-spss-sav";
378 add_most_recent (file_name, mime_type, encoding);
385 psppire_data_window_format_to_string (enum PsppireDataWindowFormat format)
387 if (format == PSPPIRE_DATA_WINDOW_SAV)
389 else if (format == PSPPIRE_DATA_WINDOW_ZSAV)
395 /* Save DE to file */
397 save_file (PsppireWindow *w)
399 const gchar *file_name = NULL;
400 gchar *utf8_file_name = NULL;
402 struct string filename ;
403 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
406 file_name = psppire_window_get_filename (w);
408 fnx = g_string_new (file_name);
410 if ( ! name_has_suffix (fnx->str))
411 g_string_append (fnx, psppire_data_window_format_to_string (de->format));
413 ds_init_empty (&filename);
415 utf8_file_name = g_filename_to_utf8 (fnx->str, -1, NULL, NULL, NULL);
417 g_string_free (fnx, TRUE);
419 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
420 g_free (utf8_file_name);
422 if (de->format == PSPPIRE_DATA_WINDOW_SAV)
423 syntax = g_strdup_printf ("SAVE OUTFILE=%s.", ds_cstr (&filename));
424 else if (de->format == PSPPIRE_DATA_WINDOW_ZSAV)
425 syntax = g_strdup_printf ("SAVE /ZCOMPRESSED /OUTFILE=%s.",
426 ds_cstr (&filename));
428 syntax = g_strdup_printf ("EXPORT OUTFILE=%s.", ds_cstr (&filename));
430 ds_destroy (&filename);
432 g_free (execute_syntax_string (de, syntax));
437 display_dict (PsppireDataWindow *de)
439 execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
443 sysfile_info (PsppireDataWindow *de)
445 GtkWidget *dialog = psppire_window_file_chooser_dialog (PSPPIRE_WINDOW (de));
447 if ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
449 struct string filename;
451 gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
452 gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
455 const gchar *encoding = psppire_encoding_selector_get_encoding (
456 gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
460 ds_init_empty (&filename);
462 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
464 g_free (utf8_file_name);
467 syntax = g_strdup_printf ("SYSFILE INFO %s ENCODING='%s'.",
468 ds_cstr (&filename), encoding);
470 syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
471 g_free (execute_syntax_string (de, syntax));
474 gtk_widget_destroy (dialog);
478 /* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
480 data_pick_filename (PsppireWindow *window)
482 GtkListStore *list_store;
483 GtkWidget *combo_box;
485 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
486 GtkFileFilter *filter;
488 gtk_file_chooser_dialog_new (_("Save"),
490 GTK_FILE_CHOOSER_ACTION_SAVE,
491 _("Cancel"), GTK_RESPONSE_CANCEL,
492 _("Save"), GTK_RESPONSE_ACCEPT,
495 g_object_set (dialog, "local-only", FALSE, NULL);
497 filter = gtk_file_filter_new ();
498 gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
499 gtk_file_filter_add_mime_type (filter, "application/x-spss-sav");
500 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
502 filter = gtk_file_filter_new ();
503 gtk_file_filter_set_name (filter, _("Compressed System Files (*.zsav)"));
504 gtk_file_filter_add_pattern (filter, "*.zsav");
505 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
507 filter = gtk_file_filter_new ();
508 gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
509 gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
510 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
512 filter = gtk_file_filter_new ();
513 gtk_file_filter_set_name (filter, _("All Files"));
514 gtk_file_filter_add_pattern (filter, "*");
515 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
516 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
519 GtkCellRenderer *cell;
524 list_store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
525 combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store));
527 gtk_list_store_append (list_store, &iter);
528 gtk_list_store_set (list_store, &iter,
529 0, PSPPIRE_DATA_WINDOW_SAV,
532 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
534 gtk_list_store_append (list_store, &iter);
535 gtk_list_store_set (list_store, &iter,
536 0, PSPPIRE_DATA_WINDOW_ZSAV,
537 1, _("Compressed System File"),
540 gtk_list_store_append (list_store, &iter);
541 gtk_list_store_set (list_store, &iter,
542 0, PSPPIRE_DATA_WINDOW_POR,
543 1, _("Portable File"),
546 label = gtk_label_new (_("Format:"));
548 cell = gtk_cell_renderer_text_new ();
549 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, FALSE);
550 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box), cell,
553 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
554 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
555 gtk_box_pack_start (GTK_BOX (hbox), combo_box, FALSE, FALSE, 0);
556 gtk_widget_show_all (hbox);
558 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), hbox);
561 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
564 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
566 case GTK_RESPONSE_ACCEPT:
571 gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
577 gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter);
578 gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter,
583 if ( ! name_has_suffix (filename->str))
584 g_string_append (filename,
585 psppire_data_window_format_to_string (format));
587 psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
589 g_string_free (filename, TRUE);
596 gtk_widget_destroy (dialog);
600 confirm_delete_dataset (PsppireDataWindow *de,
601 const char *old_dataset,
602 const char *new_dataset,
603 const char *existing_dataset)
608 dialog = gtk_message_dialog_new (
609 GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
610 _("Delete Existing Dataset?"));
612 gtk_message_dialog_format_secondary_text (
613 GTK_MESSAGE_DIALOG (dialog),
614 _("Renaming \"%s\" to \"%s\" will destroy the existing "
615 "dataset named \"%s\". Are you sure that you want to do this?"),
616 old_dataset, new_dataset, existing_dataset);
618 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
619 _("Cancel"), GTK_RESPONSE_CANCEL,
620 _("Delete"), GTK_RESPONSE_OK,
623 g_object_set (dialog, "icon-name", "pspp", NULL);
625 result = gtk_dialog_run (GTK_DIALOG (dialog));
627 gtk_widget_destroy (dialog);
629 return result == GTK_RESPONSE_OK;
633 on_rename_dataset (PsppireDataWindow *de)
635 struct dataset *ds = de->dataset;
636 struct session *session = dataset_session (ds);
637 const char *old_name = dataset_name (ds);
638 struct dataset *existing_dataset;
642 prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
644 new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
648 if (new_name == NULL)
651 existing_dataset = session_lookup_dataset (session, new_name);
652 if (existing_dataset == NULL || existing_dataset == ds
653 || confirm_delete_dataset (de, old_name, new_name,
654 dataset_name (existing_dataset)))
655 g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
662 status_bar_activate (PsppireDataWindow *de, GtkToggleAction *action)
664 GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
666 if ( gtk_toggle_action_get_active (action))
667 gtk_widget_show (statusbar);
669 gtk_widget_hide (statusbar);
674 grid_lines_activate (PsppireDataWindow *de, GtkToggleAction *action)
676 const gboolean grid_visible = gtk_toggle_action_get_active (action);
678 psppire_data_editor_show_grid (de->data_editor, grid_visible);
682 data_view_activate (PsppireDataWindow *de)
684 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
689 variable_view_activate (PsppireDataWindow *de)
691 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
696 fonts_activate (PsppireDataWindow *de)
698 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
699 GtkWidget *dialog = gtk_font_chooser_dialog_new (NULL, GTK_WINDOW (toplevel));
700 GtkStyle *style = gtk_widget_get_style (GTK_WIDGET(de->data_editor));
701 PangoFontDescription *current_font = style->font_desc;
703 gtk_font_chooser_set_font_desc (GTK_FONT_CHOOSER (dialog), current_font);
705 gtk_window_set_transient_for (GTK_WINDOW (dialog),
706 GTK_WINDOW (toplevel));
708 if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
710 PangoFontDescription* font_desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (dialog));
712 psppire_data_editor_set_font (de->data_editor, font_desc);
715 gtk_widget_hide (dialog);
720 /* Callback for the value labels action */
722 toggle_value_labels (PsppireDataWindow *de, GtkToggleAction *ta)
724 g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
728 toggle_split_window (PsppireDataWindow *de, GtkToggleAction *ta)
730 psppire_data_editor_split_window (de->data_editor,
731 gtk_toggle_action_get_active (ta));
736 file_quit (PsppireDataWindow *de)
738 /* FIXME: Need to be more intelligent here.
739 Give the user the opportunity to save any unsaved data.
745 on_recent_data_select (GtkMenuShell *menushell,
746 PsppireWindow *window)
751 gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
753 file = g_filename_from_uri (uri, NULL, NULL);
757 open_data_window (window, file, NULL, NULL);
763 charset_from_mime_type (const char *mime_type)
769 if (mime_type == NULL)
772 charset = c_strcasestr (mime_type, "charset=");
780 /* Parse a "quoted-string" as defined by RFC 822. */
781 for (p++; *p != '\0' && *p != '"'; p++)
784 ds_put_byte (&s, *p);
785 else if (*++p != '\0')
786 ds_put_byte (&s, *p);
791 /* Parse a "token" as defined by RFC 2045. */
792 while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
793 ds_put_byte (&s, *p++);
795 if (!ds_is_empty (&s))
796 return ds_steal_cstr (&s);
803 on_recent_files_select (GtkMenuShell *menushell, gpointer user_data)
810 /* Get the file name and its encoding. */
811 item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
812 file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
813 encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
814 gtk_recent_info_unref (item);
816 se = psppire_syntax_window_new (encoding);
820 if ( psppire_window_load (PSPPIRE_WINDOW (se), file, encoding, NULL) )
821 gtk_widget_show (se);
823 gtk_widget_destroy (se);
829 set_unsaved (gpointer w)
831 psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
835 on_switch_page (PsppireDataEditor *de, gpointer p,
836 gint pagenum, PsppireDataWindow *dw)
838 /* Set the appropriate ui_manager according to the selected page.
839 This is necessary, because the menus for the variable view and
840 the data view are different (slightly). */
842 gboolean is_ds = pagenum == PSPPIRE_DATA_EDITOR_DATA_VIEW;
843 const char *path = (is_ds
844 ? "/ui/menubar/view/view_data"
845 : "/ui/menubar/view/view_variables");
847 GtkWidget *page_menu_item = gtk_ui_manager_get_widget (dw->ui_manager, path);
848 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (page_menu_item), TRUE);
852 on_ui_manager_changed (PsppireDataEditor *de,
853 GParamSpec *pspec UNUSED,
854 PsppireDataWindow *dw)
856 GtkUIManager *uim = psppire_data_editor_get_ui_manager (de);
862 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
863 g_object_unref (dw->uim);
870 g_object_ref (dw->uim);
871 dw->merge_id = psppire_data_window_add_ui (dw, dw->uim);
875 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
876 Returns a pointer to the action
879 connect_action (PsppireDataWindow *dw, const char *action_name,
882 GtkAction *action = get_action_assert (dw->builder, action_name);
884 g_signal_connect_swapped (action, "activate", handler, dw);
889 /* Only a data file with at least one variable can be saved. */
891 enable_save (PsppireDataWindow *dw)
893 gboolean enable = psppire_dict_get_var_cnt (dw->dict) > 0;
895 gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save"),
897 gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save_as"),
901 /* Initializes as much of a PsppireDataWindow as we can and must before the
902 dataset has been set.
904 In particular, the 'menu' member is required in case the "filename" property
905 is set before the "dataset" property: otherwise PsppireWindow will try to
906 modify the menu as part of the "filename" property_set() function and end up
907 with a Gtk-CRITICAL since 'menu' is NULL. */
909 psppire_data_window_init (PsppireDataWindow *de)
912 de->builder = builder_new ("data-editor.ui");
914 de->ui_manager = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
916 w = gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/windows/windows_minimise_all");
918 PSPPIRE_WINDOW (de)->menu = GTK_MENU_SHELL (gtk_widget_get_parent (w));
925 file_import (PsppireDataWindow *dw)
927 GtkWidget *w = psppire_import_assistant_new (GTK_WINDOW (dw));
928 PsppireImportAssistant *asst = PSPPIRE_IMPORT_ASSISTANT (w);
929 gtk_widget_show_all (w);
931 asst->main_loop = g_main_loop_new (NULL, TRUE);
932 g_main_loop_run (asst->main_loop);
933 g_main_loop_unref (asst->main_loop);
935 if (!asst->file_name)
938 switch (asst->response)
940 case GTK_RESPONSE_APPLY:
943 gchar *fn = g_path_get_basename (asst->file_name);
944 open_data_window (PSPPIRE_WINDOW (dw), fn, NULL, psppire_import_assistant_generate_syntax (asst));
948 case PSPPIRE_RESPONSE_PASTE:
950 free (paste_syntax_to_window (psppire_import_assistant_generate_syntax (asst)));
957 gtk_widget_destroy (GTK_WIDGET (asst));
961 psppire_data_window_finish_init (PsppireDataWindow *de,
964 static const struct dataset_callbacks cbs =
966 set_unsaved, /* changed */
967 transformation_change_callback, /* transformations_changed */
974 GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
977 de->dict = psppire_dict_new_from_dict (dataset_dict (ds));
978 de->data_store = psppire_data_store_new (de->dict);
979 psppire_data_store_set_reader (de->data_store, NULL);
981 menubar = get_widget_assert (de->builder, "menubar");
982 hb = get_widget_assert (de->builder, "toolbar");
983 sb = get_widget_assert (de->builder, "status-bar");
989 PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store));
990 g_signal_connect (de->data_editor, "switch-page",
991 G_CALLBACK (on_switch_page), de);
993 g_signal_connect_swapped (de->data_store, "case-changed",
994 G_CALLBACK (set_unsaved), de);
996 g_signal_connect_swapped (de->data_store, "case-inserted",
997 G_CALLBACK (set_unsaved), de);
999 g_signal_connect_swapped (de->data_store, "cases-deleted",
1000 G_CALLBACK (set_unsaved), de);
1002 dataset_set_callbacks (de->dataset, &cbs, de);
1004 connect_help (de->builder);
1006 gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
1007 gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
1008 gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
1009 gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
1011 gtk_container_add (GTK_CONTAINER (de), box);
1013 g_signal_connect (de->dict, "weight-changed",
1014 G_CALLBACK (on_weight_change),
1017 g_signal_connect (de->dict, "filter-changed",
1018 G_CALLBACK (on_filter_change),
1021 g_signal_connect (de->dict, "split-changed",
1022 G_CALLBACK (on_split_change),
1025 g_signal_connect_swapped (de->dict, "backend-changed",
1026 G_CALLBACK (enable_save), de);
1027 g_signal_connect_swapped (de->dict, "variable-inserted",
1028 G_CALLBACK (enable_save), de);
1029 g_signal_connect_swapped (de->dict, "variable-deleted",
1030 G_CALLBACK (enable_save), de);
1033 connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1034 connect_action (de, "file_import", G_CALLBACK (file_import));
1036 connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1037 connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1038 connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1039 connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1040 connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1041 connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1043 g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1045 connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1046 connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1047 connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1050 GtkWidget *recent_data =
1051 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-data");
1053 GtkWidget *recent_files =
1054 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-files");
1057 GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1058 gtk_recent_manager_get_default ());
1060 GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1061 gtk_recent_manager_get_default ());
1063 g_object_set (menu_data, "show-tips", TRUE, NULL);
1064 g_object_set (menu_files, "show-tips", TRUE, NULL);
1067 GtkRecentFilter *filter = gtk_recent_filter_new ();
1069 gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1070 gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1072 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1074 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1077 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1080 g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1083 GtkRecentFilter *filter = gtk_recent_filter_new ();
1085 gtk_recent_filter_add_pattern (filter, "*.sps");
1086 gtk_recent_filter_add_pattern (filter, "*.SPS");
1088 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1090 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1093 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1095 g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1099 connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1102 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1103 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1105 connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1107 connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1109 connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1111 connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1113 connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1115 connect_action (de, "file_quit", G_CALLBACK (file_quit));
1117 connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1119 connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1121 g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1123 gtk_menu_shell_append (GTK_MENU_SHELL (menubar), create_help_menu (GTK_WINDOW (de)));
1125 g_signal_connect (de->data_editor, "notify::ui-manager",
1126 G_CALLBACK (on_ui_manager_changed), de);
1127 on_ui_manager_changed (de->data_editor, NULL, de);
1129 gtk_widget_show (GTK_WIDGET (de->data_editor));
1130 gtk_widget_show (box);
1132 ll_push_head (&all_data_windows, &de->ll);
1136 psppire_data_window_dispose (GObject *object)
1138 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1142 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
1143 g_object_unref (dw->uim);
1147 if (dw->builder != NULL)
1149 g_object_unref (dw->builder);
1155 g_signal_handlers_disconnect_by_func (dw->dict,
1156 G_CALLBACK (enable_save), dw);
1157 g_signal_handlers_disconnect_by_func (dw->dict,
1158 G_CALLBACK (on_weight_change), dw);
1159 g_signal_handlers_disconnect_by_func (dw->dict,
1160 G_CALLBACK (on_filter_change), dw);
1161 g_signal_handlers_disconnect_by_func (dw->dict,
1162 G_CALLBACK (on_split_change), dw);
1164 g_object_unref (dw->dict);
1170 g_object_unref (dw->data_store);
1171 dw->data_store = NULL;
1174 if (dw->ll.next != NULL)
1176 ll_remove (&dw->ll);
1180 if (G_OBJECT_CLASS (parent_class)->dispose)
1181 G_OBJECT_CLASS (parent_class)->dispose (object);
1185 psppire_data_window_finalize (GObject *object)
1187 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1191 struct dataset *dataset = dw->dataset;
1192 struct session *session = dataset_session (dataset);
1196 dataset_set_callbacks (dataset, NULL, NULL);
1197 session_set_active_dataset (session, NULL);
1198 dataset_destroy (dataset);
1201 if (G_OBJECT_CLASS (parent_class)->finalize)
1202 G_OBJECT_CLASS (parent_class)->finalize (object);
1206 psppire_data_window_set_property (GObject *object,
1208 const GValue *value,
1211 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1216 psppire_data_window_finish_init (window, g_value_get_pointer (value));
1219 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1225 psppire_data_window_get_property (GObject *object,
1230 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1235 g_value_set_pointer (value, window->dataset);
1238 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1244 psppire_data_window_add_ui (PsppireDataWindow *pdw, GtkUIManager *uim)
1250 ui_string = gtk_ui_manager_get_ui (uim);
1251 merge_id = gtk_ui_manager_add_ui_from_string (pdw->ui_manager, ui_string,
1255 g_return_val_if_fail (merge_id != 0, 0);
1257 list = gtk_ui_manager_get_action_groups (uim);
1258 for (; list != NULL; list = list->next)
1260 GtkActionGroup *action_group = list->data;
1261 GList *actions = gtk_action_group_list_actions (action_group);
1264 for (action = actions; action != NULL; action = action->next)
1266 GtkAction *a = action->data;
1268 if (PSPPIRE_IS_DIALOG_ACTION (a))
1269 g_object_set (a, "manager", pdw->ui_manager, NULL);
1272 gtk_ui_manager_insert_action_group (pdw->ui_manager, action_group, 0);
1275 gtk_window_add_accel_group (GTK_WINDOW (pdw),
1276 gtk_ui_manager_get_accel_group (uim));
1282 psppire_data_window_remove_ui (PsppireDataWindow *pdw,
1283 GtkUIManager *uim, guint merge_id)
1287 g_return_if_fail (merge_id != 0);
1289 gtk_ui_manager_remove_ui (pdw->ui_manager, merge_id);
1291 list = gtk_ui_manager_get_action_groups (uim);
1292 for (; list != NULL; list = list->next)
1294 GtkActionGroup *action_group = list->data;
1295 gtk_ui_manager_remove_action_group (pdw->ui_manager, action_group);
1298 gtk_window_remove_accel_group (GTK_WINDOW (pdw),
1299 gtk_ui_manager_get_accel_group (uim));
1303 psppire_data_window_new (struct dataset *ds)
1307 if (the_session == NULL)
1308 the_session = session_create (NULL);
1312 char *dataset_name = session_generate_dataset_name (the_session);
1313 ds = dataset_create (the_session, dataset_name);
1314 free (dataset_name);
1316 assert (dataset_session (ds) == the_session);
1320 psppire_data_window_get_type (),
1321 "description", _("Data Editor"),
1325 if (dataset_name (ds) != NULL)
1326 g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1332 psppire_data_window_is_empty (PsppireDataWindow *dw)
1334 return psppire_dict_get_var_cnt (dw->dict) == 0;
1338 psppire_data_window_iface_init (PsppireWindowIface *iface)
1340 iface->save = save_file;
1341 iface->pick_filename = data_pick_filename;
1342 iface->load = load_file;
1346 psppire_default_data_window (void)
1348 if (ll_is_empty (&all_data_windows))
1349 create_data_window ();
1350 return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1354 psppire_data_window_set_default (PsppireDataWindow *pdw)
1356 ll_remove (&pdw->ll);
1357 ll_push_head (&all_data_windows, &pdw->ll);
1361 psppire_data_window_undefault (PsppireDataWindow *pdw)
1363 ll_remove (&pdw->ll);
1364 ll_push_tail (&all_data_windows, &pdw->ll);
1368 psppire_data_window_for_dataset (struct dataset *ds)
1370 PsppireDataWindow *pdw;
1372 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1373 if (pdw->dataset == ds)
1380 psppire_data_window_for_data_store (PsppireDataStore *data_store)
1382 PsppireDataWindow *pdw;
1384 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1385 if (pdw->data_store == data_store)
1392 create_data_window (void)
1394 gtk_widget_show (psppire_data_window_new (NULL));
1398 open_data_window (PsppireWindow *victim, const char *file_name,
1399 const char *encoding, gpointer hint)
1403 if (PSPPIRE_IS_DATA_WINDOW (victim)
1404 && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1406 window = GTK_WIDGET (victim);
1407 gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
1410 window = psppire_data_window_new (NULL);
1412 psppire_window_load (PSPPIRE_WINDOW (window), file_name, encoding, hint);
1413 gtk_widget_show_all (window);