1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
22 #include "data/dataset.h"
23 #include "data/session.h"
24 #include "language/lexer/lexer.h"
25 #include "libpspp/message.h"
26 #include "libpspp/str.h"
27 #include "ui/gui/builder-wrapper.h"
28 #include "ui/gui/entry-dialog.h"
29 #include "ui/gui/executor.h"
30 #include "ui/gui/help-menu.h"
31 #include "ui/gui/helper.h"
32 #include "ui/gui/helper.h"
33 #include "ui/gui/psppire-import-assistant.h"
34 #include "ui/gui/psppire-data-window.h"
35 #include "ui/gui/psppire-dialog-action.h"
36 #include "ui/gui/psppire-encoding-selector.h"
37 #include "ui/gui/psppire-syntax-window.h"
38 #include "ui/gui/psppire-window.h"
39 #include "ui/gui/psppire.h"
40 #include "ui/gui/recode-dialog.h"
41 #include "ui/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);
727 toggle_split_window (PsppireDataWindow *de, GtkToggleAction *ta)
729 psppire_data_editor_split_window (de->data_editor,
730 gtk_toggle_action_get_active (ta));
735 file_quit (PsppireDataWindow *de)
737 /* FIXME: Need to be more intelligent here.
738 Give the user the opportunity to save any unsaved data.
744 on_recent_data_select (GtkMenuShell *menushell,
745 PsppireWindow *window)
750 gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
752 file = g_filename_from_uri (uri, NULL, NULL);
756 open_data_window (window, file, NULL, NULL);
762 charset_from_mime_type (const char *mime_type)
768 if (mime_type == NULL)
771 charset = c_strcasestr (mime_type, "charset=");
779 /* Parse a "quoted-string" as defined by RFC 822. */
780 for (p++; *p != '\0' && *p != '"'; p++)
783 ds_put_byte (&s, *p);
784 else if (*++p != '\0')
785 ds_put_byte (&s, *p);
790 /* Parse a "token" as defined by RFC 2045. */
791 while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
792 ds_put_byte (&s, *p++);
794 if (!ds_is_empty (&s))
795 return ds_steal_cstr (&s);
802 on_recent_files_select (GtkMenuShell *menushell, gpointer user_data)
809 /* Get the file name and its encoding. */
810 item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
811 file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
812 encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
813 gtk_recent_info_unref (item);
815 se = psppire_syntax_window_new (encoding);
819 if ( psppire_window_load (PSPPIRE_WINDOW (se), file, encoding, NULL) )
820 gtk_widget_show (se);
822 gtk_widget_destroy (se);
828 set_unsaved (gpointer w)
830 psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
834 on_switch_page (PsppireDataEditor *de, gpointer p,
835 gint pagenum, PsppireDataWindow *dw)
837 /* Set the appropriate ui_manager according to the selected page.
838 This is necessary, because the menus for the variable view and
839 the data view are different (slightly). */
841 gboolean is_ds = pagenum == PSPPIRE_DATA_EDITOR_DATA_VIEW;
842 const char *path = (is_ds
843 ? "/ui/menubar/view/view_data"
844 : "/ui/menubar/view/view_variables");
846 GtkWidget *page_menu_item = gtk_ui_manager_get_widget (dw->ui_manager, path);
847 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (page_menu_item), TRUE);
851 on_ui_manager_changed (PsppireDataEditor *de,
852 GParamSpec *pspec UNUSED,
853 PsppireDataWindow *dw)
855 GtkUIManager *uim = psppire_data_editor_get_ui_manager (de);
861 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
862 g_object_unref (dw->uim);
869 g_object_ref (dw->uim);
870 dw->merge_id = psppire_data_window_add_ui (dw, dw->uim);
874 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
875 Returns a pointer to the action
878 connect_action (PsppireDataWindow *dw, const char *action_name,
881 GtkAction *action = get_action_assert (dw->builder, action_name);
883 g_signal_connect_swapped (action, "activate", handler, dw);
888 /* Only a data file with at least one variable can be saved. */
890 enable_save (PsppireDataWindow *dw)
892 gboolean enable = psppire_dict_get_var_cnt (dw->dict) > 0;
894 gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save"),
896 gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save_as"),
900 /* Initializes as much of a PsppireDataWindow as we can and must before the
901 dataset has been set.
903 In particular, the 'menu' member is required in case the "filename" property
904 is set before the "dataset" property: otherwise PsppireWindow will try to
905 modify the menu as part of the "filename" property_set() function and end up
906 with a Gtk-CRITICAL since 'menu' is NULL. */
908 psppire_data_window_init (PsppireDataWindow *de)
911 de->builder = builder_new ("data-editor.ui");
913 de->ui_manager = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
915 w = gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/windows/windows_minimise_all");
917 PSPPIRE_WINDOW (de)->menu = GTK_MENU_SHELL (gtk_widget_get_parent (w));
924 file_import (PsppireDataWindow *dw)
926 GtkWidget *w = psppire_import_assistant_new (GTK_WINDOW (dw));
927 PsppireImportAssistant *asst = PSPPIRE_IMPORT_ASSISTANT (w);
928 gtk_widget_show_all (w);
930 asst->main_loop = g_main_loop_new (NULL, TRUE);
931 g_main_loop_run (asst->main_loop);
932 g_main_loop_unref (asst->main_loop);
934 if (!asst->file_name)
937 switch (asst->response)
939 case GTK_RESPONSE_APPLY:
942 gchar *fn = g_path_get_basename (asst->file_name);
943 open_data_window (PSPPIRE_WINDOW (dw), fn, NULL, psppire_import_assistant_generate_syntax (asst));
947 case PSPPIRE_RESPONSE_PASTE:
949 free (paste_syntax_to_window (psppire_import_assistant_generate_syntax (asst)));
956 gtk_widget_destroy (GTK_WIDGET (asst));
960 psppire_data_window_finish_init (PsppireDataWindow *de,
963 static const struct dataset_callbacks cbs =
965 set_unsaved, /* changed */
966 transformation_change_callback, /* transformations_changed */
973 GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
976 de->dict = psppire_dict_new_from_dict (dataset_dict (ds));
977 de->data_store = psppire_data_store_new (de->dict);
978 psppire_data_store_set_reader (de->data_store, NULL);
980 menubar = get_widget_assert (de->builder, "menubar");
981 hb = get_widget_assert (de->builder, "toolbar");
982 sb = get_widget_assert (de->builder, "status-bar");
988 PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store));
989 g_signal_connect (de->data_editor, "switch-page",
990 G_CALLBACK (on_switch_page), de);
992 g_signal_connect_swapped (de->data_store, "case-changed",
993 G_CALLBACK (set_unsaved), de);
995 g_signal_connect_swapped (de->data_store, "case-inserted",
996 G_CALLBACK (set_unsaved), de);
998 g_signal_connect_swapped (de->data_store, "cases-deleted",
999 G_CALLBACK (set_unsaved), de);
1001 dataset_set_callbacks (de->dataset, &cbs, de);
1003 connect_help (de->builder);
1005 gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
1006 gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
1007 gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
1008 gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
1010 gtk_container_add (GTK_CONTAINER (de), box);
1012 g_signal_connect (de->dict, "weight-changed",
1013 G_CALLBACK (on_weight_change),
1016 g_signal_connect (de->dict, "filter-changed",
1017 G_CALLBACK (on_filter_change),
1020 g_signal_connect (de->dict, "split-changed",
1021 G_CALLBACK (on_split_change),
1024 g_signal_connect_swapped (de->dict, "backend-changed",
1025 G_CALLBACK (enable_save), de);
1026 g_signal_connect_swapped (de->dict, "variable-inserted",
1027 G_CALLBACK (enable_save), de);
1028 g_signal_connect_swapped (de->dict, "variable-deleted",
1029 G_CALLBACK (enable_save), de);
1032 connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1033 connect_action (de, "file_import", G_CALLBACK (file_import));
1035 connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1036 connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1037 connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1038 connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1039 connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1040 connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1042 g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1044 connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1045 connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1048 GtkWidget *recent_data =
1049 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-data");
1051 GtkWidget *recent_files =
1052 gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-files");
1055 GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1056 gtk_recent_manager_get_default ());
1058 GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1059 gtk_recent_manager_get_default ());
1061 g_object_set (menu_data, "show-tips", TRUE, NULL);
1062 g_object_set (menu_files, "show-tips", TRUE, NULL);
1065 GtkRecentFilter *filter = gtk_recent_filter_new ();
1067 gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1068 gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1070 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1072 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1075 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1078 g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1081 GtkRecentFilter *filter = gtk_recent_filter_new ();
1083 gtk_recent_filter_add_pattern (filter, "*.sps");
1084 gtk_recent_filter_add_pattern (filter, "*.SPS");
1086 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1088 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1091 gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1093 g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1097 connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1100 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1101 gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1103 connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1105 connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1107 connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1109 connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1111 connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1113 connect_action (de, "file_quit", G_CALLBACK (file_quit));
1115 connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1117 connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1119 g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1121 gtk_menu_shell_append (GTK_MENU_SHELL (menubar), create_help_menu (GTK_WINDOW (de)));
1123 g_signal_connect (de->data_editor, "notify::ui-manager",
1124 G_CALLBACK (on_ui_manager_changed), de);
1125 on_ui_manager_changed (de->data_editor, NULL, de);
1127 gtk_widget_show (GTK_WIDGET (de->data_editor));
1128 gtk_widget_show (box);
1130 ll_push_head (&all_data_windows, &de->ll);
1134 psppire_data_window_dispose (GObject *object)
1136 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1140 psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
1141 g_object_unref (dw->uim);
1145 if (dw->builder != NULL)
1147 g_object_unref (dw->builder);
1153 g_signal_handlers_disconnect_by_func (dw->dict,
1154 G_CALLBACK (enable_save), dw);
1155 g_signal_handlers_disconnect_by_func (dw->dict,
1156 G_CALLBACK (on_weight_change), dw);
1157 g_signal_handlers_disconnect_by_func (dw->dict,
1158 G_CALLBACK (on_filter_change), dw);
1159 g_signal_handlers_disconnect_by_func (dw->dict,
1160 G_CALLBACK (on_split_change), dw);
1162 g_object_unref (dw->dict);
1168 g_object_unref (dw->data_store);
1169 dw->data_store = NULL;
1172 if (dw->ll.next != NULL)
1174 ll_remove (&dw->ll);
1178 if (G_OBJECT_CLASS (parent_class)->dispose)
1179 G_OBJECT_CLASS (parent_class)->dispose (object);
1183 psppire_data_window_finalize (GObject *object)
1185 PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1189 struct dataset *dataset = dw->dataset;
1190 struct session *session = dataset_session (dataset);
1194 dataset_set_callbacks (dataset, NULL, NULL);
1195 session_set_active_dataset (session, NULL);
1196 dataset_destroy (dataset);
1199 if (G_OBJECT_CLASS (parent_class)->finalize)
1200 G_OBJECT_CLASS (parent_class)->finalize (object);
1204 psppire_data_window_set_property (GObject *object,
1206 const GValue *value,
1209 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1214 psppire_data_window_finish_init (window, g_value_get_pointer (value));
1217 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1223 psppire_data_window_get_property (GObject *object,
1228 PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1233 g_value_set_pointer (value, window->dataset);
1236 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1242 psppire_data_window_add_ui (PsppireDataWindow *pdw, GtkUIManager *uim)
1248 ui_string = gtk_ui_manager_get_ui (uim);
1249 merge_id = gtk_ui_manager_add_ui_from_string (pdw->ui_manager, ui_string,
1253 g_return_val_if_fail (merge_id != 0, 0);
1255 list = gtk_ui_manager_get_action_groups (uim);
1256 for (; list != NULL; list = list->next)
1258 GtkActionGroup *action_group = list->data;
1259 GList *actions = gtk_action_group_list_actions (action_group);
1262 for (action = actions; action != NULL; action = action->next)
1264 GtkAction *a = action->data;
1266 if (PSPPIRE_IS_DIALOG_ACTION (a))
1267 g_object_set (a, "manager", pdw->ui_manager, NULL);
1270 gtk_ui_manager_insert_action_group (pdw->ui_manager, action_group, 0);
1273 gtk_window_add_accel_group (GTK_WINDOW (pdw),
1274 gtk_ui_manager_get_accel_group (uim));
1280 psppire_data_window_remove_ui (PsppireDataWindow *pdw,
1281 GtkUIManager *uim, guint merge_id)
1285 g_return_if_fail (merge_id != 0);
1287 gtk_ui_manager_remove_ui (pdw->ui_manager, merge_id);
1289 list = gtk_ui_manager_get_action_groups (uim);
1290 for (; list != NULL; list = list->next)
1292 GtkActionGroup *action_group = list->data;
1293 gtk_ui_manager_remove_action_group (pdw->ui_manager, action_group);
1296 gtk_window_remove_accel_group (GTK_WINDOW (pdw),
1297 gtk_ui_manager_get_accel_group (uim));
1301 psppire_data_window_new (struct dataset *ds)
1305 if (the_session == NULL)
1306 the_session = session_create (NULL);
1310 char *dataset_name = session_generate_dataset_name (the_session);
1311 ds = dataset_create (the_session, dataset_name);
1312 free (dataset_name);
1314 assert (dataset_session (ds) == the_session);
1318 psppire_data_window_get_type (),
1319 "description", _("Data Editor"),
1323 if (dataset_name (ds) != NULL)
1324 g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1330 psppire_data_window_is_empty (PsppireDataWindow *dw)
1332 return psppire_dict_get_var_cnt (dw->dict) == 0;
1336 psppire_data_window_iface_init (PsppireWindowIface *iface)
1338 iface->save = save_file;
1339 iface->pick_filename = data_pick_filename;
1340 iface->load = load_file;
1344 psppire_default_data_window (void)
1346 if (ll_is_empty (&all_data_windows))
1347 create_data_window ();
1348 return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1352 psppire_data_window_set_default (PsppireDataWindow *pdw)
1354 ll_remove (&pdw->ll);
1355 ll_push_head (&all_data_windows, &pdw->ll);
1359 psppire_data_window_undefault (PsppireDataWindow *pdw)
1361 ll_remove (&pdw->ll);
1362 ll_push_tail (&all_data_windows, &pdw->ll);
1366 psppire_data_window_for_dataset (struct dataset *ds)
1368 PsppireDataWindow *pdw;
1370 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1371 if (pdw->dataset == ds)
1378 psppire_data_window_for_data_store (PsppireDataStore *data_store)
1380 PsppireDataWindow *pdw;
1382 ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1383 if (pdw->data_store == data_store)
1390 create_data_window (void)
1392 gtk_widget_show (psppire_data_window_new (NULL));
1396 open_data_window (PsppireWindow *victim, const char *file_name,
1397 const char *encoding, gpointer hint)
1401 if (PSPPIRE_IS_DATA_WINDOW (victim)
1402 && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1404 window = GTK_WIDGET (victim);
1405 gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
1408 window = psppire_data_window_new (NULL);
1410 psppire_window_load (PSPPIRE_WINDOW (window), file_name, encoding, hint);
1411 gtk_widget_show_all (window);