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 GtkWidget *page_menu_item;
847 is_ds = pagenum == PSPPIRE_DATA_EDITOR_DATA_VIEW;
849 ? "/ui/menubar/view/view_data"
850 : "/ui/menubar/view/view_variables");
851 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, "data_aggregate", G_CALLBACK (aggregate_dialog));
1014 connect_action (de, "transform_autorecode", G_CALLBACK (autorecode_dialog));
1015 connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1016 connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1017 connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1018 connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1019 connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1022 GtkWidget *recent_data =
1023 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-data");
1025 GtkWidget *recent_files =
1026 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-files");
1029 GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1030 gtk_recent_manager_get_default ());
1032 GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1033 gtk_recent_manager_get_default ());
1035 g_object_set (menu_data, "show-tips", TRUE, NULL);
1036 g_object_set (menu_files, "show-tips", TRUE, NULL);
1039 GtkRecentFilter *filter = gtk_recent_filter_new ();
1041 gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1042 gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1044 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1046 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1049 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1052 g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1055 GtkRecentFilter *filter = gtk_recent_filter_new ();
1057 gtk_recent_filter_add_pattern (filter, "*.sps");
1058 gtk_recent_filter_add_pattern (filter, "*.SPS");
1060 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1062 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1065 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1067 g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1071 connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1074 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1075 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1077 connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1079 connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1081 connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1083 connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1085 connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1087 connect_action (de, "file_quit", G_CALLBACK (file_quit));
1089 connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1091 connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1093 g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1095 gtk_menu_shell_append (GTK_MENU_SHELL (menubar), create_help_menu (GTK_WINDOW (de)));
1097 g_signal_connect (de->data_editor, "notify::ui-manager",
1098 G_CALLBACK (on_ui_manager_changed), de);
1099 on_ui_manager_changed (de->data_editor, NULL, de);
1101 gtk_widget_show (GTK_WIDGET (de->data_editor));
1102 gtk_widget_show (box);
1104 ll_push_head (&all_data_windows, &de->ll);
1108 psppire_data_window_dispose (GObject *object)
1110 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1114 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
1115 g_object_unref (dw->uim);
1119 if (dw->builder != NULL)
1121 g_object_unref (dw->builder);
1127 g_signal_handlers_disconnect_by_func (dw->dict,
1128 G_CALLBACK (enable_save), dw);
1129 g_signal_handlers_disconnect_by_func (dw->dict,
1130 G_CALLBACK (on_weight_change), dw);
1131 g_signal_handlers_disconnect_by_func (dw->dict,
1132 G_CALLBACK (on_filter_change), dw);
1133 g_signal_handlers_disconnect_by_func (dw->dict,
1134 G_CALLBACK (on_split_change), dw);
1136 g_object_unref (dw->dict);
1142 g_object_unref (dw->data_store);
1143 dw->data_store = NULL;
1146 if (dw->ll.next != NULL)
1148 ll_remove (&dw->ll);
1152 if (G_OBJECT_CLASS (parent_class)->dispose)
1153 G_OBJECT_CLASS (parent_class)->dispose (object);
1157 psppire_data_window_finalize (GObject *object)
1159 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1163 struct dataset *dataset = dw->dataset;
1164 struct session *session = dataset_session (dataset);
1168 dataset_set_callbacks (dataset, NULL, NULL);
1169 session_set_active_dataset (session, NULL);
1170 dataset_destroy (dataset);
1173 if (G_OBJECT_CLASS (parent_class)->finalize)
1174 G_OBJECT_CLASS (parent_class)->finalize (object);
1178 psppire_data_window_set_property (GObject *object,
1180 const GValue *value,
1183 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1188 psppire_data_window_finish_init (window, g_value_get_pointer (value));
1191 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1197 psppire_data_window_get_property (GObject *object,
1202 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1207 g_value_set_pointer (value, window->dataset);
1210 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1216 psppire_data_window_add_ui (PsppireDataWindow *pdw, GtkUIManager *uim)
1222 ui_string = gtk_ui_manager_get_ui (uim);
1223 merge_id = gtk_ui_manager_add_ui_from_string (pdw->ui_manager, ui_string,
1227 g_return_val_if_fail (merge_id != 0, 0);
1229 list = gtk_ui_manager_get_action_groups (uim);
1230 for (; list != NULL; list = list->next)
1232 GtkActionGroup *action_group = list->data;
1233 GList *actions = gtk_action_group_list_actions (action_group);
1236 for (action = actions; action != NULL; action = action->next)
1238 GtkAction *a = action->data;
1240 if (PSPPIRE_IS_DIALOG_ACTION (a))
1241 g_object_set (a, "manager", pdw->ui_manager, NULL);
1244 gtk_ui_manager_insert_action_group (pdw->ui_manager, action_group, 0);
1247 gtk_window_add_accel_group (GTK_WINDOW (pdw),
1248 gtk_ui_manager_get_accel_group (uim));
1254 psppire_data_window_remove_ui (PsppireDataWindow *pdw,
1255 GtkUIManager *uim, guint merge_id)
1259 g_return_if_fail (merge_id != 0);
1261 gtk_ui_manager_remove_ui (pdw->ui_manager, merge_id);
1263 list = gtk_ui_manager_get_action_groups (uim);
1264 for (; list != NULL; list = list->next)
1266 GtkActionGroup *action_group = list->data;
1267 gtk_ui_manager_remove_action_group (pdw->ui_manager, action_group);
1270 gtk_window_remove_accel_group (GTK_WINDOW (pdw),
1271 gtk_ui_manager_get_accel_group (uim));
1275 psppire_data_window_new (struct dataset *ds)
1279 if (the_session == NULL)
1280 the_session = session_create (NULL);
1284 char *dataset_name = session_generate_dataset_name (the_session);
1285 ds = dataset_create (the_session, dataset_name);
1286 free (dataset_name);
1288 assert (dataset_session (ds) == the_session);
1292 psppire_data_window_get_type (),
1293 "description", _("Data Editor"),
1297 if (dataset_name (ds) != NULL)
1298 g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1304 psppire_data_window_is_empty (PsppireDataWindow *dw)
1306 return psppire_dict_get_var_cnt (dw->dict) == 0;
1310 psppire_data_window_iface_init (PsppireWindowIface *iface)
1312 iface->save = save_file;
1313 iface->pick_filename = data_pick_filename;
1314 iface->load = load_file;
1318 psppire_default_data_window (void)
1320 if (ll_is_empty (&all_data_windows))
1321 create_data_window ();
1322 return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1326 psppire_data_window_set_default (PsppireDataWindow *pdw)
1328 ll_remove (&pdw->ll);
1329 ll_push_head (&all_data_windows, &pdw->ll);
1333 psppire_data_window_undefault (PsppireDataWindow *pdw)
1335 ll_remove (&pdw->ll);
1336 ll_push_tail (&all_data_windows, &pdw->ll);
1340 psppire_data_window_for_dataset (struct dataset *ds)
1342 PsppireDataWindow *pdw;
1344 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1345 if (pdw->dataset == ds)
1352 psppire_data_window_for_data_store (PsppireDataStore *data_store)
1354 PsppireDataWindow *pdw;
1356 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1357 if (pdw->data_store == data_store)
1364 create_data_window (void)
1366 gtk_widget_show (psppire_data_window_new (NULL));
1370 open_data_window (PsppireWindow *victim, const char *file_name,
1371 const char *encoding, gpointer hint)
1375 if (PSPPIRE_IS_DATA_WINDOW (victim)
1376 && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1378 window = GTK_WIDGET (victim);
1379 gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
1382 window = psppire_data_window_new (NULL);
1384 psppire_window_load (PSPPIRE_WINDOW (window), file_name, encoding, hint);
1385 gtk_widget_show_all (window);