b020d19ecf72f4a9e52bb1188737828c5bb00cc2
[pspp] / src / ui / gui / psppire-data-window.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014  Free Software Foundation
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include <gtk/gtk.h>
20 #include <stdlib.h>
21
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/gui/select-cases-dialog.h"
42 #include "ui/syntax-gen.h"
43
44 #include "gl/c-strcase.h"
45 #include "gl/c-strcasestr.h"
46 #include "gl/xvasprintf.h"
47
48 #include <gettext.h>
49 #define _(msgid) gettext (msgid)
50 #define N_(msgid) msgid
51
52 struct session *the_session;
53 struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
54
55 static void psppire_data_window_class_init    (PsppireDataWindowClass *class);
56 static void psppire_data_window_init          (PsppireDataWindow      *data_editor);
57
58
59 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
60
61 static void psppire_data_window_dispose (GObject *object);
62 static void psppire_data_window_finalize (GObject *object);
63 static void psppire_data_window_set_property (GObject         *object,
64                                               guint            prop_id,
65                                               const GValue    *value,
66                                               GParamSpec      *pspec);
67 static void psppire_data_window_get_property (GObject         *object,
68                                               guint            prop_id,
69                                               GValue          *value,
70                                               GParamSpec      *pspec);
71
72 static guint psppire_data_window_add_ui (PsppireDataWindow *, GtkUIManager *);
73 static void psppire_data_window_remove_ui (PsppireDataWindow *,
74                                            GtkUIManager *, guint);
75
76 GType
77 psppire_data_window_get_type (void)
78 {
79   static GType psppire_data_window_type = 0;
80
81   if (!psppire_data_window_type)
82     {
83       static const GTypeInfo psppire_data_window_info =
84         {
85           sizeof (PsppireDataWindowClass),
86           NULL,
87           NULL,
88           (GClassInitFunc)psppire_data_window_class_init,
89           (GClassFinalizeFunc) NULL,
90           NULL,
91           sizeof (PsppireDataWindow),
92           0,
93           (GInstanceInitFunc) psppire_data_window_init,
94         };
95
96       static const GInterfaceInfo window_interface_info =
97         {
98           (GInterfaceInitFunc) psppire_data_window_iface_init,
99           NULL,
100           NULL
101         };
102
103       psppire_data_window_type =
104         g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
105                                 &psppire_data_window_info, 0);
106
107
108       g_type_add_interface_static (psppire_data_window_type,
109                                    PSPPIRE_TYPE_WINDOW_MODEL,
110                                    &window_interface_info);
111     }
112
113   return psppire_data_window_type;
114 }
115
116 static GObjectClass *parent_class ;
117
118 enum {
119     PROP_DATASET = 1
120 };
121
122 static void
123 psppire_data_window_class_init (PsppireDataWindowClass *class)
124 {
125   GObjectClass *object_class = G_OBJECT_CLASS (class);
126
127   parent_class = g_type_class_peek_parent (class);
128
129   object_class->dispose = psppire_data_window_dispose;
130   object_class->finalize = psppire_data_window_finalize;
131   object_class->set_property = psppire_data_window_set_property;
132   object_class->get_property = psppire_data_window_get_property;
133
134   g_object_class_install_property (
135     object_class, PROP_DATASET,
136     g_param_spec_pointer ("dataset", "Dataset",
137                           "'struct datset *' represented by the window",
138                           G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
139 }
140 \f
141 /* Run the EXECUTE command. */
142 static void
143 execute (PsppireDataWindow *dw)
144 {
145   execute_const_syntax_string (dw, "EXECUTE.");
146 }
147
148 static void
149 transformation_change_callback (bool transformations_pending,
150                                 gpointer data)
151 {
152   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
153
154   GtkWidget *status_label  =
155     get_widget_assert (de->builder, "case-counter-area");
156
157   { /* Set the sensitivity of the "Transformations Pending" menuitem */
158     GtkUIManager *uim = 
159       GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
160
161     GtkWidget *menuitem =
162       gtk_ui_manager_get_widget (uim,"/ui/menubar/transform/transform_run-pending");
163     
164     gtk_widget_set_sensitive (menuitem, transformations_pending);
165   }
166
167
168   if ( transformations_pending)
169     gtk_label_set_text (GTK_LABEL (status_label),
170                         _("Transformations Pending"));
171   else
172     gtk_label_set_text (GTK_LABEL (status_label), "");
173 }
174
175 /* Callback for when the dictionary changes its filter variable */
176 static void
177 on_filter_change (GObject *o, gint filter_index, gpointer data)
178 {
179   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
180
181   GtkWidget *filter_status_area =
182     get_widget_assert (de->builder, "filter-use-status-area");
183
184   if ( filter_index == -1 )
185     {
186       gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
187     }
188   else
189     {
190       PsppireDict *dict = NULL;
191       struct variable *var ;
192       gchar *text ;
193
194       g_object_get (de->data_editor, "dictionary", &dict, NULL);
195
196       var = psppire_dict_get_variable (dict, filter_index);
197
198       text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
199
200       gtk_label_set_text (GTK_LABEL (filter_status_area), text);
201
202       g_free (text);
203     }
204 }
205
206 /* Callback for when the dictionary changes its split variables */
207 static void
208 on_split_change (PsppireDict *dict, gpointer data)
209 {
210   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
211
212   size_t n_split_vars = dict_get_split_cnt (dict->dict);
213
214   GtkWidget *split_status_area =
215     get_widget_assert (de->builder, "split-file-status-area");
216
217   if ( n_split_vars == 0 )
218     {
219       gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
220     }
221   else
222     {
223       gint i;
224       GString *text;
225       const struct variable *const * split_vars =
226         dict_get_split_vars (dict->dict);
227
228       text = g_string_new (_("Split by "));
229
230       for (i = 0 ; i < n_split_vars - 1; ++i )
231         {
232           g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
233         }
234       g_string_append (text, var_get_name (split_vars[i]));
235
236       gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
237
238       g_string_free (text, TRUE);
239     }
240 }
241
242
243
244
245 /* Callback for when the dictionary changes its weights */
246 static void
247 on_weight_change (GObject *o, gint weight_index, gpointer data)
248 {
249   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
250
251   GtkWidget *weight_status_area =
252     get_widget_assert (de->builder, "weight-status-area");
253
254   if ( weight_index == -1 )
255     {
256       gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
257     }
258   else
259     {
260       struct variable *var ;
261       PsppireDict *dict = NULL;
262       gchar *text;
263
264       g_object_get (de->data_editor, "dictionary", &dict, NULL);
265
266       var = psppire_dict_get_variable (dict, weight_index);
267
268       text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
269
270       gtk_label_set_text (GTK_LABEL (weight_status_area), text);
271
272       g_free (text);
273     }
274 }
275
276 #if 0
277 static void
278 dump_rm (GtkRecentManager *rm)
279 {
280   GList *items = gtk_recent_manager_get_items (rm);
281
282   GList *i;
283
284   g_print ("Recent Items:\n");
285   for (i = items; i; i = i->next)
286     {
287       GtkRecentInfo *ri = i->data;
288
289       g_print ("Item: %s (Mime: %s) (Desc: %s) (URI: %s)\n",
290                gtk_recent_info_get_short_name (ri),
291                gtk_recent_info_get_mime_type (ri),
292                gtk_recent_info_get_description (ri),
293                gtk_recent_info_get_uri (ri)
294                );
295
296
297       gtk_recent_info_unref (ri);
298     }
299
300   g_list_free (items);
301 }
302 #endif
303
304 static gboolean
305 has_suffix (const gchar *name, const gchar *suffix)
306 {
307   size_t name_len = strlen (name);
308   size_t suffix_len = strlen (suffix);
309   return (name_len > suffix_len
310           && !c_strcasecmp (&name[name_len - suffix_len], suffix));
311 }
312
313 static gboolean
314 name_has_por_suffix (const gchar *name)
315 {
316   return has_suffix (name, ".por");
317 }
318
319 static gboolean
320 name_has_sav_suffix (const gchar *name)
321 {
322   return has_suffix (name, ".sav") || has_suffix (name, ".zsav");
323 }
324
325 /* Returns true if NAME has a suffix which might denote a PSPP file */
326 static gboolean
327 name_has_suffix (const gchar *name)
328 {
329   return name_has_por_suffix (name) || name_has_sav_suffix (name);
330 }
331
332 static gboolean
333 load_file (PsppireWindow *de, const gchar *file_name, const char *encoding,
334            gpointer syn)
335 {
336   const char *mime_type = NULL;
337   gchar *syntax = NULL;
338   bool ok;
339
340   if (syn == NULL)
341     {
342       gchar *utf8_file_name;
343       struct string filename;
344       
345       utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
346
347       if (NULL == utf8_file_name)
348         return FALSE;
349
350       ds_init_empty (&filename);    
351       syntax_gen_string (&filename, ss_cstr (utf8_file_name));
352       
353       g_free (utf8_file_name);
354
355       if (encoding && encoding[0])
356         syntax = g_strdup_printf ("GET FILE=%s ENCODING='%s'.",
357                                   ds_cstr (&filename), encoding);
358       else
359         syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
360       ds_destroy (&filename);
361     }
362   else
363     {
364       syntax = syn;
365     }
366
367   ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
368                        lex_reader_for_string (syntax, "UTF-8"));
369   g_free (syntax);
370
371   if (ok && syn == NULL)
372     {
373       if (name_has_por_suffix (file_name))
374         mime_type = "application/x-spss-por";
375       else if (name_has_sav_suffix (file_name))
376         mime_type = "application/x-spss-sav";
377       
378       add_most_recent (file_name, mime_type, encoding);
379     }
380
381   return ok;
382 }
383
384 static const char *
385 psppire_data_window_format_to_string (enum PsppireDataWindowFormat format)
386 {
387   if (format == PSPPIRE_DATA_WINDOW_SAV)
388     return ".sav";
389   else if (format == PSPPIRE_DATA_WINDOW_ZSAV)
390     return ".zsav";
391   else
392     return ".por";
393 }
394
395 /* Save DE to file */
396 static void
397 save_file (PsppireWindow *w)
398 {
399   const gchar *file_name = NULL;
400   gchar *utf8_file_name = NULL;
401   GString *fnx;
402   struct string filename ;
403   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
404   gchar *syntax;
405
406   file_name = psppire_window_get_filename (w);
407
408   fnx = g_string_new (file_name);
409
410   if ( ! name_has_suffix (fnx->str))
411     g_string_append (fnx, psppire_data_window_format_to_string (de->format));
412
413   ds_init_empty (&filename);
414
415   utf8_file_name = g_filename_to_utf8 (fnx->str, -1, NULL, NULL, NULL);
416
417   g_string_free (fnx, TRUE);
418
419   syntax_gen_string (&filename, ss_cstr (utf8_file_name));
420   g_free (utf8_file_name);
421
422   if (de->format == PSPPIRE_DATA_WINDOW_SAV)
423     syntax = g_strdup_printf ("SAVE OUTFILE=%s.", ds_cstr (&filename));
424   else if (de->format == PSPPIRE_DATA_WINDOW_ZSAV)
425     syntax = g_strdup_printf ("SAVE /ZCOMPRESSED /OUTFILE=%s.",
426                               ds_cstr (&filename));
427   else
428     syntax = g_strdup_printf ("EXPORT OUTFILE=%s.", ds_cstr (&filename));
429
430   ds_destroy (&filename);
431
432   g_free (execute_syntax_string (de, syntax));
433 }
434
435
436 static void
437 display_dict (PsppireDataWindow *de)
438 {
439   execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
440 }
441
442 static void
443 sysfile_info (PsppireDataWindow *de)
444 {
445   GtkWidget *dialog = psppire_window_file_chooser_dialog (PSPPIRE_WINDOW (de));
446
447   if  ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
448     {
449       struct string filename;
450       gchar *file_name =
451         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
452       gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
453                                                   NULL);
454
455       const gchar *encoding = psppire_encoding_selector_get_encoding (
456         gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (dialog)));
457
458       gchar *syntax;
459
460       ds_init_empty (&filename);
461
462       syntax_gen_string (&filename, ss_cstr (utf8_file_name));
463
464       g_free (utf8_file_name);
465
466       if (encoding)
467         syntax = g_strdup_printf ("SYSFILE INFO %s ENCODING='%s'.",
468                                   ds_cstr (&filename), encoding);
469       else
470         syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
471       g_free (execute_syntax_string (de, syntax));
472     }
473
474   gtk_widget_destroy (dialog);
475 }
476
477
478 /* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
479 static void
480 data_pick_filename (PsppireWindow *window)
481 {
482   GtkListStore *list_store;
483   GtkWidget *combo_box;
484
485   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
486   GtkFileFilter *filter;
487   GtkWidget *dialog =
488     gtk_file_chooser_dialog_new (_("Save"),
489                                  GTK_WINDOW (de),
490                                  GTK_FILE_CHOOSER_ACTION_SAVE,
491                                  _("Cancel"), GTK_RESPONSE_CANCEL,
492                                  _("Save"), GTK_RESPONSE_ACCEPT,
493                                  NULL);
494
495   g_object_set (dialog, "local-only", FALSE, NULL);
496
497   filter = gtk_file_filter_new ();
498   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
499   gtk_file_filter_add_mime_type (filter, "application/x-spss-sav");
500   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
501
502   filter = gtk_file_filter_new ();
503   gtk_file_filter_set_name (filter, _("Compressed System Files (*.zsav)"));
504   gtk_file_filter_add_pattern (filter, "*.zsav");
505   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
506
507   filter = gtk_file_filter_new ();
508   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
509   gtk_file_filter_add_mime_type (filter, "application/x-spss-por");
510   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
511
512   filter = gtk_file_filter_new ();
513   gtk_file_filter_set_name (filter, _("All Files"));
514   gtk_file_filter_add_pattern (filter, "*");
515   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
516   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
517
518   {
519     GtkCellRenderer *cell;
520     GtkWidget *label;
521     GtkTreeIter iter;
522     GtkWidget *hbox;
523
524     list_store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
525     combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store));
526
527     gtk_list_store_append (list_store, &iter);
528     gtk_list_store_set (list_store, &iter,
529                         0, PSPPIRE_DATA_WINDOW_SAV,
530                         1, _("System File"),
531                         -1);
532     gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter);
533
534     gtk_list_store_append (list_store, &iter);
535     gtk_list_store_set (list_store, &iter,
536                         0, PSPPIRE_DATA_WINDOW_ZSAV,
537                         1, _("Compressed System File"),
538                         -1);
539
540     gtk_list_store_append (list_store, &iter);
541     gtk_list_store_set (list_store, &iter,
542                         0, PSPPIRE_DATA_WINDOW_POR,
543                         1, _("Portable File"),
544                         -1);
545
546     label = gtk_label_new (_("Format:"));
547
548     cell = gtk_cell_renderer_text_new ();
549     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, FALSE);
550     gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box), cell,
551                                    "text", 1);
552
553     hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
554     gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
555     gtk_box_pack_start (GTK_BOX (hbox), combo_box, FALSE, FALSE, 0);
556     gtk_widget_show_all (hbox);
557
558     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dialog), hbox);
559   }
560
561   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
562                                                   TRUE);
563
564   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
565     {
566     case GTK_RESPONSE_ACCEPT:
567       {
568         GString *filename =
569           g_string_new
570           (
571            gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
572            );
573
574         GtkTreeIter iter;
575         int format;
576
577         gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter);
578         gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter,
579                             0, &format,
580                             -1);
581         de->format = format;
582
583         if ( ! name_has_suffix (filename->str))
584           g_string_append (filename,
585                            psppire_data_window_format_to_string (format));
586
587         psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
588
589         g_string_free (filename, TRUE);
590       }
591       break;
592     default:
593       break;
594     }
595
596   gtk_widget_destroy (dialog);
597 }
598
599 static bool
600 confirm_delete_dataset (PsppireDataWindow *de,
601                         const char *old_dataset,
602                         const char *new_dataset,
603                         const char *existing_dataset)
604 {
605   GtkWidget *dialog;
606   int result;
607
608   dialog = gtk_message_dialog_new (
609     GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
610     _("Delete Existing Dataset?"));
611
612   gtk_message_dialog_format_secondary_text (
613     GTK_MESSAGE_DIALOG (dialog),
614     _("Renaming \"%s\" to \"%s\" will destroy the existing "
615       "dataset named \"%s\".  Are you sure that you want to do this?"),
616     old_dataset, new_dataset, existing_dataset);
617
618   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
619                           _("Cancel"), GTK_RESPONSE_CANCEL,
620                           _("Delete"), GTK_RESPONSE_OK,
621                           NULL);
622
623   g_object_set (dialog, "icon-name", "pspp", NULL);
624
625   result = gtk_dialog_run (GTK_DIALOG (dialog));
626
627   gtk_widget_destroy (dialog);
628
629   return result == GTK_RESPONSE_OK;
630 }
631
632 static void
633 on_rename_dataset (PsppireDataWindow *de)
634 {
635   struct dataset *ds = de->dataset;
636   struct session *session = dataset_session (ds);
637   const char *old_name = dataset_name (ds);
638   struct dataset *existing_dataset;
639   char *new_name;
640   char *prompt;
641
642   prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
643                       old_name);
644   new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
645                                old_name);
646   free (prompt);
647
648   if (new_name == NULL)
649     return;
650
651   existing_dataset = session_lookup_dataset (session, new_name);
652   if (existing_dataset == NULL || existing_dataset == ds
653       || confirm_delete_dataset (de, old_name, new_name,
654                                  dataset_name (existing_dataset)))
655     g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
656                                                         new_name)));
657
658   free (new_name);
659 }
660
661 static void
662 status_bar_activate (PsppireDataWindow  *de, GtkToggleAction *action)
663 {
664   GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
665
666   if ( gtk_toggle_action_get_active (action))
667     gtk_widget_show (statusbar);
668   else
669     gtk_widget_hide (statusbar);
670 }
671
672
673 static void
674 grid_lines_activate (PsppireDataWindow  *de, GtkToggleAction *action)
675 {
676   const gboolean grid_visible = gtk_toggle_action_get_active (action);
677
678   psppire_data_editor_show_grid (de->data_editor, grid_visible);
679 }
680
681 static void
682 data_view_activate (PsppireDataWindow  *de)
683 {
684   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
685 }
686
687
688 static void
689 variable_view_activate (PsppireDataWindow  *de)
690 {
691   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
692 }
693
694
695 static void
696 fonts_activate (PsppireDataWindow  *de)
697 {
698   GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
699   GtkWidget *dialog =  gtk_font_chooser_dialog_new (NULL, GTK_WINDOW (toplevel));
700   GtkStyle *style = gtk_widget_get_style (GTK_WIDGET(de->data_editor));
701   PangoFontDescription *current_font = style->font_desc;
702
703   gtk_font_chooser_set_font_desc (GTK_FONT_CHOOSER (dialog), current_font);
704
705   gtk_window_set_transient_for (GTK_WINDOW (dialog),
706                                 GTK_WINDOW (toplevel));
707
708   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
709     {
710       PangoFontDescription* font_desc = gtk_font_chooser_get_font_desc (GTK_FONT_CHOOSER (dialog));
711
712       psppire_data_editor_set_font (de->data_editor, font_desc);
713     }
714
715   gtk_widget_hide (dialog);
716 }
717
718
719
720 /* Callback for the value labels action */
721 static void
722 toggle_value_labels (PsppireDataWindow  *de, GtkToggleAction *ta)
723 {
724   g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
725 }
726
727 static void
728 toggle_split_window (PsppireDataWindow  *de, GtkToggleAction *ta)
729 {
730   psppire_data_editor_split_window (de->data_editor,
731                                     gtk_toggle_action_get_active (ta));
732 }
733
734
735 static void
736 file_quit (PsppireDataWindow *de)
737 {
738   /* FIXME: Need to be more intelligent here.
739      Give the user the opportunity to save any unsaved data.
740   */
741   psppire_quit ();
742 }
743
744 static void
745 on_recent_data_select (GtkMenuShell *menushell,
746                        PsppireWindow *window)
747 {
748   gchar *file;
749
750   gchar *uri =
751     gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
752
753   file = g_filename_from_uri (uri, NULL, NULL);
754
755   g_free (uri);
756
757   open_data_window (window, file, NULL, NULL);
758
759   g_free (file);
760 }
761
762 static char *
763 charset_from_mime_type (const char *mime_type)
764 {
765   const char *charset;
766   struct string s;
767   const char *p;
768
769   if (mime_type == NULL)
770     return NULL;
771
772   charset = c_strcasestr (mime_type, "charset=");
773   if (charset == NULL)
774     return NULL;
775
776   ds_init_empty (&s);
777   p = charset + 8;
778   if (*p == '"')
779     {
780       /* Parse a "quoted-string" as defined by RFC 822. */
781       for (p++; *p != '\0' && *p != '"'; p++)
782         {
783           if (*p != '\\')
784             ds_put_byte (&s, *p);
785           else if (*++p != '\0')
786             ds_put_byte (&s, *p);
787         }
788     }
789   else
790     {
791       /* Parse a "token" as defined by RFC 2045. */
792       while (*p > 32 && *p < 127 && strchr ("()<>@,;:\\\"/[]?=", *p) == NULL)
793         ds_put_byte (&s, *p++);
794     }
795   if (!ds_is_empty (&s))
796     return ds_steal_cstr (&s);
797
798   ds_destroy (&s);
799   return NULL;
800 }
801
802 static void
803 on_recent_files_select (GtkMenuShell *menushell,   gpointer user_data)
804 {
805   GtkRecentInfo *item;
806   char *encoding;
807   GtkWidget *se;
808   gchar *file;
809
810   /* Get the file name and its encoding. */
811   item = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (menushell));
812   file = g_filename_from_uri (gtk_recent_info_get_uri (item), NULL, NULL);
813   encoding = charset_from_mime_type (gtk_recent_info_get_mime_type (item));
814   gtk_recent_info_unref (item);
815
816   se = psppire_syntax_window_new (encoding);
817
818   free (encoding);
819
820   if ( psppire_window_load (PSPPIRE_WINDOW (se), file, encoding, NULL) ) 
821     gtk_widget_show (se);
822   else
823     gtk_widget_destroy (se);
824
825   g_free (file);
826 }
827
828 static void
829 set_unsaved (gpointer w)
830 {
831   psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
832 }
833
834 static void
835 on_switch_page (PsppireDataEditor *de, gpointer p,
836                 gint pagenum, PsppireDataWindow *dw)
837 {
838   /* Set the appropriate ui_manager according to the selected page.
839      This is necessary, because the menus for the variable view and
840      the data view are different (slightly). */
841
842   gboolean is_ds = pagenum == PSPPIRE_DATA_EDITOR_DATA_VIEW;
843   const char *path = (is_ds
844           ? "/ui/menubar/view/view_data"
845           : "/ui/menubar/view/view_variables");
846
847   GtkWidget *page_menu_item = gtk_ui_manager_get_widget (dw->ui_manager, path);
848   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (page_menu_item), TRUE);
849 }
850
851 static void
852 on_ui_manager_changed (PsppireDataEditor *de,
853                        GParamSpec *pspec UNUSED,
854                        PsppireDataWindow *dw)
855 {
856   GtkUIManager *uim = psppire_data_editor_get_ui_manager (de);
857   if (uim == dw->uim)
858     return;
859
860   if (dw->uim)
861     {
862       psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
863       g_object_unref (dw->uim);
864       dw->uim = NULL;
865     }
866
867   dw->uim = uim;
868   if (dw->uim)
869     {
870       g_object_ref (dw->uim);
871       dw->merge_id = psppire_data_window_add_ui (dw, dw->uim);
872     }
873 }
874
875 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
876    Returns a pointer to the action
877 */
878 static GtkAction *
879 connect_action (PsppireDataWindow *dw, const char *action_name, 
880                                     GCallback handler)
881 {
882   GtkAction *action = get_action_assert (dw->builder, action_name);
883
884   g_signal_connect_swapped (action, "activate", handler, dw);
885
886   return action;
887 }
888
889 /* Only a data file with at least one variable can be saved. */
890 static void
891 enable_save (PsppireDataWindow *dw)
892 {
893   gboolean enable = psppire_dict_get_var_cnt (dw->dict) > 0;
894
895   gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save"),
896                             enable);
897   gtk_action_set_sensitive (get_action_assert (dw->builder, "file_save_as"),
898                             enable);
899 }
900
901 /* Initializes as much of a PsppireDataWindow as we can and must before the
902    dataset has been set.
903
904    In particular, the 'menu' member is required in case the "filename" property
905    is set before the "dataset" property: otherwise PsppireWindow will try to
906    modify the menu as part of the "filename" property_set() function and end up
907    with a Gtk-CRITICAL since 'menu' is NULL.  */
908 static void
909 psppire_data_window_init (PsppireDataWindow *de)
910 {
911   GtkWidget *w ;
912   de->builder = builder_new ("data-editor.ui");
913
914   de->ui_manager = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
915
916   w = gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/windows/windows_minimise_all");
917
918   PSPPIRE_WINDOW (de)->menu = GTK_MENU_SHELL (gtk_widget_get_parent (w));
919
920   de->uim = NULL;
921   de->merge_id = 0;
922 }
923
924 static void
925 file_import (PsppireDataWindow *dw)
926 {
927   GtkWidget *w = psppire_import_assistant_new (GTK_WINDOW (dw));
928   PsppireImportAssistant *asst = PSPPIRE_IMPORT_ASSISTANT (w);
929   gtk_widget_show_all (w);
930   
931   asst->main_loop = g_main_loop_new (NULL, TRUE);
932   g_main_loop_run (asst->main_loop);
933   g_main_loop_unref (asst->main_loop);
934
935   if (!asst->file_name)
936     goto end;
937   
938   switch (asst->response)
939     {
940     case GTK_RESPONSE_APPLY:
941       {
942         g_debug ("HERE");
943         gchar *fn = g_path_get_basename (asst->file_name);
944         open_data_window (PSPPIRE_WINDOW (dw), fn, NULL, psppire_import_assistant_generate_syntax (asst));
945         g_free (fn);
946       }
947       break;
948     case PSPPIRE_RESPONSE_PASTE:
949       g_debug ("THERE");
950       free (paste_syntax_to_window (psppire_import_assistant_generate_syntax (asst)));
951       break;
952     default:
953       break;
954     }
955     
956  end:  
957   gtk_widget_destroy (GTK_WIDGET (asst));
958 }
959
960 static void
961 psppire_data_window_finish_init (PsppireDataWindow *de,
962                                  struct dataset *ds)
963 {
964   static const struct dataset_callbacks cbs =
965     {
966       set_unsaved,                    /* changed */
967       transformation_change_callback, /* transformations_changed */
968     };
969
970   GtkWidget *menubar;
971   GtkWidget *hb ;
972   GtkWidget *sb ;
973
974   GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
975
976   de->dataset = ds;
977   de->dict = psppire_dict_new_from_dict (dataset_dict (ds));
978   de->data_store = psppire_data_store_new (de->dict);
979   psppire_data_store_set_reader (de->data_store, NULL);
980
981   menubar = get_widget_assert (de->builder, "menubar");
982   hb = get_widget_assert (de->builder, "toolbar");
983   sb = get_widget_assert (de->builder, "status-bar");
984
985   de->uim = NULL;
986   de->merge_id = 0;
987
988   de->data_editor =
989     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de->dict, de->data_store));
990   g_signal_connect (de->data_editor, "switch-page",
991                     G_CALLBACK (on_switch_page), de);
992
993   g_signal_connect_swapped (de->data_store, "case-changed",
994                             G_CALLBACK (set_unsaved), de);
995
996   g_signal_connect_swapped (de->data_store, "case-inserted",
997                             G_CALLBACK (set_unsaved), de);
998
999   g_signal_connect_swapped (de->data_store, "cases-deleted",
1000                             G_CALLBACK (set_unsaved), de);
1001
1002   dataset_set_callbacks (de->dataset, &cbs, de);
1003
1004   connect_help (de->builder);
1005
1006   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
1007   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
1008   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
1009   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
1010
1011   gtk_container_add (GTK_CONTAINER (de), box);
1012
1013   g_signal_connect (de->dict, "weight-changed",
1014                     G_CALLBACK (on_weight_change),
1015                     de);
1016
1017   g_signal_connect (de->dict, "filter-changed",
1018                     G_CALLBACK (on_filter_change),
1019                     de);
1020
1021   g_signal_connect (de->dict, "split-changed",
1022                     G_CALLBACK (on_split_change),
1023                     de);
1024
1025   g_signal_connect_swapped (de->dict, "backend-changed",
1026                             G_CALLBACK (enable_save), de);
1027   g_signal_connect_swapped (de->dict, "variable-inserted",
1028                             G_CALLBACK (enable_save), de);
1029   g_signal_connect_swapped (de->dict, "variable-deleted",
1030                             G_CALLBACK (enable_save), de);
1031   enable_save (de);
1032
1033   connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
1034   connect_action (de, "file_import", G_CALLBACK (file_import));
1035
1036   connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
1037   connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
1038   connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
1039   connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
1040   connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1041   connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1042
1043   g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1044
1045   connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1046   connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1047   connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1048
1049   {
1050     GtkWidget *recent_data =
1051       gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-data");
1052
1053     GtkWidget *recent_files =
1054       gtk_ui_manager_get_widget (de->ui_manager, "/ui/menubar/file/file_recent-files");
1055
1056
1057     GtkWidget *menu_data = gtk_recent_chooser_menu_new_for_manager (
1058       gtk_recent_manager_get_default ());
1059
1060     GtkWidget *menu_files = gtk_recent_chooser_menu_new_for_manager (
1061       gtk_recent_manager_get_default ());
1062
1063     g_object_set (menu_data, "show-tips",  TRUE, NULL);
1064     g_object_set (menu_files, "show-tips",  TRUE, NULL);
1065
1066     {
1067       GtkRecentFilter *filter = gtk_recent_filter_new ();
1068
1069       gtk_recent_filter_add_mime_type (filter, "application/x-spss-sav");
1070       gtk_recent_filter_add_mime_type (filter, "application/x-spss-por");
1071
1072       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1073
1074       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1075     }
1076
1077     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1078
1079
1080     g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1081
1082     {
1083       GtkRecentFilter *filter = gtk_recent_filter_new ();
1084
1085       gtk_recent_filter_add_pattern (filter, "*.sps");
1086       gtk_recent_filter_add_pattern (filter, "*.SPS");
1087
1088       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1089
1090       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1091     }
1092
1093     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1094
1095     g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1096
1097   }
1098
1099   connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1100
1101
1102   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1103   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1104
1105   connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1106
1107   connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1108
1109   connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1110
1111   connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1112
1113   connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1114
1115   connect_action (de, "file_quit", G_CALLBACK (file_quit));
1116
1117   connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1118
1119   connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1120
1121   g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1122
1123   gtk_menu_shell_append (GTK_MENU_SHELL (menubar),  create_help_menu (GTK_WINDOW (de)));
1124   
1125   g_signal_connect (de->data_editor, "notify::ui-manager",
1126                     G_CALLBACK (on_ui_manager_changed), de);
1127   on_ui_manager_changed (de->data_editor, NULL, de);
1128
1129   gtk_widget_show (GTK_WIDGET (de->data_editor));
1130   gtk_widget_show (box);
1131
1132   ll_push_head (&all_data_windows, &de->ll);
1133 }
1134
1135 static void
1136 psppire_data_window_dispose (GObject *object)
1137 {
1138   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1139
1140   if (dw->uim)
1141     {
1142       psppire_data_window_remove_ui (dw, dw->uim, dw->merge_id);
1143       g_object_unref (dw->uim);
1144       dw->uim = NULL;
1145     }
1146
1147   if (dw->builder != NULL)
1148     {
1149       g_object_unref (dw->builder);
1150       dw->builder = NULL;
1151     }
1152
1153   if (dw->dict)
1154     {
1155       g_signal_handlers_disconnect_by_func (dw->dict,
1156                                             G_CALLBACK (enable_save), dw);
1157       g_signal_handlers_disconnect_by_func (dw->dict,
1158                                             G_CALLBACK (on_weight_change), dw);
1159       g_signal_handlers_disconnect_by_func (dw->dict,
1160                                             G_CALLBACK (on_filter_change), dw);
1161       g_signal_handlers_disconnect_by_func (dw->dict,
1162                                             G_CALLBACK (on_split_change), dw);
1163
1164       g_object_unref (dw->dict);
1165       dw->dict = NULL;
1166     }
1167
1168   if (dw->data_store)
1169     {
1170       g_object_unref (dw->data_store);
1171       dw->data_store = NULL;
1172     }
1173
1174   if (dw->ll.next != NULL)
1175     {
1176       ll_remove (&dw->ll);
1177       dw->ll.next = NULL;
1178     }
1179
1180   if (G_OBJECT_CLASS (parent_class)->dispose)
1181     G_OBJECT_CLASS (parent_class)->dispose (object);
1182 }
1183
1184 static void
1185 psppire_data_window_finalize (GObject *object)
1186 {
1187   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1188
1189   if (dw->dataset)
1190     {
1191       struct dataset *dataset = dw->dataset;
1192       struct session *session = dataset_session (dataset);
1193
1194       dw->dataset = NULL;
1195
1196       dataset_set_callbacks (dataset, NULL, NULL);
1197       session_set_active_dataset (session, NULL);
1198       dataset_destroy (dataset);
1199     }
1200
1201   if (G_OBJECT_CLASS (parent_class)->finalize)
1202     G_OBJECT_CLASS (parent_class)->finalize (object);
1203 }
1204
1205 static void
1206 psppire_data_window_set_property (GObject         *object,
1207                                   guint            prop_id,
1208                                   const GValue    *value,
1209                                   GParamSpec      *pspec)
1210 {
1211   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1212
1213   switch (prop_id)
1214     {
1215     case PROP_DATASET:
1216       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1217       break;
1218     default:
1219       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1220       break;
1221     };
1222 }
1223
1224 static void
1225 psppire_data_window_get_property (GObject         *object,
1226                                   guint            prop_id,
1227                                   GValue          *value,
1228                                   GParamSpec      *pspec)
1229 {
1230   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1231
1232   switch (prop_id)
1233     {
1234     case PROP_DATASET:
1235       g_value_set_pointer (value, window->dataset);
1236       break;
1237     default:
1238       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1239       break;
1240     };
1241 }
1242
1243 static guint
1244 psppire_data_window_add_ui (PsppireDataWindow *pdw, GtkUIManager *uim)
1245 {
1246   gchar *ui_string;
1247   guint merge_id;
1248   GList *list;
1249
1250   ui_string = gtk_ui_manager_get_ui (uim);
1251   merge_id = gtk_ui_manager_add_ui_from_string (pdw->ui_manager, ui_string,
1252                                                 -1, NULL);
1253   g_free (ui_string);
1254
1255   g_return_val_if_fail (merge_id != 0, 0);
1256
1257   list = gtk_ui_manager_get_action_groups (uim);
1258   for (; list != NULL; list = list->next)
1259     {
1260       GtkActionGroup *action_group = list->data;
1261       GList *actions = gtk_action_group_list_actions (action_group);
1262       GList *action;
1263
1264       for (action = actions; action != NULL; action = action->next)
1265         {
1266           GtkAction *a = action->data;
1267
1268           if (PSPPIRE_IS_DIALOG_ACTION (a))
1269             g_object_set (a, "manager", pdw->ui_manager, NULL);
1270         }
1271
1272       gtk_ui_manager_insert_action_group (pdw->ui_manager, action_group, 0);
1273     }
1274
1275   gtk_window_add_accel_group (GTK_WINDOW (pdw),
1276                               gtk_ui_manager_get_accel_group (uim));
1277
1278   return merge_id;
1279 }
1280
1281 static void
1282 psppire_data_window_remove_ui (PsppireDataWindow *pdw,
1283                                GtkUIManager *uim, guint merge_id)
1284 {
1285   GList *list;
1286
1287   g_return_if_fail (merge_id != 0);
1288
1289   gtk_ui_manager_remove_ui (pdw->ui_manager, merge_id);
1290
1291   list = gtk_ui_manager_get_action_groups (uim);
1292   for (; list != NULL; list = list->next)
1293     {
1294       GtkActionGroup *action_group = list->data;
1295       gtk_ui_manager_remove_action_group (pdw->ui_manager, action_group);
1296     }
1297
1298   gtk_window_remove_accel_group (GTK_WINDOW (pdw),
1299                                  gtk_ui_manager_get_accel_group (uim));
1300 }
1301
1302 GtkWidget*
1303 psppire_data_window_new (struct dataset *ds)
1304 {
1305   GtkWidget *dw;
1306
1307   if (the_session == NULL)
1308     the_session = session_create (NULL);
1309
1310   if (ds == NULL)
1311     {
1312       char *dataset_name = session_generate_dataset_name (the_session);
1313       ds = dataset_create (the_session, dataset_name);
1314       free (dataset_name);
1315     }
1316   assert (dataset_session (ds) == the_session);
1317
1318   dw = GTK_WIDGET (
1319     g_object_new (
1320       psppire_data_window_get_type (),
1321       "description", _("Data Editor"),
1322       "dataset", ds,
1323       NULL));
1324
1325   if (dataset_name (ds) != NULL)
1326     g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
1327
1328   return dw;
1329 }
1330
1331 bool
1332 psppire_data_window_is_empty (PsppireDataWindow *dw)
1333 {
1334   return psppire_dict_get_var_cnt (dw->dict) == 0;
1335 }
1336
1337 static void
1338 psppire_data_window_iface_init (PsppireWindowIface *iface)
1339 {
1340   iface->save = save_file;
1341   iface->pick_filename = data_pick_filename;
1342   iface->load = load_file;
1343 }
1344 \f
1345 PsppireDataWindow *
1346 psppire_default_data_window (void)
1347 {
1348   if (ll_is_empty (&all_data_windows))
1349     create_data_window ();
1350   return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
1351 }
1352
1353 void
1354 psppire_data_window_set_default (PsppireDataWindow *pdw)
1355 {
1356   ll_remove (&pdw->ll);
1357   ll_push_head (&all_data_windows, &pdw->ll);
1358 }
1359
1360 void
1361 psppire_data_window_undefault (PsppireDataWindow *pdw)
1362 {
1363   ll_remove (&pdw->ll);
1364   ll_push_tail (&all_data_windows, &pdw->ll);
1365 }
1366
1367 PsppireDataWindow *
1368 psppire_data_window_for_dataset (struct dataset *ds)
1369 {
1370   PsppireDataWindow *pdw;
1371
1372   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1373     if (pdw->dataset == ds)
1374       return pdw;
1375
1376   return NULL;
1377 }
1378
1379 PsppireDataWindow *
1380 psppire_data_window_for_data_store (PsppireDataStore *data_store)
1381 {
1382   PsppireDataWindow *pdw;
1383
1384   ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
1385     if (pdw->data_store == data_store)
1386       return pdw;
1387
1388   return NULL;
1389 }
1390
1391 void
1392 create_data_window (void)
1393 {
1394   gtk_widget_show (psppire_data_window_new (NULL));
1395 }
1396
1397 void
1398 open_data_window (PsppireWindow *victim, const char *file_name,
1399                   const char *encoding, gpointer hint)
1400 {
1401   GtkWidget *window;
1402
1403   if (PSPPIRE_IS_DATA_WINDOW (victim)
1404       && psppire_data_window_is_empty (PSPPIRE_DATA_WINDOW (victim)))
1405     {
1406       window = GTK_WIDGET (victim);
1407       gtk_widget_hide (GTK_WIDGET (PSPPIRE_DATA_WINDOW (window)->data_editor));
1408     }
1409   else
1410     window = psppire_data_window_new (NULL);
1411
1412   psppire_window_load (PSPPIRE_WINDOW (window), file_name, encoding, hint);
1413   gtk_widget_show_all (window);
1414 }
1415