gui: Eliminate dataset-related global variables.
[pspp-builds.git] / src / ui / gui / psppire-data-window.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008, 2009, 2010, 2011  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/any-reader.h"
23 #include "data/dataset.h"
24 #include "language/lexer/lexer.h"
25 #include "libpspp/message.h"
26 #include "ui/gui/aggregate-dialog.h"
27 #include "ui/gui/binomial-dialog.h"
28 #include "ui/gui/chi-square-dialog.h"
29 #include "ui/gui/comments-dialog.h"
30 #include "ui/gui/compute-dialog.h"
31 #include "ui/gui/correlation-dialog.h"
32 #include "ui/gui/crosstabs-dialog.h"
33 #include "ui/gui/descriptives-dialog.h"
34 #include "ui/gui/examine-dialog.h"
35 #include "ui/gui/executor.h"
36 #include "ui/gui/factor-dialog.h"
37 #include "ui/gui/find-dialog.h"
38 #include "ui/gui/frequencies-dialog.h"
39 #include "ui/gui/goto-case-dialog.h"
40 #include "ui/gui/help-menu.h"
41 #include "ui/gui/helper.h"
42 #include "ui/gui/k-related-dialog.h"
43 #include "ui/gui/oneway-anova-dialog.h"
44 #include "ui/gui/psppire-data-window.h"
45 #include "ui/gui/psppire-syntax-window.h"
46 #include "ui/gui/psppire-window.h"
47 #include "ui/gui/psppire.h"
48 #include "ui/gui/rank-dialog.h"
49 #include "ui/gui/recode-dialog.h"
50 #include "ui/gui/regression-dialog.h"
51 #include "ui/gui/reliability-dialog.h"
52 #include "ui/gui/roc-dialog.h"
53 #include "ui/gui/select-cases-dialog.h"
54 #include "ui/gui/sort-cases-dialog.h"
55 #include "ui/gui/split-file-dialog.h"
56 #include "ui/gui/t-test-independent-samples-dialog.h"
57 #include "ui/gui/t-test-one-sample.h"
58 #include "ui/gui/t-test-paired-samples.h"
59 #include "ui/gui/text-data-import-dialog.h"
60 #include "ui/gui/transpose-dialog.h"
61 #include "ui/gui/variable-info-dialog.h"
62 #include "ui/gui/weight-cases-dialog.h"
63 #include "ui/syntax-gen.h"
64
65 #include <gettext.h>
66 #define _(msgid) gettext (msgid)
67 #define N_(msgid) msgid
68
69 static PsppireDataWindow *the_data_window;
70
71 static void psppire_data_window_class_init    (PsppireDataWindowClass *class);
72 static void psppire_data_window_init          (PsppireDataWindow      *data_editor);
73
74
75 static void psppire_data_window_iface_init (PsppireWindowIface *iface);
76
77 static void psppire_data_window_dispose (GObject *object);
78 static void psppire_data_window_set_property (GObject         *object,
79                                               guint            prop_id,
80                                               const GValue    *value,
81                                               GParamSpec      *pspec);
82 static void psppire_data_window_get_property (GObject         *object,
83                                               guint            prop_id,
84                                               GValue          *value,
85                                               GParamSpec      *pspec);
86
87 GType
88 psppire_data_window_get_type (void)
89 {
90   static GType psppire_data_window_type = 0;
91
92   if (!psppire_data_window_type)
93     {
94       static const GTypeInfo psppire_data_window_info =
95         {
96           sizeof (PsppireDataWindowClass),
97           NULL,
98           NULL,
99           (GClassInitFunc)psppire_data_window_class_init,
100           (GClassFinalizeFunc) NULL,
101           NULL,
102           sizeof (PsppireDataWindow),
103           0,
104           (GInstanceInitFunc) psppire_data_window_init,
105         };
106
107       static const GInterfaceInfo window_interface_info =
108         {
109           (GInterfaceInitFunc) psppire_data_window_iface_init,
110           NULL,
111           NULL
112         };
113
114       psppire_data_window_type =
115         g_type_register_static (PSPPIRE_TYPE_WINDOW, "PsppireDataWindow",
116                                 &psppire_data_window_info, 0);
117
118
119       g_type_add_interface_static (psppire_data_window_type,
120                                    PSPPIRE_TYPE_WINDOW_MODEL,
121                                    &window_interface_info);
122     }
123
124   return psppire_data_window_type;
125 }
126
127 static GObjectClass *parent_class ;
128
129 enum {
130     PROP_DATASET = 1
131 };
132
133 static void
134 psppire_data_window_class_init (PsppireDataWindowClass *class)
135 {
136   GObjectClass *object_class = G_OBJECT_CLASS (class);
137
138   parent_class = g_type_class_peek_parent (class);
139
140   object_class->dispose = psppire_data_window_dispose;
141   object_class->set_property = psppire_data_window_set_property;
142   object_class->get_property = psppire_data_window_get_property;
143
144   g_object_class_install_property (
145     object_class, PROP_DATASET,
146     g_param_spec_pointer ("dataset", "Dataset",
147                           "'struct datset *' represented by the window",
148                           G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
149 }
150 \f
151
152 extern GtkRecentManager *the_recent_mgr;
153
154 static void
155 set_paste_menuitem_sensitivity (PsppireDataWindow *de, gboolean x)
156 {
157   GtkAction *edit_paste = get_action_assert (de->builder, "edit_paste");
158
159   gtk_action_set_sensitive (edit_paste, x);
160 }
161
162 static void
163 set_cut_copy_menuitem_sensitivity (PsppireDataWindow *de, gboolean x)
164 {
165   GtkAction *edit_copy = get_action_assert (de->builder, "edit_copy");
166   GtkAction *edit_cut = get_action_assert (de->builder, "edit_cut");
167
168   gtk_action_set_sensitive (edit_copy, x);
169   gtk_action_set_sensitive (edit_cut, x);
170 }
171
172 /* Run the EXECUTE command. */
173 static void
174 execute (PsppireDataWindow *dw)
175 {
176   execute_const_syntax_string (dw, "EXECUTE.");
177 }
178
179 static void
180 transformation_change_callback (bool transformations_pending,
181                                 gpointer data)
182 {
183   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
184
185   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
186
187   GtkWidget *menuitem =
188     gtk_ui_manager_get_widget (uim,"/ui/menubar/transform/transform_run-pending");
189
190   GtkWidget *status_label  =
191     get_widget_assert (de->builder, "case-counter-area");
192
193   gtk_widget_set_sensitive (menuitem, transformations_pending);
194
195
196   if ( transformations_pending)
197     gtk_label_set_text (GTK_LABEL (status_label),
198                         _("Transformations Pending"));
199   else
200     gtk_label_set_text (GTK_LABEL (status_label), "");
201 }
202
203 /* Callback for when the dictionary changes its filter variable */
204 static void
205 on_filter_change (GObject *o, gint filter_index, gpointer data)
206 {
207   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
208
209   GtkWidget *filter_status_area =
210     get_widget_assert (de->builder, "filter-use-status-area");
211
212   if ( filter_index == -1 )
213     {
214       gtk_label_set_text (GTK_LABEL (filter_status_area), _("Filter off"));
215     }
216   else
217     {
218       PsppireVarStore *vs = NULL;
219       PsppireDict *dict = NULL;
220       struct variable *var ;
221       gchar *text ;
222
223       g_object_get (de->data_editor, "var-store", &vs, NULL);
224       g_object_get (vs, "dictionary", &dict, NULL);
225
226       var = psppire_dict_get_variable (dict, filter_index);
227
228       text = g_strdup_printf (_("Filter by %s"), var_get_name (var));
229
230       gtk_label_set_text (GTK_LABEL (filter_status_area), text);
231
232       g_free (text);
233     }
234 }
235
236 /* Callback for when the dictionary changes its split variables */
237 static void
238 on_split_change (PsppireDict *dict, gpointer data)
239 {
240   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
241
242   size_t n_split_vars = dict_get_split_cnt (dict->dict);
243
244   GtkWidget *split_status_area =
245     get_widget_assert (de->builder, "split-file-status-area");
246
247   if ( n_split_vars == 0 )
248     {
249       gtk_label_set_text (GTK_LABEL (split_status_area), _("No Split"));
250     }
251   else
252     {
253       gint i;
254       GString *text;
255       const struct variable *const * split_vars =
256         dict_get_split_vars (dict->dict);
257
258       text = g_string_new (_("Split by "));
259
260       for (i = 0 ; i < n_split_vars - 1; ++i )
261         {
262           g_string_append_printf (text, "%s, ", var_get_name (split_vars[i]));
263         }
264       g_string_append (text, var_get_name (split_vars[i]));
265
266       gtk_label_set_text (GTK_LABEL (split_status_area), text->str);
267
268       g_string_free (text, TRUE);
269     }
270 }
271
272
273
274
275 /* Callback for when the dictionary changes its weights */
276 static void
277 on_weight_change (GObject *o, gint weight_index, gpointer data)
278 {
279   PsppireDataWindow  *de = PSPPIRE_DATA_WINDOW (data);
280
281   GtkWidget *weight_status_area =
282     get_widget_assert (de->builder, "weight-status-area");
283
284   if ( weight_index == -1 )
285     {
286       gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
287     }
288   else
289     {
290       struct variable *var ;
291       PsppireVarStore *vs = NULL;
292       PsppireDict *dict = NULL;
293       gchar *text;
294
295       g_object_get (de->data_editor, "var-store", &vs, NULL);
296       g_object_get (vs, "dictionary", &dict, NULL);
297
298       var = psppire_dict_get_variable (dict, weight_index);
299
300       text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
301
302       gtk_label_set_text (GTK_LABEL (weight_status_area), text);
303
304       g_free (text);
305     }
306 }
307
308 #if 0
309 static void
310 dump_rm (GtkRecentManager *rm)
311 {
312   GList *items = gtk_recent_manager_get_items (rm);
313
314   GList *i;
315
316   g_print ("Recent Items:\n");
317   for (i = items; i; i = i->next)
318     {
319       GtkRecentInfo *ri = i->data;
320
321       g_print ("Item: %s (Mime: %s) (Desc: %s) (URI: %s)\n",
322                gtk_recent_info_get_short_name (ri),
323                gtk_recent_info_get_mime_type (ri),
324                gtk_recent_info_get_description (ri),
325                gtk_recent_info_get_uri (ri)
326                );
327
328
329       gtk_recent_info_unref (ri);
330     }
331
332   g_list_free (items);
333 }
334 #endif
335
336
337 static gboolean
338 load_file (PsppireWindow *de, const gchar *file_name)
339 {
340   gchar *utf8_file_name;
341   struct string filename;
342   gchar *syntax;
343   bool ok;
344
345   ds_init_empty (&filename);
346
347   utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
348
349   syntax_gen_string (&filename, ss_cstr (utf8_file_name));
350
351   g_free (utf8_file_name);
352
353   syntax = g_strdup_printf ("GET FILE=%s.", ds_cstr (&filename));
354   ds_destroy (&filename);
355
356   ok = execute_syntax (PSPPIRE_DATA_WINDOW (de),
357                        lex_reader_for_string (syntax));
358   g_free (syntax);
359   return ok;
360 }
361
362 static GtkWidget *
363 sysfile_chooser_dialog (PsppireWindow *toplevel)
364 {
365   GtkWidget *dialog =
366     gtk_file_chooser_dialog_new (_("Open"),
367                                  GTK_WINDOW (toplevel),
368                                  GTK_FILE_CHOOSER_ACTION_OPEN,
369                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
370                                  GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
371                                  NULL);
372
373   GtkFileFilter *filter;
374
375   filter = gtk_file_filter_new ();
376   gtk_file_filter_set_name (filter, _("Data and Syntax Files"));
377   gtk_file_filter_add_pattern (filter, "*.sav");
378   gtk_file_filter_add_pattern (filter, "*.SAV");
379   gtk_file_filter_add_pattern (filter, "*.por");
380   gtk_file_filter_add_pattern (filter, "*.POR");
381   gtk_file_filter_add_pattern (filter, "*.sps");
382   gtk_file_filter_add_pattern (filter, "*.SPS");
383   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
384
385   filter = gtk_file_filter_new ();
386   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
387   gtk_file_filter_add_pattern (filter, "*.sav");
388   gtk_file_filter_add_pattern (filter, "*.SAV");
389   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
390
391   filter = gtk_file_filter_new ();
392   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
393   gtk_file_filter_add_pattern (filter, "*.por");
394   gtk_file_filter_add_pattern (filter, "*.POR");
395   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
396
397   filter = gtk_file_filter_new ();
398   gtk_file_filter_set_name (filter, _("Syntax Files (*.sps) "));
399   gtk_file_filter_add_pattern (filter, "*.sps");
400   gtk_file_filter_add_pattern (filter, "*.SPS");
401   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
402
403   filter = gtk_file_filter_new ();
404   gtk_file_filter_set_name (filter, _("All Files"));
405   gtk_file_filter_add_pattern (filter, "*");
406   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
407
408   {
409     gchar *dir_name;
410     gchar *filename = NULL;
411     g_object_get (toplevel, "filename", &filename, NULL);
412
413     if ( ! g_path_is_absolute (filename))
414       {
415         gchar *path =
416           g_build_filename (g_get_current_dir (), filename, NULL);
417         dir_name = g_path_get_dirname (path);
418         g_free (path);
419       }
420     else
421       {
422         dir_name = g_path_get_dirname (filename);
423       }
424     gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
425                                          dir_name);
426     free (dir_name);
427   }
428
429   return dialog;
430 }
431
432 /* Callback for the data_open action.
433    Prompts for a filename and opens it */
434 static void
435 open_window (PsppireWindow *de)
436 {
437   GtkWidget *dialog = sysfile_chooser_dialog (de);
438
439   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
440     {
441     case GTK_RESPONSE_ACCEPT:
442       {
443         gchar *name =
444           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
445
446         gchar *sysname = convert_glib_filename_to_system_filename (name, NULL);
447
448         if (any_reader_may_open (sysname))
449           psppire_window_load (de, name);
450         else
451           open_syntax_window (name);
452
453         g_free (sysname);
454         g_free (name);
455       }
456       break;
457     default:
458       break;
459     }
460
461   gtk_widget_destroy (dialog);
462 }
463
464 /* Returns true if NAME has a suffix which might denote a PSPP file */
465 static gboolean
466 name_has_suffix (const gchar *name)
467 {
468   if ( g_str_has_suffix (name, ".sav"))
469     return TRUE;
470   if ( g_str_has_suffix (name, ".SAV"))
471     return TRUE;
472   if ( g_str_has_suffix (name, ".por"))
473     return TRUE;
474   if ( g_str_has_suffix (name, ".POR"))
475     return TRUE;
476
477   return FALSE;
478 }
479
480
481 /* Save DE to file */
482 static void
483 save_file (PsppireWindow *w)
484 {
485   gchar *utf8_file_name = NULL;
486   gchar *file_name = NULL;
487   GString *fnx;
488   struct string filename ;
489   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (w);
490   gchar *syntax;
491
492   g_object_get (w, "filename", &file_name, NULL);
493
494   fnx = g_string_new (file_name);
495
496   if ( ! name_has_suffix (fnx->str))
497     {
498       if ( de->save_as_portable)
499         g_string_append (fnx, ".por");
500       else
501         g_string_append (fnx, ".sav");
502     }
503
504   ds_init_empty (&filename);
505
506   utf8_file_name = g_filename_to_utf8 (fnx->str, -1, NULL, NULL, NULL);
507
508   g_string_free (fnx, TRUE);
509
510   syntax_gen_string (&filename, ss_cstr (utf8_file_name));
511   g_free (utf8_file_name);
512
513   syntax = g_strdup_printf ("%s OUTFILE=%s.",
514                             de->save_as_portable ? "EXPORT" : "SAVE",
515                             ds_cstr (&filename));
516
517   ds_destroy (&filename);
518
519   g_free (execute_syntax_string (de, syntax));
520 }
521
522
523 static void
524 insert_case (PsppireDataWindow *dw)
525 {
526   psppire_data_editor_insert_case (dw->data_editor);
527 }
528
529 static void
530 on_insert_variable (PsppireDataWindow *dw)
531 {
532   psppire_data_editor_insert_variable (dw->data_editor);
533 }
534
535
536 static void
537 display_dict (PsppireDataWindow *de)
538 {
539   execute_const_syntax_string (de, "DISPLAY DICTIONARY.");
540 }
541
542 static void
543 sysfile_info (PsppireDataWindow *de)
544 {
545   GtkWidget *dialog = sysfile_chooser_dialog (PSPPIRE_WINDOW (de));
546
547   if  ( GTK_RESPONSE_ACCEPT == gtk_dialog_run (GTK_DIALOG (dialog)))
548     {
549       struct string filename;
550       gchar *file_name =
551         gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
552
553       gchar *utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL,
554                                                   NULL);
555
556       gchar *syntax;
557
558       ds_init_empty (&filename);
559
560       syntax_gen_string (&filename, ss_cstr (utf8_file_name));
561
562       g_free (utf8_file_name);
563
564       syntax = g_strdup_printf ("SYSFILE INFO %s.", ds_cstr (&filename));
565       g_free (execute_syntax_string (de, syntax));
566     }
567
568   gtk_widget_destroy (dialog);
569 }
570
571
572 /* Callback for data_save_as action. Prompt for a filename and save */
573 static void
574 data_save_as_dialog (PsppireDataWindow *de)
575 {
576   GtkWidget *button_sys;
577   GtkWidget *dialog =
578     gtk_file_chooser_dialog_new (_("Save"),
579                                  GTK_WINDOW (de),
580                                  GTK_FILE_CHOOSER_ACTION_SAVE,
581                                  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
582                                  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
583                                  NULL);
584
585   GtkFileFilter *filter = gtk_file_filter_new ();
586   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
587   gtk_file_filter_add_pattern (filter, "*.sav");
588   gtk_file_filter_add_pattern (filter, "*.SAV");
589   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
590
591   filter = gtk_file_filter_new ();
592   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
593   gtk_file_filter_add_pattern (filter, "*.por");
594   gtk_file_filter_add_pattern (filter, "*.POR");
595   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
596
597   filter = gtk_file_filter_new ();
598   gtk_file_filter_set_name (filter, _("All Files"));
599   gtk_file_filter_add_pattern (filter, "*");
600   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
601
602   {
603     GtkWidget *button_por;
604     GtkWidget *vbox = gtk_vbox_new (TRUE, 5);
605     button_sys =
606       gtk_radio_button_new_with_label (NULL, _("System File"));
607
608     button_por =
609       gtk_radio_button_new_with_label
610       (gtk_radio_button_get_group (GTK_RADIO_BUTTON(button_sys)),
611        _("Portable File"));
612
613     psppire_box_pack_start_defaults (GTK_BOX (vbox), button_sys);
614     psppire_box_pack_start_defaults (GTK_BOX (vbox), button_por);
615
616     gtk_widget_show_all (vbox);
617
618     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
619   }
620
621   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
622     {
623     case GTK_RESPONSE_ACCEPT:
624       {
625         GString *filename =
626           g_string_new
627           (
628            gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog))
629            );
630
631         de->save_as_portable =
632           ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button_sys));
633
634         if ( ! name_has_suffix (filename->str))
635           {
636             if ( de->save_as_portable)
637               g_string_append (filename, ".por");
638             else
639               g_string_append (filename, ".sav");
640           }
641
642         psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
643
644         save_file (PSPPIRE_WINDOW (de));
645
646         g_string_free (filename, TRUE);
647       }
648       break;
649     default:
650       break;
651     }
652
653   gtk_widget_destroy (dialog);
654 }
655
656
657 /* Callback for data_save action.
658  */
659 static void
660 data_save (PsppireWindow *de)
661 {
662   const gchar *fn = psppire_window_get_filename (de);
663
664   if ( NULL != fn)
665     psppire_window_save (de);
666   else
667     data_save_as_dialog (PSPPIRE_DATA_WINDOW (de));
668 }
669
670
671 /* Callback for data_new action.
672    Performs the NEW FILE command */
673 static void
674 new_file (PsppireDataWindow *de)
675 {
676   execute_const_syntax_string (de, "NEW FILE.");
677   psppire_window_set_filename (PSPPIRE_WINDOW (de), NULL);
678 }
679
680
681
682 static void
683 on_edit_paste (PsppireDataWindow  *de)
684 {
685   psppire_data_editor_clip_paste (de->data_editor);
686 }
687
688 static void
689 on_edit_copy (PsppireDataWindow  *de)
690 {
691   psppire_data_editor_clip_copy (de->data_editor);
692 }
693
694
695
696 static void
697 on_edit_cut (PsppireDataWindow  *de)
698 {
699   psppire_data_editor_clip_cut (de->data_editor);
700 }
701
702
703 static void
704 status_bar_activate (PsppireDataWindow  *de, GtkToggleAction *action)
705 {
706   GtkWidget *statusbar = get_widget_assert (de->builder, "status-bar");
707
708   if ( gtk_toggle_action_get_active (action))
709     gtk_widget_show (statusbar);
710   else
711     gtk_widget_hide (statusbar);
712 }
713
714
715 static void
716 grid_lines_activate (PsppireDataWindow  *de, GtkToggleAction *action)
717 {
718   const gboolean grid_visible = gtk_toggle_action_get_active (action);
719
720   psppire_data_editor_show_grid (de->data_editor, grid_visible);
721 }
722
723 static void
724 data_view_activate (PsppireDataWindow  *de)
725 {
726   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
727 }
728
729
730 static void
731 variable_view_activate (PsppireDataWindow  *de)
732 {
733   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
734 }
735
736
737 static void
738 fonts_activate (PsppireDataWindow  *de)
739 {
740   GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (de));
741   PangoFontDescription *current_font;
742   gchar *font_name;
743   GtkWidget *dialog =
744     gtk_font_selection_dialog_new (_("Font Selection"));
745
746
747   current_font = GTK_WIDGET(de->data_editor)->style->font_desc;
748   font_name = pango_font_description_to_string (current_font);
749
750   gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG (dialog), font_name);
751
752   g_free (font_name);
753
754   gtk_window_set_transient_for (GTK_WINDOW (dialog),
755                                 GTK_WINDOW (toplevel));
756
757   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
758     {
759       const gchar *font = gtk_font_selection_dialog_get_font_name
760         (GTK_FONT_SELECTION_DIALOG (dialog));
761
762       PangoFontDescription* font_desc =
763         pango_font_description_from_string (font);
764
765       psppire_data_editor_set_font (de->data_editor, font_desc);
766     }
767
768   gtk_widget_hide (dialog);
769 }
770
771
772
773 /* Callback for the value labels action */
774 static void
775 toggle_value_labels (PsppireDataWindow  *de, GtkToggleAction *ta)
776 {
777   g_object_set (de->data_editor, "value-labels", gtk_toggle_action_get_active (ta), NULL);
778 }
779
780 static void
781 toggle_split_window (PsppireDataWindow  *de, GtkToggleAction *ta)
782 {
783   psppire_data_editor_split_window (de->data_editor,
784                                     gtk_toggle_action_get_active (ta));
785 }
786
787
788 static void
789 file_quit (PsppireDataWindow *de)
790 {
791   /* FIXME: Need to be more intelligent here.
792      Give the user the opportunity to save any unsaved data.
793   */
794   g_object_unref (de->data_store);
795
796   psppire_quit ();
797 }
798
799
800 static void
801 on_recent_data_select (GtkMenuShell *menushell,
802                        PsppireWindow *window)
803 {
804   gchar *file;
805
806   gchar *uri =
807     gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
808
809   file = g_filename_from_uri (uri, NULL, NULL);
810
811   g_free (uri);
812
813   psppire_window_load (window, file);
814
815   g_free (file);
816 }
817
818 static void
819 on_recent_files_select (GtkMenuShell *menushell,   gpointer user_data)
820 {
821   gchar *file;
822
823   GtkWidget *se ;
824
825   gchar *uri =
826     gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (menushell));
827
828   file = g_filename_from_uri (uri, NULL, NULL);
829
830   g_free (uri);
831
832   se = psppire_syntax_window_new ();
833
834   if ( psppire_window_load (PSPPIRE_WINDOW (se), file) ) 
835     gtk_widget_show (se);
836   else
837     gtk_widget_destroy (se);
838
839   g_free (file);
840 }
841
842
843 static void
844 enable_delete_cases (GtkWidget *w, gint case_num, gpointer data)
845 {
846   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
847
848   gtk_action_set_visible (de->delete_cases, case_num != -1);
849 }
850
851
852 static void
853 enable_delete_variables (GtkWidget *w, gint var, gpointer data)
854 {
855   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (data);
856
857   gtk_action_set_visible (de->delete_variables, var != -1);
858 }
859
860 /* Callback for when the datasheet/varsheet is selected */
861 static void
862 on_switch_sheet (GtkNotebook *notebook,
863                  GtkNotebookPage *page,
864                  guint page_num,
865                  gpointer user_data)
866 {
867   PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (user_data);
868
869   GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
870
871   GtkWidget *view_data =
872     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_data");
873
874   GtkWidget *view_variables =
875     gtk_ui_manager_get_widget (uim,"/ui/menubar/view/view_variables");
876
877   switch (page_num)
878     {
879     case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
880       gtk_widget_hide (view_variables);
881       gtk_widget_show (view_data);
882       gtk_action_set_sensitive (de->insert_variable, TRUE);
883       gtk_action_set_sensitive (de->insert_case, FALSE);
884       gtk_action_set_sensitive (de->invoke_goto_dialog, FALSE);
885       break;
886     case PSPPIRE_DATA_EDITOR_DATA_VIEW:
887       gtk_widget_show (view_variables);
888       gtk_widget_hide (view_data);
889       gtk_action_set_sensitive (de->invoke_goto_dialog, TRUE);
890       gtk_action_set_sensitive (de->insert_case, TRUE);
891       break;
892     default:
893       g_assert_not_reached ();
894       break;
895     }
896
897 #if 0
898   update_paste_menuitem (de, page_num);
899 #endif
900 }
901
902
903
904 static void
905 set_unsaved (gpointer w)
906 {
907   psppire_window_set_unsaved (PSPPIRE_WINDOW (w));
908 }
909
910
911 /* Connects the action called ACTION_NAME to HANDLER passing DW as the auxilliary data.
912    Returns a pointer to the action
913 */
914 static GtkAction *
915 connect_action (PsppireDataWindow *dw, const char *action_name, 
916                                     GCallback handler)
917 {
918   GtkAction *action = get_action_assert (dw->builder, action_name);
919  
920   g_signal_connect_swapped (action, "activate", handler, dw);
921
922   return action;
923 }
924
925 /* Initializes as much of a PsppireDataWindow as we can and must before the
926    dataset has been set.
927
928    In particular, the 'menu' member is required in case the "filename" property
929    is set before the "dataset" property: otherwise PsppireWindow will try to
930    modify the menu as part of the "filename" property_set() function and end up
931    with a Gtk-CRITICAL since 'menu' is NULL.  */
932 static void
933 psppire_data_window_init (PsppireDataWindow *de)
934 {
935   GtkUIManager *uim;
936
937   de->builder = builder_new ("data-editor.ui");
938
939   uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
940
941   PSPPIRE_WINDOW (de)->menu =
942     GTK_MENU_SHELL (gtk_ui_manager_get_widget (uim,"/ui/menubar/windows/windows_minimise_all")->parent);
943 }
944
945 static void
946 psppire_data_window_finish_init (PsppireDataWindow *de,
947                                  struct dataset *ds)
948 {
949   static const struct dataset_callbacks cbs =
950     {
951       set_unsaved,                    /* changed */
952       transformation_change_callback, /* transformations_changed */
953     };
954
955   PsppireDict *dict;
956
957   GtkWidget *menubar;
958   GtkWidget *hb ;
959   GtkWidget *sb ;
960
961   GtkWidget *box = gtk_vbox_new (FALSE, 0);
962
963   de->dataset = ds;
964   dict = psppire_dict_new_from_dict (dataset_dict (ds));
965   de->var_store = psppire_var_store_new (dict);
966   de->data_store = psppire_data_store_new (dict);
967   psppire_data_store_set_reader (de->data_store, NULL);
968
969   menubar = get_widget_assert (de->builder, "menubar");
970   hb = get_widget_assert (de->builder, "handlebox1");
971   sb = get_widget_assert (de->builder, "status-bar");
972
973   de->data_editor =
974     PSPPIRE_DATA_EDITOR (psppire_data_editor_new (de, de->var_store,
975                                                   de->data_store));
976
977   g_signal_connect_swapped (de->data_store, "case-changed",
978                             G_CALLBACK (set_unsaved), de);
979
980   g_signal_connect_swapped (de->data_store, "case-inserted",
981                             G_CALLBACK (set_unsaved), de);
982
983   g_signal_connect_swapped (de->data_store, "cases-deleted",
984                             G_CALLBACK (set_unsaved), de);
985
986   dataset_set_callbacks (de->dataset, &cbs, de);
987
988   connect_help (de->builder);
989
990   gtk_box_pack_start (GTK_BOX (box), menubar, FALSE, TRUE, 0);
991   gtk_box_pack_start (GTK_BOX (box), hb, FALSE, TRUE, 0);
992   gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (de->data_editor), TRUE, TRUE, 0);
993   gtk_box_pack_start (GTK_BOX (box), sb, FALSE, TRUE, 0);
994
995   gtk_container_add (GTK_CONTAINER (de), box);
996
997   set_cut_copy_menuitem_sensitivity (de, FALSE);
998
999   g_signal_connect_swapped (de->data_editor, "data-selection-changed",
1000                             G_CALLBACK (set_cut_copy_menuitem_sensitivity), de);
1001
1002
1003   set_paste_menuitem_sensitivity (de, FALSE);
1004
1005   g_signal_connect_swapped (de->data_editor, "data-available-changed",
1006                             G_CALLBACK (set_paste_menuitem_sensitivity), de);
1007
1008   g_signal_connect (dict, "weight-changed",
1009                     G_CALLBACK (on_weight_change),
1010                     de);
1011
1012   g_signal_connect (dict, "filter-changed",
1013                     G_CALLBACK (on_filter_change),
1014                     de);
1015
1016   g_signal_connect (dict, "split-changed",
1017                     G_CALLBACK (on_split_change),
1018                     de);
1019
1020
1021   connect_action (de, "edit_copy", G_CALLBACK (on_edit_copy));
1022
1023   connect_action (de, "edit_cut", G_CALLBACK (on_edit_cut));
1024
1025   connect_action (de, "file_new_data", G_CALLBACK (new_file));
1026
1027   connect_action (de, "file_import-text", G_CALLBACK (text_data_import_assistant));
1028
1029   connect_action (de, "file_save", G_CALLBACK (data_save));
1030  
1031   connect_action (de, "file_open", G_CALLBACK (open_window));
1032
1033   connect_action (de, "file_save_as", G_CALLBACK (data_save_as_dialog));
1034
1035   connect_action (de, "file_information_working-file", G_CALLBACK (display_dict));
1036
1037   connect_action (de, "file_information_external-file", G_CALLBACK (sysfile_info));
1038
1039   connect_action (de, "edit_paste", G_CALLBACK (on_edit_paste));
1040
1041   de->insert_case = connect_action (de, "edit_insert-case", G_CALLBACK (insert_case));
1042
1043   de->insert_variable = connect_action (de, "action_insert-variable", G_CALLBACK (on_insert_variable));
1044
1045   de->invoke_goto_dialog = connect_action (de, "edit_goto-case", G_CALLBACK (goto_case_dialog));
1046
1047   g_signal_connect_swapped (get_action_assert (de->builder, "view_value-labels"), "toggled", G_CALLBACK (toggle_value_labels), de);
1048
1049   {
1050     de->delete_cases = get_action_assert (de->builder, "edit_clear-cases");
1051
1052     g_signal_connect_swapped (de->delete_cases, "activate", G_CALLBACK (psppire_data_editor_delete_cases), de->data_editor);
1053
1054     gtk_action_set_visible (de->delete_cases, FALSE);
1055   }
1056
1057
1058   {
1059     de->delete_variables = get_action_assert (de->builder, "edit_clear-variables");
1060
1061     g_signal_connect_swapped (de->delete_variables, "activate", G_CALLBACK (psppire_data_editor_delete_variables), de->data_editor);
1062
1063     gtk_action_set_visible (de->delete_variables, FALSE);
1064   }
1065
1066
1067   connect_action (de, "data_transpose", G_CALLBACK (transpose_dialog));
1068
1069   connect_action (de, "data_select-cases", G_CALLBACK (select_cases_dialog));
1070  
1071   connect_action (de, "data_sort-cases", G_CALLBACK (sort_cases_dialog));
1072
1073   connect_action (de, "data_aggregate", G_CALLBACK (aggregate_dialog));
1074
1075   connect_action (de, "transform_compute", G_CALLBACK (compute_dialog));
1076
1077   connect_action (de, "edit_find", G_CALLBACK (find_dialog));
1078
1079   connect_action (de, "data_split-file", G_CALLBACK (split_file_dialog));
1080
1081   connect_action (de, "data_weight-cases", G_CALLBACK (weight_cases_dialog));
1082
1083
1084   connect_action (de, "utilities_variables", G_CALLBACK (variable_info_dialog));
1085  
1086   connect_action (de, "oneway-anova", G_CALLBACK (oneway_anova_dialog));
1087
1088   connect_action (de, "indep-t-test", G_CALLBACK (t_test_independent_samples_dialog));
1089
1090   connect_action (de, "paired-t-test", G_CALLBACK (t_test_paired_samples_dialog));
1091
1092   connect_action (de, "one-sample-t-test", G_CALLBACK (t_test_one_sample_dialog));
1093
1094   connect_action (de, "utilities_comments", G_CALLBACK (comments_dialog));
1095  
1096   connect_action (de, "transform_rank", G_CALLBACK (rank_dialog));
1097  
1098   connect_action (de, "transform_recode-same", G_CALLBACK (recode_same_dialog));
1099  
1100   connect_action (de, "transform_recode-different", G_CALLBACK (recode_different_dialog));
1101
1102   connect_action (de, "analyze_descriptives", G_CALLBACK (descriptives_dialog));
1103  
1104   connect_action (de, "analyze_frequencies", G_CALLBACK (frequencies_dialog));
1105  
1106   connect_action (de, "crosstabs", G_CALLBACK (crosstabs_dialog));
1107  
1108   connect_action (de, "analyze_explore", G_CALLBACK (examine_dialog));
1109  
1110   connect_action (de, "linear-regression", G_CALLBACK (regression_dialog));
1111  
1112   connect_action (de, "reliability", G_CALLBACK (reliability_dialog));
1113  
1114   connect_action (de, "roc-curve", G_CALLBACK (roc_dialog));
1115
1116   connect_action (de, "correlation", G_CALLBACK (correlation_dialog));
1117  
1118   connect_action (de, "factor-analysis", G_CALLBACK (factor_dialog));
1119
1120   connect_action (de, "chi-square", G_CALLBACK (chisquare_dialog));
1121
1122   connect_action (de, "binomial", G_CALLBACK (binomial_dialog));
1123
1124   connect_action (de, "k-related-samples", G_CALLBACK (k_related_dialog));
1125  
1126
1127   {
1128     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1129
1130     GtkWidget *recent_data =
1131       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-data");
1132
1133     GtkWidget *recent_files =
1134       gtk_ui_manager_get_widget (uim,"/ui/menubar/file/file_recent-files");
1135
1136
1137     GtkWidget *menu_data =
1138       gtk_recent_chooser_menu_new_for_manager (the_recent_mgr);
1139
1140     GtkWidget *menu_files =
1141       gtk_recent_chooser_menu_new_for_manager (the_recent_mgr);
1142
1143     {
1144       GtkRecentFilter *filter = gtk_recent_filter_new ();
1145
1146       gtk_recent_filter_add_pattern (filter, "*.sav");
1147       gtk_recent_filter_add_pattern (filter, "*.SAV");
1148       gtk_recent_filter_add_pattern (filter, "*.por");
1149       gtk_recent_filter_add_pattern (filter, "*.POR");
1150
1151       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_data), GTK_RECENT_SORT_MRU);
1152
1153       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_data), filter);
1154     }
1155
1156     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_data), menu_data);
1157
1158
1159     g_signal_connect (menu_data, "selection-done", G_CALLBACK (on_recent_data_select), de);
1160
1161     {
1162       GtkRecentFilter *filter = gtk_recent_filter_new ();
1163
1164       gtk_recent_filter_add_pattern (filter, "*.sps");
1165       gtk_recent_filter_add_pattern (filter, "*.SPS");
1166
1167       gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu_files), GTK_RECENT_SORT_MRU);
1168
1169       gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu_files), filter);
1170     }
1171
1172     gtk_menu_item_set_submenu (GTK_MENU_ITEM (recent_files), menu_files);
1173
1174     g_signal_connect (menu_files, "selection-done", G_CALLBACK (on_recent_files_select), de);
1175
1176   }
1177
1178   connect_action (de, "file_new_syntax", G_CALLBACK (create_syntax_window));
1179
1180
1181   g_signal_connect (de->data_editor,
1182                     "cases-selected",
1183                     G_CALLBACK (enable_delete_cases),
1184                     de);
1185
1186   g_signal_connect (de->data_editor,
1187                     "variables-selected",
1188                     G_CALLBACK (enable_delete_variables),
1189                     de);
1190
1191
1192   g_signal_connect (de->data_editor,
1193                     "switch-page",
1194                     G_CALLBACK (on_switch_sheet), de);
1195
1196   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
1197   gtk_notebook_set_current_page (GTK_NOTEBOOK (de->data_editor), PSPPIRE_DATA_EDITOR_DATA_VIEW);
1198
1199   connect_action (de, "view_statusbar", G_CALLBACK (status_bar_activate));
1200
1201   connect_action (de, "view_gridlines", G_CALLBACK (grid_lines_activate));
1202
1203   connect_action (de, "view_data", G_CALLBACK (data_view_activate));
1204
1205   connect_action (de, "view_variables", G_CALLBACK (variable_view_activate));
1206
1207   connect_action (de, "view_fonts", G_CALLBACK (fonts_activate));
1208
1209   connect_action (de, "file_quit", G_CALLBACK (file_quit));
1210
1211   connect_action (de, "transform_run-pending", G_CALLBACK (execute));
1212
1213   connect_action (de, "windows_minimise_all", G_CALLBACK (psppire_window_minimise_all));
1214
1215   g_signal_connect_swapped (get_action_assert (de->builder, "windows_split"), "toggled", G_CALLBACK (toggle_split_window), de);
1216
1217   {
1218     GtkUIManager *uim = GTK_UI_MANAGER (get_object_assert (de->builder, "uimanager1", GTK_TYPE_UI_MANAGER));
1219
1220     merge_help_menu (uim);
1221   }
1222
1223   {
1224     GtkWidget *data_sheet_cases_popup_menu = get_widget_assert (de->builder,
1225                                                                 "datasheet-cases-popup");
1226
1227     GtkWidget *var_sheet_variable_popup_menu = get_widget_assert (de->builder,
1228                                                                   "varsheet-variable-popup");
1229
1230     GtkWidget *data_sheet_variable_popup_menu = get_widget_assert (de->builder,
1231                                                                    "datasheet-variable-popup");
1232
1233     g_signal_connect_swapped (get_action_assert (de->builder, "sort-up"), "activate",
1234                               G_CALLBACK (psppire_data_editor_sort_ascending),
1235                               de->data_editor);
1236
1237     g_signal_connect_swapped (get_action_assert (de->builder, "sort-down"), "activate",
1238                               G_CALLBACK (psppire_data_editor_sort_descending),
1239                               de->data_editor);
1240
1241     g_object_set (de->data_editor,
1242                   "datasheet-column-menu", data_sheet_variable_popup_menu,
1243                   "datasheet-row-menu", data_sheet_cases_popup_menu,
1244                   "varsheet-row-menu", var_sheet_variable_popup_menu,
1245                   NULL);
1246   }
1247
1248   gtk_widget_show (GTK_WIDGET (de->data_editor));
1249   gtk_widget_show (box);
1250
1251   the_data_window = de;
1252 }
1253
1254 static void
1255 psppire_data_window_dispose (GObject *object)
1256 {
1257   PsppireDataWindow *dw = PSPPIRE_DATA_WINDOW (object);
1258
1259   if (dw->builder != NULL)
1260     {
1261       g_object_unref (dw->builder);
1262       dw->builder = NULL;
1263     }
1264
1265   if (the_data_window == dw)
1266     the_data_window = NULL;
1267
1268   G_OBJECT_CLASS (parent_class)->dispose (object);
1269 }
1270
1271 static void
1272 psppire_data_window_set_property (GObject         *object,
1273                                   guint            prop_id,
1274                                   const GValue    *value,
1275                                   GParamSpec      *pspec)
1276 {
1277   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1278
1279   switch (prop_id)
1280     {
1281     case PROP_DATASET:
1282       psppire_data_window_finish_init (window, g_value_get_pointer (value));
1283       break;
1284     default:
1285       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1286       break;
1287     };
1288 }
1289
1290 static void
1291 psppire_data_window_get_property (GObject         *object,
1292                                   guint            prop_id,
1293                                   GValue          *value,
1294                                   GParamSpec      *pspec)
1295 {
1296   PsppireDataWindow *window = PSPPIRE_DATA_WINDOW (object);
1297
1298   switch (prop_id)
1299     {
1300     case PROP_DATASET:
1301       g_value_set_pointer (value, window->dataset);
1302       break;
1303     default:
1304       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1305       break;
1306     };
1307 }
1308
1309
1310 GtkWidget*
1311 psppire_data_window_new (struct dataset *ds)
1312 {
1313   return GTK_WIDGET (
1314     g_object_new (
1315       psppire_data_window_get_type (),
1316       /* TRANSLATORS: This will form a filename.  Please avoid whitespace. */
1317       "filename", _("PSPP-data"),
1318       "description", _("Data Editor"),
1319       "dataset", ds,
1320       NULL));
1321 }
1322
1323
1324 static void
1325 psppire_data_window_iface_init (PsppireWindowIface *iface)
1326 {
1327   iface->save = save_file;
1328   iface->load = load_file;
1329 }
1330
1331 \f
1332 PsppireDataWindow *
1333 psppire_default_data_window (void)
1334 {
1335   if (the_data_window == NULL)
1336     gtk_widget_show (psppire_data_window_new (dataset_create ()));
1337   return the_data_window;
1338 }