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/aggregate-dialog.h"
28 #include "ui/gui/autorecode-dialog.h"
29 #include "ui/gui/builder-wrapper.h"
30 #include "ui/gui/comments-dialog.h"
31 #include "ui/gui/entry-dialog.h"
32 #include "ui/gui/executor.h"
33 #include "ui/gui/help-menu.h"
34 #include "ui/gui/helper.h"
35 #include "ui/gui/helper.h"
36 #include "ui/gui/psppire-data-window.h"
37 #include "ui/gui/psppire-dialog-action.h"
38 #include "ui/gui/psppire-encoding-selector.h"
39 #include "ui/gui/psppire-syntax-window.h"
40 #include "ui/gui/psppire-window.h"
41 #include "ui/gui/psppire.h"
42 #include "ui/gui/recode-dialog.h"
43 #include "ui/gui/select-cases-dialog.h"
44 #include "ui/gui/split-file-dialog.h"
45 #include "ui/gui/text-data-import-dialog.h"
46 #include "ui/gui/weight-cases-dialog.h"
47 #include "ui/syntax-gen.h"
49 #include "gl/c-strcase.h"
50 #include "gl/c-strcasestr.h"
51 #include "gl/xvasprintf.h"
54 #define _(msgid) gettext (msgid)
55 #define N_(msgid) msgid
57 struct session *the_session;
58 struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
60 static void psppire_data_window_class_init (PsppireDataWindowClass *class);
61 static void psppire_data_window_init (PsppireDataWindow *data_editor);
64 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
66 static void psppire_data_window_dispose (GObject *object);
67 static void psppire_data_window_finalize (GObject *object);
68 static void psppire_data_window_set_property (GObject *object,
72 static void psppire_data_window_get_property (GObject *object,
77 static guint psppire_data_window_add_ui (PsppireDataWindow *, GtkUIManager *);
78 static void psppire_data_window_remove_ui (PsppireDataWindow *,
79 GtkUIManager *, guint);
82 psppire_data_window_get_type (void)
84 static GType psppire_data_window_type = 0;
86 if (!psppire_data_window_type)
88 static const GTypeInfo psppire_data_window_info =
90 sizeof (PsppireDataWindowClass),
93 (GClassInitFunc)psppire_data_window_class_init,
94 (GClassFinalizeFunc) NULL,
96 sizeof (PsppireDataWindow),
98 (GInstanceInitFunc) psppire_data_window_init,
101 static const GInterfaceInfo window_interface_info =
103 (GInterfaceInitFunc) psppire_data_window_iface_init,
108 psppire_data_window_type =
109 g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
110 &psppire_data_window_info, 0);
113 g_type_add_interface_static (psppire_data_window_type,
114 PSPPIRE_TYPE_WINDOW_MODEL,
115 &window_interface_info);
118 return psppire_data_window_type;
121 static GObjectClass *parent_class ;
128 psppire_data_window_class_init (PsppireDataWindowClass *class)
130 GObjectClass *object_class = G_OBJECT_CLASS (class);
132 parent_class = g_type_class_peek_parent (class);
134 object_class->dispose = psppire_data_window_dispose;
135 object_class->finalize = psppire_data_window_finalize;
136 object_class->set_property = psppire_data_window_set_property;
137 object_class->get_property = psppire_data_window_get_property;
139 g_object_class_install_property (
140 object_class, PROP_DATASET,
141 g_param_spec_pointer ("dataset", "Dataset",
142 "'struct datset *' represented by the window",
143 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
146 /* Run the EXECUTE command. */
148 execute (PsppireDataWindow *dw)
150 execute_const_syntax_string (dw, "EXECUTE.");
154 transformation_change_callback (bool transformations_pending,
157 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
159 GtkWidget *status_label =
160 get_widget_assert (de->builder, "case-counter-area");
162 { /* Set the sensitivity of the "Transformations Pending" menuitem */
164 GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
166 GtkWidget *menuitem =
167 gtk_ui_manager_get_widget (uim,"/ui/menubar/transform/transform_run-pending");
169 gtk_widget_set_sensitive (menuitem, transformations_pending);
173 if ( transformations_pending)
174 gtk_label_set_text (GTK_LABEL (status_label),
175 _("Transformations Pending"));
177 gtk_label_set_text (GTK_LABEL (status_label), "");
180 /* Callback for when the dictionary changes its filter variable */
182 on_filter_change (GObject *o, gint filter_index, gpointer data)
184 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
186 GtkWidget *filter_status_area =
187 get_widget_assert (de->builder, "filter-use-status-area");
189 if ( filter_index == -1 )
191 gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
195 PsppireDict *dict = NULL;
196 struct variable *var ;
199 g_object_get (de->data_editor, "dictionary", &dict, NULL);
201 var = psppire_dict_get_variable (dict, filter_index);
203 text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
205 gtk_label_set_text (GTK_LABEL (filter_status_area), text);
211 /* Callback for when the dictionary changes its split variables */
213 on_split_change (PsppireDict *dict, gpointer data)
215 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
217 size_t n_split_vars = dict_get_split_cnt (dict->dict);
219 GtkWidget *split_status_area =
220 get_widget_assert (de->builder, "split-file-status-area");
222 if ( n_split_vars == 0 )
224 gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
230 const struct variable *const * split_vars =
231 dict_get_split_vars (dict->dict);
233 text = g_string_new (_("Split by "));
235 for (i = 0 ; i < n_split_vars - 1; ++i )
237 g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
239 g_string_append (text, var_get_name (split_vars[i]));
241 gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
243 g_string_free (text, TRUE);
250 /* Callback for when the dictionary changes its weights */
252 on_weight_change (GObject *o, gint weight_index, gpointer data)
254 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
256 GtkWidget *weight_status_area =
257 get_widget_assert (de->builder, "weight-status-area");
259 if ( weight_index == -1 )
261 gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
265 struct variable *var ;
266 PsppireDict *dict = NULL;
269 g_object_get (de->data_editor, "dictionary", &dict, NULL);
271 var = psppire_dict_get_variable (dict, weight_index);
273 text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
275 gtk_label_set_text (GTK_LABEL (weight_status_area), text);
283 dump_rm (GtkRecentManager *rm)
285 GList *items = gtk_recent_manager_get_items (rm);
289 g_print ("Recent Items:\n");
290 for (i = items; i; i = i->next)
292 GtkRecentInfo *ri = i->data;
294 g_print ("Item: %s (Mime: %s) (Desc: %s) (URI: %s)\n",
295 gtk_recent_info_get_short_name (ri),
296 gtk_recent_info_get_mime_type (ri),
297 gtk_recent_info_get_description (ri),
298 gtk_recent_info_get_uri (ri)
302 gtk_recent_info_unref (ri);
310 has_suffix (const gchar *name, const gchar *suffix)
312 size_t name_len = strlen (name);
313 size_t suffix_len = strlen (suffix);
314 return (name_len > suffix_len
315 && !c_strcasecmp (&name[name_len - suffix_len], suffix));
319 name_has_por_suffix (const gchar *name)
321 return has_suffix (name, ".por");
325 name_has_sav_suffix (const gchar *name)
327 return has_suffix (name, ".sav") || has_suffix (name, ".zsav");
330 /* Returns true if NAME has a suffix which might denote a PSPP file */
332 name_has_suffix (const gchar *name)
334 return name_has_por_suffix (name) || name_has_sav_suffix (name);
338 load_file (PsppireWindow *de, const gchar *file_name, const char *encoding,
341 const char *mime_type = NULL;
342 gchar *syntax = NULL;
347 gchar *utf8_file_name;
348 struct string filename;
350 utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
352 if (NULL == utf8_file_name)
355 ds_init_empty (&filename);
356 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
358 g_free (utf8_file_name);
360 if (encoding && encoding[0])
361 syntax = g_strdup_printf ("GET FILE=%s ENCODING='%s'.",
362 ds_cstr (&filename), encoding);
364 syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
365 ds_destroy (&filename);
372 ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
373 lex_reader_for_string (syntax));
376 if (ok && syn == NULL)
378 if (name_has_por_suffix (file_name))
379 mime_type = "application/x-spss-por";
380 else if (name_has_sav_suffix (file_name))
381 mime_type = "application/x-spss-sav";
383 add_most_recent (file_name, mime_type, encoding);
390 psppire_data_window_format_to_string (enum PsppireDataWindowFormat format)
392 if (format == PSPPIRE_DATA_WINDOW_SAV)
394 else if (format == PSPPIRE_DATA_WINDOW_ZSAV)
400 /* Save DE to file */
402 save_file (PsppireWindow *w)
404 const gchar *file_name = NULL;
405 gchar *utf8_file_name = NULL;
407 struct string filename ;
408 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
411 file_name = psppire_window_get_filename (w);
413 fnx = g_string_new (file_name);
415 if ( ! name_has_suffix (fnx->str))
416 g_string_append (fnx, psppire_data_window_format_to_string (de->format));
418 ds_init_empty (&filename);
420 utf8_file_name = g_filename_to_utf8 (fnx->str, -1, NULL, NULL, NULL);
422 g_string_free (fnx, TRUE);
424 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
425 g_free (utf8_file_name);
427 if (de->format == PSPPIRE_DATA_WINDOW_SAV)
428 syntax = g_strdup_printf ("SAVE OUTFILE=%s.", ds_cstr (&filename));
429 else if (de->format == PSPPIRE_DATA_WINDOW_ZSAV)
430 syntax = g_strdup_printf ("SAVE /ZCOMPRESSED /OUTFILE=%s.",
431 ds_cstr (&filename));
433 syntax = g_strdup_printf ("EXPORT OUTFILE=%s.", ds_cstr (&filename));
435 ds_destroy (&filename);
437 g_free (execute_syntax_string (de, syntax));
442 display_dict (PsppireDataWindow *de)
444 execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
448 sysfile_info (PsppireDataWindow *de)
450 GtkWidget *dialog = psppire_window_file_chooser_dialog (PSPPIRE_WINDOW (de));
452 if ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
454 struct string filename;
456 gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
457 gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
460 const gchar *encoding = psppire_encoding_selector_get_encoding (
461 gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
465 ds_init_empty (&filename);
467 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
469 g_free (utf8_file_name);
472 syntax = g_strdup_printf ("SYSFILE INFO %s ENCODING='%s'.",
473 ds_cstr (&filename), encoding);
475 syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
476 g_free (execute_syntax_string (de, syntax));
479 gtk_widget_destroy (dialog);
483 /* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
485 data_pick_filename (PsppireWindow *window)
487 GtkListStore *list_store;
488 GtkWidget *combo_box;
490 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
491 GtkFileFilter *filter;
493 gtk_file_chooser_dialog_new (_("Save"),
495 GTK_FILE_CHOOSER_ACTION_SAVE,
496 _("Cancel"), GTK_RESPONSE_CANCEL,
497 _("Save"), GTK_RESPONSE_ACCEPT,
500 g_object_set (dialog, "local-only", FALSE, NULL);
502 filter = gtk_file_filter_new ();
503 gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
504 gtk_file_filter_add_mime_type (filter, "application/x-spss-sav");
505 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
507 filter = gtk_file_filter_new ();
508 gtk_file_filter_set_name (filter, _("Compressed System Files (*.zsav)"));
509 gtk_file_filter_add_pattern (filter, "*.zsav");
510 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
512 filter = gtk_file_filter_new ();
513 gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
514 gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
515 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
517 filter = gtk_file_filter_new ();
518 gtk_file_filter_set_name (filter, _("All Files"));
519 gtk_file_filter_add_pattern (filter, "*");
520 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
521 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
524 GtkCellRenderer *cell;
529 list_store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
530 combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store));
532 gtk_list_store_append (list_store, &iter);
533 gtk_list_store_set (list_store, &iter,
534 0, PSPPIRE_DATA_WINDOW_SAV,
537 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
539 gtk_list_store_append (list_store, &iter);
540 gtk_list_store_set (list_store, &iter,
541 0, PSPPIRE_DATA_WINDOW_ZSAV,
542 1, _("Compressed System File"),
545 gtk_list_store_append (list_store, &iter);
546 gtk_list_store_set (list_store, &iter,
547 0, PSPPIRE_DATA_WINDOW_POR,
548 1, _("Portable File"),
551 label = gtk_label_new (_("Format:"));
553 cell = gtk_cell_renderer_text_new ();
554 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, FALSE);
555 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box), cell,
558 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
559 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
560 gtk_box_pack_start (GTK_BOX (hbox), combo_box, FALSE, FALSE, 0);
561 gtk_widget_show_all (hbox);
563 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), hbox);
566 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
569 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
571 case GTK_RESPONSE_ACCEPT:
576 gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
582 gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter);
583 gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter,
588 if ( ! name_has_suffix (filename->str))
589 g_string_append (filename,
590 psppire_data_window_format_to_string (format));
592 psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
594 g_string_free (filename, TRUE);
601 gtk_widget_destroy (dialog);
605 confirm_delete_dataset (PsppireDataWindow *de,
606 const char *old_dataset,
607 const char *new_dataset,
608 const char *existing_dataset)
613 dialog = gtk_message_dialog_new (
614 GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
615 _("Delete Existing Dataset?"));
617 gtk_message_dialog_format_secondary_text (
618 GTK_MESSAGE_DIALOG (dialog),
619 _("Renaming \"%s\" to \"%s\" will destroy the existing "
620 "dataset named \"%s\". Are you sure that you want to do this?"),
621 old_dataset, new_dataset, existing_dataset);
623 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
624 _("Cancel"), GTK_RESPONSE_CANCEL,
625 _("Delete"), GTK_RESPONSE_OK,
628 g_object_set (dialog, "icon-name", "pspp", NULL);
630 result = gtk_dialog_run (GTK_DIALOG (dialog));
632 gtk_widget_destroy (dialog);
634 return result == GTK_RESPONSE_OK;
638 on_rename_dataset (PsppireDataWindow *de)
640 struct dataset *ds = de->dataset;
641 struct session *session = dataset_session (ds);
642 const char *old_name = dataset_name (ds);
643 struct dataset *existing_dataset;
647 prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
649 new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
653 if (new_name == NULL)
656 existing_dataset = session_lookup_dataset (session, new_name);
657 if (existing_dataset == NULL || existing_dataset == ds
658 || confirm_delete_dataset (de, old_name, new_name,
659 dataset_name (existing_dataset)))
660 g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
667 status_bar_activate (PsppireDataWindow *de, GtkToggleAction *action)
669 GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
671 if ( gtk_toggle_action_get_active (action))
672 gtk_widget_show (statusbar);
674 gtk_widget_hide (statusbar);
679 grid_lines_activate (PsppireDataWindow *de, GtkToggleAction *action)
681 const gboolean grid_visible = gtk_toggle_action_get_active (action);
683 psppire_data_editor_show_grid (de->data_editor, grid_visible);
687 data_view_activate (PsppireDataWindow *de)
689 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
694 variable_view_activate (PsppireDataWindow *de)
696 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
701 fonts_activate (PsppireDataWindow *de)
703 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
704 GtkWidget *dialog = gtk_font_chooser_dialog_new (NULL, GTK_WINDOW (toplevel));
705 GtkStyle *style = gtk_widget_get_style (GTK_WIDGET(de->data_editor));
706 PangoFontDescription *current_font = style->font_desc;
708 gtk_font_chooser_set_font_desc (GTK_FONT_CHOOSER (dialog), current_font);
710 gtk_window_set_transient_for (GTK_WINDOW (dialog),
711 GTK_WINDOW (toplevel));
713 if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
715 PangoFontDescription* font_desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (dialog));
717 psppire_data_editor_set_font (de->data_editor, font_desc);
720 gtk_widget_hide (dialog);
725 /* Callback for the value labels action */
727 toggle_value_labels (PsppireDataWindow *de, GtkToggleAction *ta)
729 g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
733 toggle_split_window (PsppireDataWindow *de, GtkToggleAction *ta)
735 psppire_data_editor_split_window (de->data_editor,
736 gtk_toggle_action_get_active (ta));
741 file_quit (PsppireDataWindow *de)
743 /* FIXME: Need to be more intelligent here.
744 Give the user the opportunity to save any unsaved data.
750 on_recent_data_select (GtkMenuShell *menushell,
751 PsppireWindow *window)
756 gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
758 file = g_filename_from_uri (uri, NULL, NULL);
762 open_data_window (window, file, NULL, NULL);
768 charset_from_mime_type (const char *mime_type)
774 if (mime_type == NULL)
777 charset = c_strcasestr (mime_type, "charset=");
785 /* Parse a "quoted-string" as defined by RFC 822. */
786 for (p++; *p != '\0' && *p != '"'; p++)
789 ds_put_byte (&s, *p);
790 else if (*++p != '\0')
791 ds_put_byte (&s, *p);
796 /* Parse a "token" as defined by RFC 2045. */
797 while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
798 ds_put_byte (&s, *p++);
800 if (!ds_is_empty (&s))
801 return ds_steal_cstr (&s);
808 on_recent_files_select (GtkMenuShell *menushell, gpointer user_data)
815 /* Get the file name and its encoding. */
816 item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
817 file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
818 encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
819 gtk_recent_info_unref (item);
821 se = psppire_syntax_window_new (encoding);
825 if ( psppire_window_load (PSPPIRE_WINDOW (se), file, encoding, NULL) )
826 gtk_widget_show (se);
828 gtk_widget_destroy (se);
834 set_unsaved (gpointer w)
836 psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
840 on_switch_page (PsppireDataEditor *de, gpointer p,
841 gint pagenum, PsppireDataWindow *dw)
843 /* Set the appropriate ui_manager according to the selected page.
844 This is necessary, because the menus for the variable view and
845 the data view are different (slightly). */
847 gboolean is_ds = pagenum == PSPPIRE_DATA_EDITOR_DATA_VIEW;
848 const char *path = (is_ds
849 ? "/ui/menubar/view/view_data"
850 : "/ui/menubar/view/view_variables");
852 GtkWidget *page_menu_item = gtk_ui_manager_get_widget (dw->ui_manager, path);
853 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (page_menu_item), TRUE);
857 on_ui_manager_changed (PsppireDataEditor *de,
858 GParamSpec *pspec UNUSED,
859 PsppireDataWindow *dw)
861 GtkUIManager *uim = psppire_data_editor_get_ui_manager (de);
867 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
868 g_object_unref (dw->uim);
875 g_object_ref (dw->uim);
876 dw->merge_id = psppire_data_window_add_ui (dw, dw->uim);
880 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
881 Returns a pointer to the action
884 connect_action (PsppireDataWindow *dw, const char *action_name,
887 GtkAction *action = get_action_assert (dw->builder, action_name);
889 g_signal_connect_swapped (action, "activate", handler, dw);
894 /* Only a data file with at least one variable can be saved. */
896 enable_save (PsppireDataWindow *dw)
898 gboolean enable = psppire_dict_get_var_cnt (dw->dict) > 0;
900 gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save"),
902 gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save_as"),
906 /* Initializes as much of a PsppireDataWindow as we can and must before the
907 dataset has been set.
909 In particular, the 'menu' member is required in case the "filename" property
910 is set before the "dataset" property: otherwise PsppireWindow will try to
911 modify the menu as part of the "filename" property_set() function and end up
912 with a Gtk-CRITICAL since 'menu' is NULL. */
914 psppire_data_window_init (PsppireDataWindow *de)
917 de->builder = builder_new ("data-editor.ui");
919 de->ui_manager = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
921 w = gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/windows/windows_minimise_all");
923 PSPPIRE_WINDOW (de)->menu = GTK_MENU_SHELL (gtk_widget_get_parent (w));
930 psppire_data_window_finish_init (PsppireDataWindow *de,
933 static const struct dataset_callbacks cbs =
935 set_unsaved, /* changed */
936 transformation_change_callback, /* transformations_changed */
943 GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
946 de->dict = psppire_dict_new_from_dict (dataset_dict (ds));
947 de->data_store = psppire_data_store_new (de->dict);
948 psppire_data_store_set_reader (de->data_store, NULL);
950 menubar = get_widget_assert (de->builder, "menubar");
951 hb = get_widget_assert (de->builder, "toolbar");
952 sb = get_widget_assert (de->builder, "status-bar");
958 PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store));
959 g_signal_connect (de->data_editor, "switch-page",
960 G_CALLBACK (on_switch_page), de);
962 g_signal_connect_swapped (de->data_store, "case-changed",
963 G_CALLBACK (set_unsaved), de);
965 g_signal_connect_swapped (de->data_store, "case-inserted",
966 G_CALLBACK (set_unsaved), de);
968 g_signal_connect_swapped (de->data_store, "cases-deleted",
969 G_CALLBACK (set_unsaved), de);
971 dataset_set_callbacks (de->dataset, &cbs, de);
973 connect_help (de->builder);
975 gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
976 gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
977 gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
978 gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
980 gtk_container_add (GTK_CONTAINER (de), box);
982 g_signal_connect (de->dict, "weight-changed",
983 G_CALLBACK (on_weight_change),
986 g_signal_connect (de->dict, "filter-changed",
987 G_CALLBACK (on_filter_change),
990 g_signal_connect (de->dict, "split-changed",
991 G_CALLBACK (on_split_change),
994 g_signal_connect_swapped (de->dict, "backend-changed",
995 G_CALLBACK (enable_save), de);
996 g_signal_connect_swapped (de->dict, "variable-inserted",
997 G_CALLBACK (enable_save), de);
998 g_signal_connect_swapped (de->dict, "variable-deleted",
999 G_CALLBACK (enable_save), de);
1002 connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1003 connect_action (de, "file_import", G_CALLBACK (text_data_import_assistant));
1004 connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1005 connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1006 connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1007 connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1008 connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1009 connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1011 g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1013 connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1014 connect_action (de, "data_aggregate", G_CALLBACK (aggregate_dialog));
1015 connect_action (de, "transform_autorecode", G_CALLBACK (autorecode_dialog));
1016 connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1017 connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1018 connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1019 connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1020 connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1023 GtkWidget *recent_data =
1024 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-data");
1026 GtkWidget *recent_files =
1027 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-files");
1030 GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1031 gtk_recent_manager_get_default ());
1033 GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1034 gtk_recent_manager_get_default ());
1036 g_object_set (menu_data, "show-tips", TRUE, NULL);
1037 g_object_set (menu_files, "show-tips", TRUE, NULL);
1040 GtkRecentFilter *filter = gtk_recent_filter_new ();
1042 gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1043 gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1045 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1047 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1050 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1053 g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1056 GtkRecentFilter *filter = gtk_recent_filter_new ();
1058 gtk_recent_filter_add_pattern (filter, "*.sps");
1059 gtk_recent_filter_add_pattern (filter, "*.SPS");
1061 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1063 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1066 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1068 g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1072 connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1075 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1076 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1078 connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1080 connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1082 connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1084 connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1086 connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1088 connect_action (de, "file_quit", G_CALLBACK (file_quit));
1090 connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1092 connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1094 g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1096 gtk_menu_shell_append (GTK_MENU_SHELL (menubar), create_help_menu (GTK_WINDOW (de)));
1098 g_signal_connect (de->data_editor, "notify::ui-manager",
1099 G_CALLBACK (on_ui_manager_changed), de);
1100 on_ui_manager_changed (de->data_editor, NULL, de);
1102 gtk_widget_show (GTK_WIDGET (de->data_editor));
1103 gtk_widget_show (box);
1105 ll_push_head (&all_data_windows, &de->ll);
1109 psppire_data_window_dispose (GObject *object)
1111 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1115 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
1116 g_object_unref (dw->uim);
1120 if (dw->builder != NULL)
1122 g_object_unref (dw->builder);
1128 g_signal_handlers_disconnect_by_func (dw->dict,
1129 G_CALLBACK (enable_save), dw);
1130 g_signal_handlers_disconnect_by_func (dw->dict,
1131 G_CALLBACK (on_weight_change), dw);
1132 g_signal_handlers_disconnect_by_func (dw->dict,
1133 G_CALLBACK (on_filter_change), dw);
1134 g_signal_handlers_disconnect_by_func (dw->dict,
1135 G_CALLBACK (on_split_change), dw);
1137 g_object_unref (dw->dict);
1143 g_object_unref (dw->data_store);
1144 dw->data_store = NULL;
1147 if (dw->ll.next != NULL)
1149 ll_remove (&dw->ll);
1153 if (G_OBJECT_CLASS (parent_class)->dispose)
1154 G_OBJECT_CLASS (parent_class)->dispose (object);
1158 psppire_data_window_finalize (GObject *object)
1160 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1164 struct dataset *dataset = dw->dataset;
1165 struct session *session = dataset_session (dataset);
1169 dataset_set_callbacks (dataset, NULL, NULL);
1170 session_set_active_dataset (session, NULL);
1171 dataset_destroy (dataset);
1174 if (G_OBJECT_CLASS (parent_class)->finalize)
1175 G_OBJECT_CLASS (parent_class)->finalize (object);
1179 psppire_data_window_set_property (GObject *object,
1181 const GValue *value,
1184 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1189 psppire_data_window_finish_init (window, g_value_get_pointer (value));
1192 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1198 psppire_data_window_get_property (GObject *object,
1203 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1208 g_value_set_pointer (value, window->dataset);
1211 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1217 psppire_data_window_add_ui (PsppireDataWindow *pdw, GtkUIManager *uim)
1223 ui_string = gtk_ui_manager_get_ui (uim);
1224 merge_id = gtk_ui_manager_add_ui_from_string (pdw->ui_manager, ui_string,
1228 g_return_val_if_fail (merge_id != 0, 0);
1230 list = gtk_ui_manager_get_action_groups (uim);
1231 for (; list != NULL; list = list->next)
1233 GtkActionGroup *action_group = list->data;
1234 GList *actions = gtk_action_group_list_actions (action_group);
1237 for (action = actions; action != NULL; action = action->next)
1239 GtkAction *a = action->data;
1241 if (PSPPIRE_IS_DIALOG_ACTION (a))
1242 g_object_set (a, "manager", pdw->ui_manager, NULL);
1245 gtk_ui_manager_insert_action_group (pdw->ui_manager, action_group, 0);
1248 gtk_window_add_accel_group (GTK_WINDOW (pdw),
1249 gtk_ui_manager_get_accel_group (uim));
1255 psppire_data_window_remove_ui (PsppireDataWindow *pdw,
1256 GtkUIManager *uim, guint merge_id)
1260 g_return_if_fail (merge_id != 0);
1262 gtk_ui_manager_remove_ui (pdw->ui_manager, merge_id);
1264 list = gtk_ui_manager_get_action_groups (uim);
1265 for (; list != NULL; list = list->next)
1267 GtkActionGroup *action_group = list->data;
1268 gtk_ui_manager_remove_action_group (pdw->ui_manager, action_group);
1271 gtk_window_remove_accel_group (GTK_WINDOW (pdw),
1272 gtk_ui_manager_get_accel_group (uim));
1276 psppire_data_window_new (struct dataset *ds)
1280 if (the_session == NULL)
1281 the_session = session_create (NULL);
1285 char *dataset_name = session_generate_dataset_name (the_session);
1286 ds = dataset_create (the_session, dataset_name);
1287 free (dataset_name);
1289 assert (dataset_session (ds) == the_session);
1293 psppire_data_window_get_type (),
1294 "description", _("Data Editor"),
1298 if (dataset_name (ds) != NULL)
1299 g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1305 psppire_data_window_is_empty (PsppireDataWindow *dw)
1307 return psppire_dict_get_var_cnt (dw->dict) == 0;
1311 psppire_data_window_iface_init (PsppireWindowIface *iface)
1313 iface->save = save_file;
1314 iface->pick_filename = data_pick_filename;
1315 iface->load = load_file;
1319 psppire_default_data_window (void)
1321 if (ll_is_empty (&all_data_windows))
1322 create_data_window ();
1323 return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1327 psppire_data_window_set_default (PsppireDataWindow *pdw)
1329 ll_remove (&pdw->ll);
1330 ll_push_head (&all_data_windows, &pdw->ll);
1334 psppire_data_window_undefault (PsppireDataWindow *pdw)
1336 ll_remove (&pdw->ll);
1337 ll_push_tail (&all_data_windows, &pdw->ll);
1341 psppire_data_window_for_dataset (struct dataset *ds)
1343 PsppireDataWindow *pdw;
1345 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1346 if (pdw->dataset == ds)
1353 psppire_data_window_for_data_store (PsppireDataStore *data_store)
1355 PsppireDataWindow *pdw;
1357 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1358 if (pdw->data_store == data_store)
1365 create_data_window (void)
1367 gtk_widget_show (psppire_data_window_new (NULL));
1371 open_data_window (PsppireWindow *victim, const char *file_name,
1372 const char *encoding, gpointer hint)
1376 if (PSPPIRE_IS_DATA_WINDOW (victim)
1377 && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1379 window = GTK_WIDGET (victim);
1380 gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
1383 window = psppire_data_window_new (NULL);
1385 psppire_window_load (PSPPIRE_WINDOW (window), file_name, encoding, hint);
1386 gtk_widget_show_all (window);