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/autorecode-dialog.h"
28 #include "ui/gui/builder-wrapper.h"
29 #include "ui/gui/comments-dialog.h"
30 #include "ui/gui/entry-dialog.h"
31 #include "ui/gui/executor.h"
32 #include "ui/gui/help-menu.h"
33 #include "ui/gui/helper.h"
34 #include "ui/gui/helper.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/text-data-import-dialog.h"
45 #include "ui/gui/weight-cases-dialog.h"
46 #include "ui/syntax-gen.h"
48 #include "gl/c-strcase.h"
49 #include "gl/c-strcasestr.h"
50 #include "gl/xvasprintf.h"
53 #define _(msgid) gettext (msgid)
54 #define N_(msgid) msgid
56 struct session *the_session;
57 struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
59 static void psppire_data_window_class_init (PsppireDataWindowClass *class);
60 static void psppire_data_window_init (PsppireDataWindow *data_editor);
63 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
65 static void psppire_data_window_dispose (GObject *object);
66 static void psppire_data_window_finalize (GObject *object);
67 static void psppire_data_window_set_property (GObject *object,
71 static void psppire_data_window_get_property (GObject *object,
76 static guint psppire_data_window_add_ui (PsppireDataWindow *, GtkUIManager *);
77 static void psppire_data_window_remove_ui (PsppireDataWindow *,
78 GtkUIManager *, guint);
81 psppire_data_window_get_type (void)
83 static GType psppire_data_window_type = 0;
85 if (!psppire_data_window_type)
87 static const GTypeInfo psppire_data_window_info =
89 sizeof (PsppireDataWindowClass),
92 (GClassInitFunc)psppire_data_window_class_init,
93 (GClassFinalizeFunc) NULL,
95 sizeof (PsppireDataWindow),
97 (GInstanceInitFunc) psppire_data_window_init,
100 static const GInterfaceInfo window_interface_info =
102 (GInterfaceInitFunc) psppire_data_window_iface_init,
107 psppire_data_window_type =
108 g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
109 &psppire_data_window_info, 0);
112 g_type_add_interface_static (psppire_data_window_type,
113 PSPPIRE_TYPE_WINDOW_MODEL,
114 &window_interface_info);
117 return psppire_data_window_type;
120 static GObjectClass *parent_class ;
127 psppire_data_window_class_init (PsppireDataWindowClass *class)
129 GObjectClass *object_class = G_OBJECT_CLASS (class);
131 parent_class = g_type_class_peek_parent (class);
133 object_class->dispose = psppire_data_window_dispose;
134 object_class->finalize = psppire_data_window_finalize;
135 object_class->set_property = psppire_data_window_set_property;
136 object_class->get_property = psppire_data_window_get_property;
138 g_object_class_install_property (
139 object_class, PROP_DATASET,
140 g_param_spec_pointer ("dataset", "Dataset",
141 "'struct datset *' represented by the window",
142 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
145 /* Run the EXECUTE command. */
147 execute (PsppireDataWindow *dw)
149 execute_const_syntax_string (dw, "EXECUTE.");
153 transformation_change_callback (bool transformations_pending,
156 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
158 GtkWidget *status_label =
159 get_widget_assert (de->builder, "case-counter-area");
161 { /* Set the sensitivity of the "Transformations Pending" menuitem */
163 GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
165 GtkWidget *menuitem =
166 gtk_ui_manager_get_widget (uim,"/ui/menubar/transform/transform_run-pending");
168 gtk_widget_set_sensitive (menuitem, transformations_pending);
172 if ( transformations_pending)
173 gtk_label_set_text (GTK_LABEL (status_label),
174 _("Transformations Pending"));
176 gtk_label_set_text (GTK_LABEL (status_label), "");
179 /* Callback for when the dictionary changes its filter variable */
181 on_filter_change (GObject *o, gint filter_index, gpointer data)
183 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
185 GtkWidget *filter_status_area =
186 get_widget_assert (de->builder, "filter-use-status-area");
188 if ( filter_index == -1 )
190 gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
194 PsppireDict *dict = NULL;
195 struct variable *var ;
198 g_object_get (de->data_editor, "dictionary", &dict, NULL);
200 var = psppire_dict_get_variable (dict, filter_index);
202 text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
204 gtk_label_set_text (GTK_LABEL (filter_status_area), text);
210 /* Callback for when the dictionary changes its split variables */
212 on_split_change (PsppireDict *dict, gpointer data)
214 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
216 size_t n_split_vars = dict_get_split_cnt (dict->dict);
218 GtkWidget *split_status_area =
219 get_widget_assert (de->builder, "split-file-status-area");
221 if ( n_split_vars == 0 )
223 gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
229 const struct variable *const * split_vars =
230 dict_get_split_vars (dict->dict);
232 text = g_string_new (_("Split by "));
234 for (i = 0 ; i < n_split_vars - 1; ++i )
236 g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
238 g_string_append (text, var_get_name (split_vars[i]));
240 gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
242 g_string_free (text, TRUE);
249 /* Callback for when the dictionary changes its weights */
251 on_weight_change (GObject *o, gint weight_index, gpointer data)
253 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
255 GtkWidget *weight_status_area =
256 get_widget_assert (de->builder, "weight-status-area");
258 if ( weight_index == -1 )
260 gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
264 struct variable *var ;
265 PsppireDict *dict = NULL;
268 g_object_get (de->data_editor, "dictionary", &dict, NULL);
270 var = psppire_dict_get_variable (dict, weight_index);
272 text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
274 gtk_label_set_text (GTK_LABEL (weight_status_area), text);
282 dump_rm (GtkRecentManager *rm)
284 GList *items = gtk_recent_manager_get_items (rm);
288 g_print ("Recent Items:\n");
289 for (i = items; i; i = i->next)
291 GtkRecentInfo *ri = i->data;
293 g_print ("Item: %s (Mime: %s) (Desc: %s) (URI: %s)\n",
294 gtk_recent_info_get_short_name (ri),
295 gtk_recent_info_get_mime_type (ri),
296 gtk_recent_info_get_description (ri),
297 gtk_recent_info_get_uri (ri)
301 gtk_recent_info_unref (ri);
309 has_suffix (const gchar *name, const gchar *suffix)
311 size_t name_len = strlen (name);
312 size_t suffix_len = strlen (suffix);
313 return (name_len > suffix_len
314 && !c_strcasecmp (&name[name_len - suffix_len], suffix));
318 name_has_por_suffix (const gchar *name)
320 return has_suffix (name, ".por");
324 name_has_sav_suffix (const gchar *name)
326 return has_suffix (name, ".sav") || has_suffix (name, ".zsav");
329 /* Returns true if NAME has a suffix which might denote a PSPP file */
331 name_has_suffix (const gchar *name)
333 return name_has_por_suffix (name) || name_has_sav_suffix (name);
337 load_file (PsppireWindow *de, const gchar *file_name, const char *encoding,
340 const char *mime_type = NULL;
341 gchar *syntax = NULL;
346 gchar *utf8_file_name;
347 struct string filename;
349 utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
351 if (NULL == utf8_file_name)
354 ds_init_empty (&filename);
355 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
357 g_free (utf8_file_name);
359 if (encoding && encoding[0])
360 syntax = g_strdup_printf ("GET FILE=%s ENCODING='%s'.",
361 ds_cstr (&filename), encoding);
363 syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
364 ds_destroy (&filename);
371 ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
372 lex_reader_for_string (syntax, "UTF-8"));
375 if (ok && syn == NULL)
377 if (name_has_por_suffix (file_name))
378 mime_type = "application/x-spss-por";
379 else if (name_has_sav_suffix (file_name))
380 mime_type = "application/x-spss-sav";
382 add_most_recent (file_name, mime_type, encoding);
389 psppire_data_window_format_to_string (enum PsppireDataWindowFormat format)
391 if (format == PSPPIRE_DATA_WINDOW_SAV)
393 else if (format == PSPPIRE_DATA_WINDOW_ZSAV)
399 /* Save DE to file */
401 save_file (PsppireWindow *w)
403 const gchar *file_name = NULL;
404 gchar *utf8_file_name = NULL;
406 struct string filename ;
407 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
410 file_name = psppire_window_get_filename (w);
412 fnx = g_string_new (file_name);
414 if ( ! name_has_suffix (fnx->str))
415 g_string_append (fnx, psppire_data_window_format_to_string (de->format));
417 ds_init_empty (&filename);
419 utf8_file_name = g_filename_to_utf8 (fnx->str, -1, NULL, NULL, NULL);
421 g_string_free (fnx, TRUE);
423 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
424 g_free (utf8_file_name);
426 if (de->format == PSPPIRE_DATA_WINDOW_SAV)
427 syntax = g_strdup_printf ("SAVE OUTFILE=%s.", ds_cstr (&filename));
428 else if (de->format == PSPPIRE_DATA_WINDOW_ZSAV)
429 syntax = g_strdup_printf ("SAVE /ZCOMPRESSED /OUTFILE=%s.",
430 ds_cstr (&filename));
432 syntax = g_strdup_printf ("EXPORT OUTFILE=%s.", ds_cstr (&filename));
434 ds_destroy (&filename);
436 g_free (execute_syntax_string (de, syntax));
441 display_dict (PsppireDataWindow *de)
443 execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
447 sysfile_info (PsppireDataWindow *de)
449 GtkWidget *dialog = psppire_window_file_chooser_dialog (PSPPIRE_WINDOW (de));
451 if ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
453 struct string filename;
455 gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
456 gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
459 const gchar *encoding = psppire_encoding_selector_get_encoding (
460 gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
464 ds_init_empty (&filename);
466 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
468 g_free (utf8_file_name);
471 syntax = g_strdup_printf ("SYSFILE INFO %s ENCODING='%s'.",
472 ds_cstr (&filename), encoding);
474 syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
475 g_free (execute_syntax_string (de, syntax));
478 gtk_widget_destroy (dialog);
482 /* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
484 data_pick_filename (PsppireWindow *window)
486 GtkListStore *list_store;
487 GtkWidget *combo_box;
489 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
490 GtkFileFilter *filter;
492 gtk_file_chooser_dialog_new (_("Save"),
494 GTK_FILE_CHOOSER_ACTION_SAVE,
495 _("Cancel"), GTK_RESPONSE_CANCEL,
496 _("Save"), GTK_RESPONSE_ACCEPT,
499 g_object_set (dialog, "local-only", FALSE, NULL);
501 filter = gtk_file_filter_new ();
502 gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
503 gtk_file_filter_add_mime_type (filter, "application/x-spss-sav");
504 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
506 filter = gtk_file_filter_new ();
507 gtk_file_filter_set_name (filter, _("Compressed System Files (*.zsav)"));
508 gtk_file_filter_add_pattern (filter, "*.zsav");
509 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
511 filter = gtk_file_filter_new ();
512 gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
513 gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
514 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
516 filter = gtk_file_filter_new ();
517 gtk_file_filter_set_name (filter, _("All Files"));
518 gtk_file_filter_add_pattern (filter, "*");
519 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
520 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
523 GtkCellRenderer *cell;
528 list_store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
529 combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store));
531 gtk_list_store_append (list_store, &iter);
532 gtk_list_store_set (list_store, &iter,
533 0, PSPPIRE_DATA_WINDOW_SAV,
536 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
538 gtk_list_store_append (list_store, &iter);
539 gtk_list_store_set (list_store, &iter,
540 0, PSPPIRE_DATA_WINDOW_ZSAV,
541 1, _("Compressed System File"),
544 gtk_list_store_append (list_store, &iter);
545 gtk_list_store_set (list_store, &iter,
546 0, PSPPIRE_DATA_WINDOW_POR,
547 1, _("Portable File"),
550 label = gtk_label_new (_("Format:"));
552 cell = gtk_cell_renderer_text_new ();
553 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, FALSE);
554 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box), cell,
557 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
558 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
559 gtk_box_pack_start (GTK_BOX (hbox), combo_box, FALSE, FALSE, 0);
560 gtk_widget_show_all (hbox);
562 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), hbox);
565 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
568 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
570 case GTK_RESPONSE_ACCEPT:
575 gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
581 gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter);
582 gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter,
587 if ( ! name_has_suffix (filename->str))
588 g_string_append (filename,
589 psppire_data_window_format_to_string (format));
591 psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
593 g_string_free (filename, TRUE);
600 gtk_widget_destroy (dialog);
604 confirm_delete_dataset (PsppireDataWindow *de,
605 const char *old_dataset,
606 const char *new_dataset,
607 const char *existing_dataset)
612 dialog = gtk_message_dialog_new (
613 GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
614 _("Delete Existing Dataset?"));
616 gtk_message_dialog_format_secondary_text (
617 GTK_MESSAGE_DIALOG (dialog),
618 _("Renaming \"%s\" to \"%s\" will destroy the existing "
619 "dataset named \"%s\". Are you sure that you want to do this?"),
620 old_dataset, new_dataset, existing_dataset);
622 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
623 _("Cancel"), GTK_RESPONSE_CANCEL,
624 _("Delete"), GTK_RESPONSE_OK,
627 g_object_set (dialog, "icon-name", "pspp", NULL);
629 result = gtk_dialog_run (GTK_DIALOG (dialog));
631 gtk_widget_destroy (dialog);
633 return result == GTK_RESPONSE_OK;
637 on_rename_dataset (PsppireDataWindow *de)
639 struct dataset *ds = de->dataset;
640 struct session *session = dataset_session (ds);
641 const char *old_name = dataset_name (ds);
642 struct dataset *existing_dataset;
646 prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
648 new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
652 if (new_name == NULL)
655 existing_dataset = session_lookup_dataset (session, new_name);
656 if (existing_dataset == NULL || existing_dataset == ds
657 || confirm_delete_dataset (de, old_name, new_name,
658 dataset_name (existing_dataset)))
659 g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
666 status_bar_activate (PsppireDataWindow *de, GtkToggleAction *action)
668 GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
670 if ( gtk_toggle_action_get_active (action))
671 gtk_widget_show (statusbar);
673 gtk_widget_hide (statusbar);
678 grid_lines_activate (PsppireDataWindow *de, GtkToggleAction *action)
680 const gboolean grid_visible = gtk_toggle_action_get_active (action);
682 psppire_data_editor_show_grid (de->data_editor, grid_visible);
686 data_view_activate (PsppireDataWindow *de)
688 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
693 variable_view_activate (PsppireDataWindow *de)
695 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
700 fonts_activate (PsppireDataWindow *de)
702 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
703 GtkWidget *dialog = gtk_font_chooser_dialog_new (NULL, GTK_WINDOW (toplevel));
704 GtkStyle *style = gtk_widget_get_style (GTK_WIDGET(de->data_editor));
705 PangoFontDescription *current_font = style->font_desc;
707 gtk_font_chooser_set_font_desc (GTK_FONT_CHOOSER (dialog), current_font);
709 gtk_window_set_transient_for (GTK_WINDOW (dialog),
710 GTK_WINDOW (toplevel));
712 if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
714 PangoFontDescription* font_desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (dialog));
716 psppire_data_editor_set_font (de->data_editor, font_desc);
719 gtk_widget_hide (dialog);
724 /* Callback for the value labels action */
726 toggle_value_labels (PsppireDataWindow *de, GtkToggleAction *ta)
728 g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
732 toggle_split_window (PsppireDataWindow *de, GtkToggleAction *ta)
734 psppire_data_editor_split_window (de->data_editor,
735 gtk_toggle_action_get_active (ta));
740 file_quit (PsppireDataWindow *de)
742 /* FIXME: Need to be more intelligent here.
743 Give the user the opportunity to save any unsaved data.
749 on_recent_data_select (GtkMenuShell *menushell,
750 PsppireWindow *window)
755 gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
757 file = g_filename_from_uri (uri, NULL, NULL);
761 open_data_window (window, file, NULL, NULL);
767 charset_from_mime_type (const char *mime_type)
773 if (mime_type == NULL)
776 charset = c_strcasestr (mime_type, "charset=");
784 /* Parse a "quoted-string" as defined by RFC 822. */
785 for (p++; *p != '\0' && *p != '"'; p++)
788 ds_put_byte (&s, *p);
789 else if (*++p != '\0')
790 ds_put_byte (&s, *p);
795 /* Parse a "token" as defined by RFC 2045. */
796 while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
797 ds_put_byte (&s, *p++);
799 if (!ds_is_empty (&s))
800 return ds_steal_cstr (&s);
807 on_recent_files_select (GtkMenuShell *menushell, gpointer user_data)
814 /* Get the file name and its encoding. */
815 item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
816 file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
817 encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
818 gtk_recent_info_unref (item);
820 se = psppire_syntax_window_new (encoding);
824 if ( psppire_window_load (PSPPIRE_WINDOW (se), file, encoding, NULL) )
825 gtk_widget_show (se);
827 gtk_widget_destroy (se);
833 set_unsaved (gpointer w)
835 psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
839 on_switch_page (PsppireDataEditor *de, gpointer p,
840 gint pagenum, PsppireDataWindow *dw)
842 /* Set the appropriate ui_manager according to the selected page.
843 This is necessary, because the menus for the variable view and
844 the data view are different (slightly). */
846 gboolean is_ds = pagenum == PSPPIRE_DATA_EDITOR_DATA_VIEW;
847 const char *path = (is_ds
848 ? "/ui/menubar/view/view_data"
849 : "/ui/menubar/view/view_variables");
851 GtkWidget *page_menu_item = gtk_ui_manager_get_widget (dw->ui_manager, path);
852 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (page_menu_item), TRUE);
856 on_ui_manager_changed (PsppireDataEditor *de,
857 GParamSpec *pspec UNUSED,
858 PsppireDataWindow *dw)
860 GtkUIManager *uim = psppire_data_editor_get_ui_manager (de);
866 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
867 g_object_unref (dw->uim);
874 g_object_ref (dw->uim);
875 dw->merge_id = psppire_data_window_add_ui (dw, dw->uim);
879 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
880 Returns a pointer to the action
883 connect_action (PsppireDataWindow *dw, const char *action_name,
886 GtkAction *action = get_action_assert (dw->builder, action_name);
888 g_signal_connect_swapped (action, "activate", handler, dw);
893 /* Only a data file with at least one variable can be saved. */
895 enable_save (PsppireDataWindow *dw)
897 gboolean enable = psppire_dict_get_var_cnt (dw->dict) > 0;
899 gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save"),
901 gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save_as"),
905 /* Initializes as much of a PsppireDataWindow as we can and must before the
906 dataset has been set.
908 In particular, the 'menu' member is required in case the "filename" property
909 is set before the "dataset" property: otherwise PsppireWindow will try to
910 modify the menu as part of the "filename" property_set() function and end up
911 with a Gtk-CRITICAL since 'menu' is NULL. */
913 psppire_data_window_init (PsppireDataWindow *de)
916 de->builder = builder_new ("data-editor.ui");
918 de->ui_manager = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
920 w = gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/windows/windows_minimise_all");
922 PSPPIRE_WINDOW (de)->menu = GTK_MENU_SHELL (gtk_widget_get_parent (w));
929 psppire_data_window_finish_init (PsppireDataWindow *de,
932 static const struct dataset_callbacks cbs =
934 set_unsaved, /* changed */
935 transformation_change_callback, /* transformations_changed */
942 GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
945 de->dict = psppire_dict_new_from_dict (dataset_dict (ds));
946 de->data_store = psppire_data_store_new (de->dict);
947 psppire_data_store_set_reader (de->data_store, NULL);
949 menubar = get_widget_assert (de->builder, "menubar");
950 hb = get_widget_assert (de->builder, "toolbar");
951 sb = get_widget_assert (de->builder, "status-bar");
957 PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store));
958 g_signal_connect (de->data_editor, "switch-page",
959 G_CALLBACK (on_switch_page), de);
961 g_signal_connect_swapped (de->data_store, "case-changed",
962 G_CALLBACK (set_unsaved), de);
964 g_signal_connect_swapped (de->data_store, "case-inserted",
965 G_CALLBACK (set_unsaved), de);
967 g_signal_connect_swapped (de->data_store, "cases-deleted",
968 G_CALLBACK (set_unsaved), de);
970 dataset_set_callbacks (de->dataset, &cbs, de);
972 connect_help (de->builder);
974 gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
975 gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
976 gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
977 gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
979 gtk_container_add (GTK_CONTAINER (de), box);
981 g_signal_connect (de->dict, "weight-changed",
982 G_CALLBACK (on_weight_change),
985 g_signal_connect (de->dict, "filter-changed",
986 G_CALLBACK (on_filter_change),
989 g_signal_connect (de->dict, "split-changed",
990 G_CALLBACK (on_split_change),
993 g_signal_connect_swapped (de->dict, "backend-changed",
994 G_CALLBACK (enable_save), de);
995 g_signal_connect_swapped (de->dict, "variable-inserted",
996 G_CALLBACK (enable_save), de);
997 g_signal_connect_swapped (de->dict, "variable-deleted",
998 G_CALLBACK (enable_save), de);
1001 connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1002 connect_action (de, "file_import", G_CALLBACK (text_data_import_assistant));
1003 connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1004 connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1005 connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1006 connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1007 connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1008 connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1010 g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1012 connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1013 connect_action (de, "transform_autorecode", G_CALLBACK (autorecode_dialog));
1014 connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1015 connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1016 connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1017 connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1018 connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1021 GtkWidget *recent_data =
1022 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-data");
1024 GtkWidget *recent_files =
1025 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-files");
1028 GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1029 gtk_recent_manager_get_default ());
1031 GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1032 gtk_recent_manager_get_default ());
1034 g_object_set (menu_data, "show-tips", TRUE, NULL);
1035 g_object_set (menu_files, "show-tips", TRUE, NULL);
1038 GtkRecentFilter *filter = gtk_recent_filter_new ();
1040 gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1041 gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1043 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1045 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1048 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1051 g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1054 GtkRecentFilter *filter = gtk_recent_filter_new ();
1056 gtk_recent_filter_add_pattern (filter, "*.sps");
1057 gtk_recent_filter_add_pattern (filter, "*.SPS");
1059 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1061 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1064 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1066 g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1070 connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1073 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1074 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1076 connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1078 connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1080 connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1082 connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1084 connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1086 connect_action (de, "file_quit", G_CALLBACK (file_quit));
1088 connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1090 connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1092 g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1094 gtk_menu_shell_append (GTK_MENU_SHELL (menubar), create_help_menu (GTK_WINDOW (de)));
1096 g_signal_connect (de->data_editor, "notify::ui-manager",
1097 G_CALLBACK (on_ui_manager_changed), de);
1098 on_ui_manager_changed (de->data_editor, NULL, de);
1100 gtk_widget_show (GTK_WIDGET (de->data_editor));
1101 gtk_widget_show (box);
1103 ll_push_head (&all_data_windows, &de->ll);
1107 psppire_data_window_dispose (GObject *object)
1109 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1113 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
1114 g_object_unref (dw->uim);
1118 if (dw->builder != NULL)
1120 g_object_unref (dw->builder);
1126 g_signal_handlers_disconnect_by_func (dw->dict,
1127 G_CALLBACK (enable_save), dw);
1128 g_signal_handlers_disconnect_by_func (dw->dict,
1129 G_CALLBACK (on_weight_change), dw);
1130 g_signal_handlers_disconnect_by_func (dw->dict,
1131 G_CALLBACK (on_filter_change), dw);
1132 g_signal_handlers_disconnect_by_func (dw->dict,
1133 G_CALLBACK (on_split_change), dw);
1135 g_object_unref (dw->dict);
1141 g_object_unref (dw->data_store);
1142 dw->data_store = NULL;
1145 if (dw->ll.next != NULL)
1147 ll_remove (&dw->ll);
1151 if (G_OBJECT_CLASS (parent_class)->dispose)
1152 G_OBJECT_CLASS (parent_class)->dispose (object);
1156 psppire_data_window_finalize (GObject *object)
1158 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1162 struct dataset *dataset = dw->dataset;
1163 struct session *session = dataset_session (dataset);
1167 dataset_set_callbacks (dataset, NULL, NULL);
1168 session_set_active_dataset (session, NULL);
1169 dataset_destroy (dataset);
1172 if (G_OBJECT_CLASS (parent_class)->finalize)
1173 G_OBJECT_CLASS (parent_class)->finalize (object);
1177 psppire_data_window_set_property (GObject *object,
1179 const GValue *value,
1182 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1187 psppire_data_window_finish_init (window, g_value_get_pointer (value));
1190 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1196 psppire_data_window_get_property (GObject *object,
1201 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1206 g_value_set_pointer (value, window->dataset);
1209 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1215 psppire_data_window_add_ui (PsppireDataWindow *pdw, GtkUIManager *uim)
1221 ui_string = gtk_ui_manager_get_ui (uim);
1222 merge_id = gtk_ui_manager_add_ui_from_string (pdw->ui_manager, ui_string,
1226 g_return_val_if_fail (merge_id != 0, 0);
1228 list = gtk_ui_manager_get_action_groups (uim);
1229 for (; list != NULL; list = list->next)
1231 GtkActionGroup *action_group = list->data;
1232 GList *actions = gtk_action_group_list_actions (action_group);
1235 for (action = actions; action != NULL; action = action->next)
1237 GtkAction *a = action->data;
1239 if (PSPPIRE_IS_DIALOG_ACTION (a))
1240 g_object_set (a, "manager", pdw->ui_manager, NULL);
1243 gtk_ui_manager_insert_action_group (pdw->ui_manager, action_group, 0);
1246 gtk_window_add_accel_group (GTK_WINDOW (pdw),
1247 gtk_ui_manager_get_accel_group (uim));
1253 psppire_data_window_remove_ui (PsppireDataWindow *pdw,
1254 GtkUIManager *uim, guint merge_id)
1258 g_return_if_fail (merge_id != 0);
1260 gtk_ui_manager_remove_ui (pdw->ui_manager, merge_id);
1262 list = gtk_ui_manager_get_action_groups (uim);
1263 for (; list != NULL; list = list->next)
1265 GtkActionGroup *action_group = list->data;
1266 gtk_ui_manager_remove_action_group (pdw->ui_manager, action_group);
1269 gtk_window_remove_accel_group (GTK_WINDOW (pdw),
1270 gtk_ui_manager_get_accel_group (uim));
1274 psppire_data_window_new (struct dataset *ds)
1278 if (the_session == NULL)
1279 the_session = session_create (NULL);
1283 char *dataset_name = session_generate_dataset_name (the_session);
1284 ds = dataset_create (the_session, dataset_name);
1285 free (dataset_name);
1287 assert (dataset_session (ds) == the_session);
1291 psppire_data_window_get_type (),
1292 "description", _("Data Editor"),
1296 if (dataset_name (ds) != NULL)
1297 g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1303 psppire_data_window_is_empty (PsppireDataWindow *dw)
1305 return psppire_dict_get_var_cnt (dw->dict) == 0;
1309 psppire_data_window_iface_init (PsppireWindowIface *iface)
1311 iface->save = save_file;
1312 iface->pick_filename = data_pick_filename;
1313 iface->load = load_file;
1317 psppire_default_data_window (void)
1319 if (ll_is_empty (&all_data_windows))
1320 create_data_window ();
1321 return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1325 psppire_data_window_set_default (PsppireDataWindow *pdw)
1327 ll_remove (&pdw->ll);
1328 ll_push_head (&all_data_windows, &pdw->ll);
1332 psppire_data_window_undefault (PsppireDataWindow *pdw)
1334 ll_remove (&pdw->ll);
1335 ll_push_tail (&all_data_windows, &pdw->ll);
1339 psppire_data_window_for_dataset (struct dataset *ds)
1341 PsppireDataWindow *pdw;
1343 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1344 if (pdw->dataset == ds)
1351 psppire_data_window_for_data_store (PsppireDataStore *data_store)
1353 PsppireDataWindow *pdw;
1355 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1356 if (pdw->data_store == data_store)
1363 create_data_window (void)
1365 gtk_widget_show (psppire_data_window_new (NULL));
1369 open_data_window (PsppireWindow *victim, const char *file_name,
1370 const char *encoding, gpointer hint)
1374 if (PSPPIRE_IS_DATA_WINDOW (victim)
1375 && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1377 window = GTK_WIDGET (victim);
1378 gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
1381 window = psppire_data_window_new (NULL);
1383 psppire_window_load (PSPPIRE_WINDOW (window), file_name, encoding, hint);
1384 gtk_widget_show_all (window);