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