1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
22 #include "data/dataset.h"
23 #include "data/session.h"
24 #include "language/lexer/lexer.h"
25 #include "libpspp/message.h"
26 #include "libpspp/str.h"
27 #include "ui/gui/builder-wrapper.h"
28 #include "ui/gui/entry-dialog.h"
29 #include "ui/gui/executor.h"
30 #include "ui/gui/help-menu.h"
31 #include "ui/gui/helper.h"
32 #include "ui/gui/helper.h"
33 #include "ui/gui/psppire-import-assistant.h"
34 #include "ui/gui/psppire-data-window.h"
35 #include "ui/gui/psppire-dialog-action.h"
36 #include "ui/gui/psppire-encoding-selector.h"
37 #include "ui/gui/psppire-syntax-window.h"
38 #include "ui/gui/psppire-window.h"
39 #include "ui/gui/windows-menu.h"
40 #include "ui/gui/psppire.h"
41 #include "ui/syntax-gen.h"
43 #include "gl/c-strcase.h"
44 #include "gl/c-strcasestr.h"
45 #include "gl/xvasprintf.h"
48 #define _(msgid) gettext (msgid)
49 #define N_(msgid) msgid
51 struct session *the_session;
52 struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
54 static void psppire_data_window_class_init (PsppireDataWindowClass *class);
55 static void psppire_data_window_init (PsppireDataWindow *data_editor);
58 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
60 static void psppire_data_window_dispose (GObject *object);
61 static void psppire_data_window_finalize (GObject *object);
62 static void psppire_data_window_set_property (GObject *object,
66 static void psppire_data_window_get_property (GObject *object,
71 static guint psppire_data_window_add_ui (PsppireDataWindow *, GtkUIManager *);
72 static void psppire_data_window_remove_ui (PsppireDataWindow *,
73 GtkUIManager *, guint);
76 psppire_data_window_get_type (void)
78 static GType psppire_data_window_type = 0;
80 if (!psppire_data_window_type)
82 static const GTypeInfo psppire_data_window_info =
84 sizeof (PsppireDataWindowClass),
87 (GClassInitFunc)psppire_data_window_class_init,
88 (GClassFinalizeFunc) NULL,
90 sizeof (PsppireDataWindow),
92 (GInstanceInitFunc) psppire_data_window_init,
95 static const GInterfaceInfo window_interface_info =
97 (GInterfaceInitFunc) psppire_data_window_iface_init,
102 psppire_data_window_type =
103 g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
104 &psppire_data_window_info, 0);
107 g_type_add_interface_static (psppire_data_window_type,
108 PSPPIRE_TYPE_WINDOW_MODEL,
109 &window_interface_info);
112 return psppire_data_window_type;
115 static GObjectClass *parent_class ;
122 psppire_data_window_class_init (PsppireDataWindowClass *class)
124 GObjectClass *object_class = G_OBJECT_CLASS (class);
126 parent_class = g_type_class_peek_parent (class);
128 object_class->dispose = psppire_data_window_dispose;
129 object_class->finalize = psppire_data_window_finalize;
130 object_class->set_property = psppire_data_window_set_property;
131 object_class->get_property = psppire_data_window_get_property;
133 g_object_class_install_property (
134 object_class, PROP_DATASET,
135 g_param_spec_pointer ("dataset", "Dataset",
136 "'struct datset *' represented by the window",
137 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
140 /* Run the EXECUTE command. */
142 execute (PsppireDataWindow *dw)
144 execute_const_syntax_string (dw, "EXECUTE.");
148 transformation_change_callback (bool transformations_pending,
151 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
153 GtkWidget *status_label =
154 get_widget_assert (de->builder, "case-counter-area");
156 { /* Set the sensitivity of the "Transformations Pending" menuitem */
158 GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
160 GtkWidget *menuitem =
161 gtk_ui_manager_get_widget (uim,"/ui/menubar/transform/transform_run-pending");
163 gtk_widget_set_sensitive (menuitem, transformations_pending);
167 if ( transformations_pending)
168 gtk_label_set_text (GTK_LABEL (status_label),
169 _("Transformations Pending"));
171 gtk_label_set_text (GTK_LABEL (status_label), "");
174 /* Callback for when the dictionary changes its filter variable */
176 on_filter_change (GObject *o, gint filter_index, gpointer data)
178 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
180 GtkWidget *filter_status_area =
181 get_widget_assert (de->builder, "filter-use-status-area");
183 if ( filter_index == -1 )
185 gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
189 PsppireDict *dict = NULL;
190 struct variable *var ;
193 g_object_get (de->data_editor, "dictionary", &dict, NULL);
195 var = psppire_dict_get_variable (dict, filter_index);
197 text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
199 gtk_label_set_text (GTK_LABEL (filter_status_area), text);
205 /* Callback for when the dictionary changes its split variables */
207 on_split_change (PsppireDict *dict, gpointer data)
209 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
211 size_t n_split_vars = dict_get_split_cnt (dict->dict);
213 GtkWidget *split_status_area =
214 get_widget_assert (de->builder, "split-file-status-area");
216 if ( n_split_vars == 0 )
218 gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
224 const struct variable *const * split_vars =
225 dict_get_split_vars (dict->dict);
227 text = g_string_new (_("Split by "));
229 for (i = 0 ; i < n_split_vars - 1; ++i )
231 g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
233 g_string_append (text, var_get_name (split_vars[i]));
235 gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
237 g_string_free (text, TRUE);
244 /* Callback for when the dictionary changes its weights */
246 on_weight_change (GObject *o, gint weight_index, gpointer data)
248 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
250 GtkWidget *weight_status_area =
251 get_widget_assert (de->builder, "weight-status-area");
253 if ( weight_index == -1 )
255 gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
259 struct variable *var ;
260 PsppireDict *dict = NULL;
263 g_object_get (de->data_editor, "dictionary", &dict, NULL);
265 var = psppire_dict_get_variable (dict, weight_index);
267 text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
269 gtk_label_set_text (GTK_LABEL (weight_status_area), text);
277 dump_rm (GtkRecentManager *rm)
279 GList *items = gtk_recent_manager_get_items (rm);
283 g_print ("Recent Items:\n");
284 for (i = items; i; i = i->next)
286 GtkRecentInfo *ri = i->data;
288 g_print ("Item: %s (Mime: %s) (Desc: %s) (URI: %s)\n",
289 gtk_recent_info_get_short_name (ri),
290 gtk_recent_info_get_mime_type (ri),
291 gtk_recent_info_get_description (ri),
292 gtk_recent_info_get_uri (ri)
296 gtk_recent_info_unref (ri);
304 has_suffix (const gchar *name, const gchar *suffix)
306 size_t name_len = strlen (name);
307 size_t suffix_len = strlen (suffix);
308 return (name_len > suffix_len
309 && !c_strcasecmp (&name[name_len - suffix_len], suffix));
313 name_has_por_suffix (const gchar *name)
315 return has_suffix (name, ".por");
319 name_has_sav_suffix (const gchar *name)
321 return has_suffix (name, ".sav") || has_suffix (name, ".zsav");
324 /* Returns true if NAME has a suffix which might denote a PSPP file */
326 name_has_suffix (const gchar *name)
328 return name_has_por_suffix (name) || name_has_sav_suffix (name);
332 load_file (PsppireWindow *de, const gchar *file_name, const char *encoding,
335 const char *mime_type = NULL;
336 gchar *syntax = NULL;
341 gchar *utf8_file_name;
342 struct string filename;
344 utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
346 if (NULL == utf8_file_name)
349 ds_init_empty (&filename);
350 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
352 g_free (utf8_file_name);
354 if (encoding && encoding[0])
355 syntax = g_strdup_printf ("GET FILE=%s ENCODING='%s'.",
356 ds_cstr (&filename), encoding);
358 syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
359 ds_destroy (&filename);
366 ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
367 lex_reader_for_string (syntax, "UTF-8"));
370 if (ok && syn == NULL)
372 if (name_has_por_suffix (file_name))
373 mime_type = "application/x-spss-por";
374 else if (name_has_sav_suffix (file_name))
375 mime_type = "application/x-spss-sav";
377 add_most_recent (file_name, mime_type, encoding);
384 psppire_data_window_format_to_string (enum PsppireDataWindowFormat format)
386 if (format == PSPPIRE_DATA_WINDOW_SAV)
388 else if (format == PSPPIRE_DATA_WINDOW_ZSAV)
394 /* Save DE to file */
396 save_file (PsppireWindow *w)
398 const gchar *file_name = NULL;
399 gchar *utf8_file_name = NULL;
401 struct string filename ;
402 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
405 file_name = psppire_window_get_filename (w);
407 fnx = g_string_new (file_name);
409 if ( ! name_has_suffix (fnx->str))
410 g_string_append (fnx, psppire_data_window_format_to_string (de->format));
412 ds_init_empty (&filename);
414 utf8_file_name = g_filename_to_utf8 (fnx->str, -1, NULL, NULL, NULL);
416 g_string_free (fnx, TRUE);
418 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
419 g_free (utf8_file_name);
421 if (de->format == PSPPIRE_DATA_WINDOW_SAV)
422 syntax = g_strdup_printf ("SAVE OUTFILE=%s.", ds_cstr (&filename));
423 else if (de->format == PSPPIRE_DATA_WINDOW_ZSAV)
424 syntax = g_strdup_printf ("SAVE /ZCOMPRESSED /OUTFILE=%s.",
425 ds_cstr (&filename));
427 syntax = g_strdup_printf ("EXPORT OUTFILE=%s.", ds_cstr (&filename));
429 ds_destroy (&filename);
431 g_free (execute_syntax_string (de, syntax));
436 display_dict (PsppireDataWindow *de)
438 execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
442 sysfile_info (PsppireDataWindow *de)
444 GtkWidget *dialog = psppire_window_file_chooser_dialog (PSPPIRE_WINDOW (de));
446 if ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
448 struct string filename;
450 gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
451 gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
454 const gchar *encoding = psppire_encoding_selector_get_encoding (
455 gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
459 ds_init_empty (&filename);
461 syntax_gen_string (&filename, ss_cstr (utf8_file_name));
463 g_free (utf8_file_name);
466 syntax = g_strdup_printf ("SYSFILE INFO %s ENCODING='%s'.",
467 ds_cstr (&filename), encoding);
469 syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
470 g_free (execute_syntax_string (de, syntax));
473 gtk_widget_destroy (dialog);
477 /* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
479 data_pick_filename (PsppireWindow *window)
481 GtkListStore *list_store;
482 GtkWidget *combo_box;
484 PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
485 GtkFileFilter *filter;
487 gtk_file_chooser_dialog_new (_("Save"),
489 GTK_FILE_CHOOSER_ACTION_SAVE,
490 _("Cancel"), GTK_RESPONSE_CANCEL,
491 _("Save"), GTK_RESPONSE_ACCEPT,
494 g_object_set (dialog, "local-only", FALSE, NULL);
496 filter = gtk_file_filter_new ();
497 gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
498 gtk_file_filter_add_mime_type (filter, "application/x-spss-sav");
499 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
501 filter = gtk_file_filter_new ();
502 gtk_file_filter_set_name (filter, _("Compressed System Files (*.zsav)"));
503 gtk_file_filter_add_pattern (filter, "*.zsav");
504 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
506 filter = gtk_file_filter_new ();
507 gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
508 gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
509 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
511 filter = gtk_file_filter_new ();
512 gtk_file_filter_set_name (filter, _("All Files"));
513 gtk_file_filter_add_pattern (filter, "*");
514 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
515 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
518 GtkCellRenderer *cell;
523 list_store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
524 combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store));
526 gtk_list_store_append (list_store, &iter);
527 gtk_list_store_set (list_store, &iter,
528 0, PSPPIRE_DATA_WINDOW_SAV,
531 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
533 gtk_list_store_append (list_store, &iter);
534 gtk_list_store_set (list_store, &iter,
535 0, PSPPIRE_DATA_WINDOW_ZSAV,
536 1, _("Compressed System File"),
539 gtk_list_store_append (list_store, &iter);
540 gtk_list_store_set (list_store, &iter,
541 0, PSPPIRE_DATA_WINDOW_POR,
542 1, _("Portable File"),
545 label = gtk_label_new (_("Format:"));
547 cell = gtk_cell_renderer_text_new ();
548 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, FALSE);
549 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box), cell,
552 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
553 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
554 gtk_box_pack_start (GTK_BOX (hbox), combo_box, FALSE, FALSE, 0);
555 gtk_widget_show_all (hbox);
557 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), hbox);
560 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
563 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
565 case GTK_RESPONSE_ACCEPT:
570 gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
576 gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter);
577 gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter,
582 if ( ! name_has_suffix (filename->str))
583 g_string_append (filename,
584 psppire_data_window_format_to_string (format));
586 psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
588 g_string_free (filename, TRUE);
595 gtk_widget_destroy (dialog);
599 confirm_delete_dataset (PsppireDataWindow *de,
600 const char *old_dataset,
601 const char *new_dataset,
602 const char *existing_dataset)
607 dialog = gtk_message_dialog_new (
608 GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
609 _("Delete Existing Dataset?"));
611 gtk_message_dialog_format_secondary_text (
612 GTK_MESSAGE_DIALOG (dialog),
613 _("Renaming \"%s\" to \"%s\" will destroy the existing "
614 "dataset named \"%s\". Are you sure that you want to do this?"),
615 old_dataset, new_dataset, existing_dataset);
617 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
618 _("Cancel"), GTK_RESPONSE_CANCEL,
619 _("Delete"), GTK_RESPONSE_OK,
622 g_object_set (dialog, "icon-name", "pspp", NULL);
624 result = gtk_dialog_run (GTK_DIALOG (dialog));
626 gtk_widget_destroy (dialog);
628 return result == GTK_RESPONSE_OK;
632 on_rename_dataset (PsppireDataWindow *de)
634 struct dataset *ds = de->dataset;
635 struct session *session = dataset_session (ds);
636 const char *old_name = dataset_name (ds);
637 struct dataset *existing_dataset;
641 prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
643 new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
647 if (new_name == NULL)
650 existing_dataset = session_lookup_dataset (session, new_name);
651 if (existing_dataset == NULL || existing_dataset == ds
652 || confirm_delete_dataset (de, old_name, new_name,
653 dataset_name (existing_dataset)))
654 g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
661 status_bar_activate (PsppireDataWindow *de, GtkToggleAction *action)
663 GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
665 if ( gtk_toggle_action_get_active (action))
666 gtk_widget_show (statusbar);
668 gtk_widget_hide (statusbar);
673 grid_lines_activate (PsppireDataWindow *de, GtkToggleAction *action)
675 const gboolean grid_visible = gtk_toggle_action_get_active (action);
677 psppire_data_editor_show_grid (de->data_editor, grid_visible);
681 data_view_activate (PsppireDataWindow *de)
683 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
688 variable_view_activate (PsppireDataWindow *de)
690 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
695 fonts_activate (PsppireDataWindow *de)
697 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
698 GtkWidget *dialog = gtk_font_chooser_dialog_new (NULL, GTK_WINDOW (toplevel));
699 GtkStyle *style = gtk_widget_get_style (GTK_WIDGET(de->data_editor));
700 PangoFontDescription *current_font = style->font_desc;
702 gtk_font_chooser_set_font_desc (GTK_FONT_CHOOSER (dialog), current_font);
704 gtk_window_set_transient_for (GTK_WINDOW (dialog),
705 GTK_WINDOW (toplevel));
707 if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
709 PangoFontDescription* font_desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (dialog));
711 psppire_data_editor_set_font (de->data_editor, font_desc);
714 gtk_widget_hide (dialog);
719 /* Callback for the value labels action */
721 toggle_value_labels (PsppireDataWindow *de, GtkToggleAction *ta)
723 g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
728 file_quit (PsppireDataWindow *de)
730 /* FIXME: Need to be more intelligent here.
731 Give the user the opportunity to save any unsaved data.
737 on_recent_data_select (GtkMenuShell *menushell,
738 PsppireWindow *window)
743 gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
745 file = g_filename_from_uri (uri, NULL, NULL);
749 open_data_window (window, file, NULL, NULL);
755 charset_from_mime_type (const char *mime_type)
761 if (mime_type == NULL)
764 charset = c_strcasestr (mime_type, "charset=");
772 /* Parse a "quoted-string" as defined by RFC 822. */
773 for (p++; *p != '\0' && *p != '"'; p++)
776 ds_put_byte (&s, *p);
777 else if (*++p != '\0')
778 ds_put_byte (&s, *p);
783 /* Parse a "token" as defined by RFC 2045. */
784 while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
785 ds_put_byte (&s, *p++);
787 if (!ds_is_empty (&s))
788 return ds_steal_cstr (&s);
795 on_recent_files_select (GtkMenuShell *menushell, gpointer user_data)
802 /* Get the file name and its encoding. */
803 item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
804 file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
805 encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
806 gtk_recent_info_unref (item);
808 se = psppire_syntax_window_new (encoding);
812 if ( psppire_window_load (PSPPIRE_WINDOW (se), file, encoding, NULL) )
813 gtk_widget_show (se);
815 gtk_widget_destroy (se);
821 set_unsaved (gpointer w)
823 psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
827 on_switch_page (PsppireDataEditor *de, gpointer p,
828 gint pagenum, PsppireDataWindow *dw)
830 /* Set the appropriate ui_manager according to the selected page.
831 This is necessary, because the menus for the variable view and
832 the data view are different (slightly). */
834 gboolean is_ds = pagenum == PSPPIRE_DATA_EDITOR_DATA_VIEW;
835 const char *path = (is_ds
836 ? "/ui/menubar/view/view_data"
837 : "/ui/menubar/view/view_variables");
839 GtkWidget *page_menu_item = gtk_ui_manager_get_widget (dw->ui_manager, path);
840 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (page_menu_item), TRUE);
844 on_ui_manager_changed (PsppireDataEditor *de,
845 GParamSpec *pspec UNUSED,
846 PsppireDataWindow *dw)
848 GtkUIManager *uim = psppire_data_editor_get_ui_manager (de);
854 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
855 g_object_unref (dw->uim);
862 g_object_ref (dw->uim);
863 dw->merge_id = psppire_data_window_add_ui (dw, dw->uim);
867 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
868 Returns a pointer to the action
871 connect_action (PsppireDataWindow *dw, const char *action_name,
874 GtkAction *action = get_action_assert (dw->builder, action_name);
876 g_signal_connect_swapped (action, "activate", handler, dw);
881 /* Only a data file with at least one variable can be saved. */
883 enable_save (PsppireDataWindow *dw)
885 gboolean enable = psppire_dict_get_var_cnt (dw->dict) > 0;
887 gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save"),
889 gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save_as"),
893 /* Initializes as much of a PsppireDataWindow as we can and must before the
894 dataset has been set.
896 In particular, the 'menu' member is required in case the "filename" property
897 is set before the "dataset" property: otherwise PsppireWindow will try to
898 modify the menu as part of the "filename" property_set() function and end up
899 with a Gtk-CRITICAL since 'menu' is NULL. */
901 psppire_data_window_init (PsppireDataWindow *de)
903 de->builder = builder_new ("data-editor.ui");
905 de->ui_manager = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
912 file_import (PsppireDataWindow *dw)
914 GtkWidget *w = psppire_import_assistant_new (GTK_WINDOW (dw));
915 PsppireImportAssistant *asst = PSPPIRE_IMPORT_ASSISTANT (w);
916 gtk_widget_show_all (w);
918 asst->main_loop = g_main_loop_new (NULL, TRUE);
919 g_main_loop_run (asst->main_loop);
920 g_main_loop_unref (asst->main_loop);
922 if (!asst->file_name)
925 switch (asst->response)
927 case GTK_RESPONSE_APPLY:
930 gchar *fn = g_path_get_basename (asst->file_name);
931 open_data_window (PSPPIRE_WINDOW (dw), fn, NULL, psppire_import_assistant_generate_syntax (asst));
935 case PSPPIRE_RESPONSE_PASTE:
937 free (paste_syntax_to_window (psppire_import_assistant_generate_syntax (asst)));
944 gtk_widget_destroy (GTK_WIDGET (asst));
948 psppire_data_window_finish_init (PsppireDataWindow *de,
951 static const struct dataset_callbacks cbs =
953 set_unsaved, /* changed */
954 transformation_change_callback, /* transformations_changed */
961 GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
964 de->dict = psppire_dict_new_from_dict (dataset_dict (ds));
965 de->data_store = psppire_data_store_new (de->dict);
966 psppire_data_store_set_reader (de->data_store, NULL);
968 menubar = get_widget_assert (de->builder, "menubar");
969 hb = get_widget_assert (de->builder, "toolbar");
970 sb = get_widget_assert (de->builder, "status-bar");
976 PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store));
977 g_signal_connect (de->data_editor, "switch-page",
978 G_CALLBACK (on_switch_page), de);
980 g_signal_connect_swapped (de->data_store, "case-changed",
981 G_CALLBACK (set_unsaved), de);
983 g_signal_connect_swapped (de->data_store, "case-inserted",
984 G_CALLBACK (set_unsaved), de);
986 g_signal_connect_swapped (de->data_store, "cases-deleted",
987 G_CALLBACK (set_unsaved), de);
989 dataset_set_callbacks (de->dataset, &cbs, de);
991 connect_help (de->builder);
993 gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
994 gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
995 gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
996 gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
998 gtk_container_add (GTK_CONTAINER (de), box);
1000 g_signal_connect (de->dict, "weight-changed",
1001 G_CALLBACK (on_weight_change),
1004 g_signal_connect (de->dict, "filter-changed",
1005 G_CALLBACK (on_filter_change),
1008 g_signal_connect (de->dict, "split-changed",
1009 G_CALLBACK (on_split_change),
1012 g_signal_connect_swapped (de->dict, "backend-changed",
1013 G_CALLBACK (enable_save), de);
1014 g_signal_connect_swapped (de->dict, "variable-inserted",
1015 G_CALLBACK (enable_save), de);
1016 g_signal_connect_swapped (de->dict, "variable-deleted",
1017 G_CALLBACK (enable_save), de);
1020 connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1021 connect_action (de, "file_import", G_CALLBACK (file_import));
1023 connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1024 connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1025 connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1026 connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1027 connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1028 connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1030 g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1033 GtkWidget *recent_data =
1034 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-data");
1036 GtkWidget *recent_files =
1037 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-files");
1040 GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1041 gtk_recent_manager_get_default ());
1043 GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1044 gtk_recent_manager_get_default ());
1046 g_object_set (menu_data, "show-tips", TRUE, NULL);
1047 g_object_set (menu_files, "show-tips", TRUE, NULL);
1050 GtkRecentFilter *filter = gtk_recent_filter_new ();
1052 gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1053 gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1055 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1057 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1060 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1063 g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1066 GtkRecentFilter *filter = gtk_recent_filter_new ();
1068 gtk_recent_filter_add_pattern (filter, "*.sps");
1069 gtk_recent_filter_add_pattern (filter, "*.SPS");
1071 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1073 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1076 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1078 g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1082 connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1085 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1086 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1088 connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1090 connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1092 connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1094 connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1096 connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1098 connect_action (de, "file_quit", G_CALLBACK (file_quit));
1100 connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1102 gtk_menu_shell_append (GTK_MENU_SHELL (menubar), create_windows_menu (GTK_WINDOW (de)));
1103 gtk_menu_shell_append (GTK_MENU_SHELL (menubar), create_help_menu (GTK_WINDOW (de)));
1105 g_signal_connect (de->data_editor, "notify::ui-manager",
1106 G_CALLBACK (on_ui_manager_changed), de);
1107 on_ui_manager_changed (de->data_editor, NULL, de);
1109 gtk_widget_show (GTK_WIDGET (de->data_editor));
1110 gtk_widget_show (box);
1112 ll_push_head (&all_data_windows, &de->ll);
1116 psppire_data_window_dispose (GObject *object)
1118 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1122 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
1123 g_object_unref (dw->uim);
1127 if (dw->builder != NULL)
1129 g_object_unref (dw->builder);
1135 g_signal_handlers_disconnect_by_func (dw->dict,
1136 G_CALLBACK (enable_save), dw);
1137 g_signal_handlers_disconnect_by_func (dw->dict,
1138 G_CALLBACK (on_weight_change), dw);
1139 g_signal_handlers_disconnect_by_func (dw->dict,
1140 G_CALLBACK (on_filter_change), dw);
1141 g_signal_handlers_disconnect_by_func (dw->dict,
1142 G_CALLBACK (on_split_change), dw);
1144 g_object_unref (dw->dict);
1150 g_object_unref (dw->data_store);
1151 dw->data_store = NULL;
1154 if (dw->ll.next != NULL)
1156 ll_remove (&dw->ll);
1160 if (G_OBJECT_CLASS (parent_class)->dispose)
1161 G_OBJECT_CLASS (parent_class)->dispose (object);
1165 psppire_data_window_finalize (GObject *object)
1167 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1171 struct dataset *dataset = dw->dataset;
1172 struct session *session = dataset_session (dataset);
1176 dataset_set_callbacks (dataset, NULL, NULL);
1177 session_set_active_dataset (session, NULL);
1178 dataset_destroy (dataset);
1181 if (G_OBJECT_CLASS (parent_class)->finalize)
1182 G_OBJECT_CLASS (parent_class)->finalize (object);
1186 psppire_data_window_set_property (GObject *object,
1188 const GValue *value,
1191 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1196 psppire_data_window_finish_init (window, g_value_get_pointer (value));
1199 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1205 psppire_data_window_get_property (GObject *object,
1210 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1215 g_value_set_pointer (value, window->dataset);
1218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1224 psppire_data_window_add_ui (PsppireDataWindow *pdw, GtkUIManager *uim)
1230 ui_string = gtk_ui_manager_get_ui (uim);
1231 merge_id = gtk_ui_manager_add_ui_from_string (pdw->ui_manager, ui_string,
1235 g_return_val_if_fail (merge_id != 0, 0);
1237 list = gtk_ui_manager_get_action_groups (uim);
1238 for (; list != NULL; list = list->next)
1240 GtkActionGroup *action_group = list->data;
1241 GList *actions = gtk_action_group_list_actions (action_group);
1244 for (action = actions; action != NULL; action = action->next)
1246 GtkAction *a = action->data;
1248 if (PSPPIRE_IS_DIALOG_ACTION (a))
1249 g_object_set (a, "manager", pdw->ui_manager, NULL);
1252 gtk_ui_manager_insert_action_group (pdw->ui_manager, action_group, 0);
1255 gtk_window_add_accel_group (GTK_WINDOW (pdw),
1256 gtk_ui_manager_get_accel_group (uim));
1262 psppire_data_window_remove_ui (PsppireDataWindow *pdw,
1263 GtkUIManager *uim, guint merge_id)
1267 g_return_if_fail (merge_id != 0);
1269 gtk_ui_manager_remove_ui (pdw->ui_manager, merge_id);
1271 list = gtk_ui_manager_get_action_groups (uim);
1272 for (; list != NULL; list = list->next)
1274 GtkActionGroup *action_group = list->data;
1275 gtk_ui_manager_remove_action_group (pdw->ui_manager, action_group);
1278 gtk_window_remove_accel_group (GTK_WINDOW (pdw),
1279 gtk_ui_manager_get_accel_group (uim));
1283 psppire_data_window_new (struct dataset *ds)
1287 if (the_session == NULL)
1288 the_session = session_create (NULL);
1292 char *dataset_name = session_generate_dataset_name (the_session);
1293 ds = dataset_create (the_session, dataset_name);
1294 free (dataset_name);
1296 assert (dataset_session (ds) == the_session);
1300 psppire_data_window_get_type (),
1301 "description", _("Data Editor"),
1305 if (dataset_name (ds) != NULL)
1306 g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1312 psppire_data_window_is_empty (PsppireDataWindow *dw)
1314 return psppire_dict_get_var_cnt (dw->dict) == 0;
1318 psppire_data_window_iface_init (PsppireWindowIface *iface)
1320 iface->save = save_file;
1321 iface->pick_filename = data_pick_filename;
1322 iface->load = load_file;
1326 psppire_default_data_window (void)
1328 if (ll_is_empty (&all_data_windows))
1329 create_data_window ();
1330 return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1334 psppire_data_window_set_default (PsppireDataWindow *pdw)
1336 ll_remove (&pdw->ll);
1337 ll_push_head (&all_data_windows, &pdw->ll);
1341 psppire_data_window_undefault (PsppireDataWindow *pdw)
1343 ll_remove (&pdw->ll);
1344 ll_push_tail (&all_data_windows, &pdw->ll);
1348 psppire_data_window_for_dataset (struct dataset *ds)
1350 PsppireDataWindow *pdw;
1352 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1353 if (pdw->dataset == ds)
1360 psppire_data_window_for_data_store (PsppireDataStore *data_store)
1362 PsppireDataWindow *pdw;
1364 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1365 if (pdw->data_store == data_store)
1372 create_data_window (void)
1374 gtk_widget_show (psppire_data_window_new (NULL));
1378 open_data_window (PsppireWindow *victim, const char *file_name,
1379 const char *encoding, gpointer hint)
1383 if (PSPPIRE_IS_DATA_WINDOW (victim)
1384 && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1386 window = GTK_WIDGET (victim);
1387 gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
1390 window = psppire_data_window_new (NULL);
1392 psppire_window_load (PSPPIRE_WINDOW (window), file_name, encoding, hint);
1393 gtk_widget_show_all (window);