When inserting new cases into the data sheet, initialise them with blank/SYSMIS
[pspp-builds.git] / src / ui / gui / menu-actions.c
1 /* 
2     PSPPIRE --- A Graphical User Interface for PSPP
3     Copyright (C) 2004, 2005  Free Software Foundation
4     Written by John Darrington
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19     02110-1301, USA. */
20
21 #include <config.h>
22 #include <stdlib.h>
23
24 #include <data/file-handle-def.h>
25 #include <data/sys-file-reader.h>
26 #include <data/case.h>
27
28 #include <glade/glade.h>
29 #include <gtk/gtk.h>
30
31 #include <gtksheet/gtksheet.h>
32 #include "helper.h"
33 #include "menu-actions.h"
34 #include "psppire-variable.h"
35 #include "psppire-dict.h"
36
37 #include "var-sheet.h"
38 #include "data-sheet.h"
39
40 #include "psppire-var-store.h"
41 #include "psppire-data-store.h"
42
43 #define _(A) A
44 #define N_(A) A
45
46
47 extern GladeXML *xml;
48
49
50 extern PsppireDict *the_dictionary ;
51 extern PsppireCaseArray *the_cases ;
52
53
54 static struct file_handle *psppire_handle = 0;
55
56 static const gchar handle_name[] = "psppire_handle";
57
58 static const gchar untitled[] = _("Untitled");
59
60 static const gchar window_title[]=_("PSPP Data Editor");
61
62
63 static void
64 psppire_set_window_title(const gchar *text)
65 {
66   GtkWidget *data_editor = get_widget_assert(xml, "data_editor");
67   
68   gchar *title = g_strdup_printf("%s --- %s", text, window_title);
69
70   gtk_window_set_title(GTK_WINDOW(data_editor), title);
71
72   g_free(title);
73 }
74
75 void
76 on_new1_activate                       (GtkMenuItem     *menuitem,
77                                         gpointer         user_data)
78 {
79   psppire_dict_clear(the_dictionary);
80   psppire_case_array_clear(the_cases);
81
82   psppire_set_window_title(untitled);
83
84   if (psppire_handle)
85     fh_free(psppire_handle);
86   psppire_handle = 0 ;
87 }
88
89 static gboolean
90 populate_case_from_reader(struct ccase *c, gpointer aux)
91 {
92   struct sfm_reader *reader = aux;
93
94   return sfm_read_case(reader, c);
95 }
96
97
98 void
99 on_open1_activate                      (GtkMenuItem     *menuitem,
100                                         gpointer         user_data)
101 {
102   bool finished = FALSE;
103
104   GtkWidget *dialog;
105   GtkWidget *data_editor  = get_widget_assert(xml, "data_editor");
106   GtkFileFilter *filter ;
107  
108   dialog = gtk_file_chooser_dialog_new (_("Open"),
109                                         GTK_WINDOW(data_editor),
110                                         GTK_FILE_CHOOSER_ACTION_OPEN,
111                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
112                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
113                                         NULL);
114
115   filter = gtk_file_filter_new();
116   gtk_file_filter_set_name(filter, _("System Files (*.sav)"));
117   gtk_file_filter_add_pattern(filter, "*.sav");
118   gtk_file_filter_add_pattern(filter, "*.SAV");
119   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
120
121   filter = gtk_file_filter_new();
122   gtk_file_filter_set_name(filter, _("Portable Files (*.por) "));
123   gtk_file_filter_add_pattern(filter, "*.por");
124   gtk_file_filter_add_pattern(filter, "*.POR");
125   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
126
127   filter = gtk_file_filter_new();
128   gtk_file_filter_set_name(filter, _("All Files"));
129   gtk_file_filter_add_pattern(filter, "*");
130   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
131
132
133   do {
134
135     if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
136       {
137         PsppireVarStore *var_store ;
138         PsppireDataStore *data_store ;
139         struct dictionary *new_dict;
140         struct sfm_read_info ri;
141         struct sfm_reader *reader ; 
142
143         GtkWidget *data_sheet = get_widget_assert(xml, "data_sheet");
144         GtkWidget *var_sheet = get_widget_assert(xml, "variable_sheet");
145         gchar *file_name;
146
147         g_assert(data_sheet);
148         g_assert(var_sheet);
149
150         file_name = gtk_file_chooser_get_filename
151           (GTK_FILE_CHOOSER (dialog));
152
153         if ( psppire_handle ) 
154           fh_free(psppire_handle);
155
156         psppire_handle = 
157           fh_create_file (handle_name, file_name, fh_default_properties());
158
159         if ( !psppire_handle ) 
160           {
161             g_warning("Cannot read handle for reading system file \"%s\"\n", 
162                       file_name);
163             continue;
164           }
165
166
167         reader = sfm_open_reader (psppire_handle, &new_dict, &ri);
168       
169         if ( ! reader ) 
170           continue;
171
172         the_dictionary = psppire_dict_new_from_dict(new_dict);
173
174         var_store = 
175           PSPPIRE_VAR_STORE(gtk_sheet_get_model(GTK_SHEET(var_sheet)));
176         
177         psppire_var_store_set_dictionary(var_store, the_dictionary);
178
179
180         data_store = 
181           PSPPIRE_DATA_STORE(gtk_sheet_get_model(GTK_SHEET(data_sheet)));
182         
183
184         psppire_data_store_set_dictionary(data_store,
185                                           the_dictionary);
186
187         psppire_case_array_clear(data_store->cases);
188
189
190         psppire_set_window_title(basename(file_name));
191
192         g_free (file_name);
193
194         {
195           const int ni = dict_get_next_value_idx(the_dictionary->dict);
196           gint case_num;
197           if ( ni == 0 ) 
198             goto done;
199       
200
201           for(case_num=0;;case_num++)
202             {
203               if (!psppire_case_array_append_case(the_cases, 
204                                                   populate_case_from_reader, 
205                                                   reader))
206                 break;
207             }
208         }
209
210         sfm_close_reader(reader);
211         finished = TRUE;
212       }
213     else
214       {
215         finished = TRUE;
216       }
217   } while ( ! finished ) ;
218
219  done:
220   gtk_widget_destroy (dialog);
221 }
222
223
224 /* Re initialise HANDLE, by interrogating the user for a new file name */
225 static void
226 recreate_save_handle(struct file_handle **handle)
227 {
228   GtkWidget *dialog;
229
230   GtkWidget *data_editor  = get_widget_assert(xml, "data_editor");
231
232   dialog = gtk_file_chooser_dialog_new (_("Save Data As"),
233                                         GTK_WINDOW(data_editor),
234                                         GTK_FILE_CHOOSER_ACTION_SAVE,
235                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
236                                         GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
237                                         NULL);
238
239   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
240     {
241       char *file_name = gtk_file_chooser_get_filename
242         (GTK_FILE_CHOOSER (dialog));
243
244 #if 0
245       if ( *handle ) 
246         destroy_file_handle(*handle, 0);
247 #endif
248       *handle = fh_create_file (handle_name, file_name, fh_default_properties());
249
250       psppire_set_window_title(basename(file_name));
251
252       g_free (file_name);
253     }
254
255   gtk_widget_destroy (dialog);
256 }
257
258 void
259 on_save1_activate                      (GtkMenuItem     *menuitem,
260                                         gpointer         user_data)
261 {
262   GtkSheet *data_sheet ;
263   PsppireDataStore *data_store ;
264
265   if ( ! psppire_handle ) 
266     recreate_save_handle(&psppire_handle);
267   
268   data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
269   data_store = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
270   
271   if ( psppire_handle ) 
272     psppire_data_store_create_system_file(data_store,
273                                        psppire_handle);
274 }
275
276
277 void
278 on_save_as1_activate                   (GtkMenuItem     *menuitem,
279                                         gpointer         user_data)
280 {
281   GtkSheet *data_sheet ;
282   PsppireDataStore *data_store ;
283
284   recreate_save_handle(&psppire_handle);
285   if ( ! psppire_handle ) 
286     return ;
287
288   data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
289   data_store = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
290
291   if ( psppire_handle ) 
292     psppire_data_store_create_system_file(data_store,
293                                        psppire_handle);
294 }
295
296
297 void
298 on_quit1_activate                      (GtkMenuItem     *menuitem,
299                                         gpointer         user_data)
300 {
301   gtk_main_quit();
302 }
303
304
305 void
306 on_cut1_activate                       (GtkMenuItem     *menuitem,
307                                         gpointer         user_data)
308 {
309
310 }
311
312
313 void
314 on_copy1_activate                      (GtkMenuItem     *menuitem,
315                                         gpointer         user_data)
316 {
317
318 }
319
320
321 void
322 on_paste1_activate                     (GtkMenuItem     *menuitem,
323                                         gpointer         user_data)
324 {
325
326 }
327
328 /* Fill a case with SYSMIS for numeric and whitespace for string
329    variables respectively */ 
330 static gboolean 
331 blank_case(struct ccase *cc, gpointer _dict)
332 {
333   gint i;
334   PsppireDict *dict = _dict;
335
336   for(i = 0 ; i < psppire_dict_get_var_cnt(dict); ++i ) 
337     {
338       union value *val ;
339
340       const struct PsppireVariable *var = psppire_dict_get_variable(dict, i);
341       
342       gint idx = psppire_variable_get_index(var);
343
344       val = case_data_rw(cc, idx) ;
345
346       if ( psppire_variable_get_type(var) == ALPHA ) 
347         memset(val->s, ' ', psppire_variable_get_width(var));
348       else
349         val->f = SYSMIS;
350
351       case_unshare(cc);
352     }
353
354   return TRUE;
355 }
356
357
358 void
359 on_insert1_activate                    (GtkMenuItem     *menuitem,
360                                         gpointer         user_data)
361 {
362   GtkNotebook *notebook = GTK_NOTEBOOK(get_widget_assert(xml, "notebook1"));
363   gint page = -1;
364
365   page = gtk_notebook_get_current_page(notebook);
366
367   switch (page) 
368     {
369     case PAGE_DATA_SHEET:
370       {
371         GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
372         PsppireDataStore *data_store = 
373           PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
374
375         psppire_case_array_insert_case(data_store->cases, 
376                                        data_sheet->range.row0, 
377                                        blank_case, the_dictionary);
378       }
379       break;
380     case PAGE_VAR_SHEET:
381       {
382         GtkSheet *var_sheet = 
383           GTK_SHEET(get_widget_assert(xml, "variable_sheet"));
384
385         PsppireVarStore *var_store = 
386           PSPPIRE_VAR_STORE(gtk_sheet_get_model(var_sheet));
387
388         psppire_dict_insert_variable(var_store->dict, var_sheet->range.row0, 0);
389       }
390       break;
391     }
392 }
393
394 void
395 on_delete1_activate                    (GtkMenuItem     *menuitem,
396                                         gpointer         user_data)
397 {
398   gint page = -1;
399   GtkWidget *notebook = get_widget_assert(xml, "notebook1");
400
401   page = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
402   switch ( page) 
403     {
404     case PAGE_DATA_SHEET:
405       {
406         GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
407         PsppireDataStore *data_store = 
408           PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
409
410         psppire_case_array_delete_cases(data_store->cases, 
411                                     data_sheet->range.row0, 
412                                     1 + data_sheet->range.rowi 
413                                     - data_sheet->range.row0  );
414       }
415       break;
416     case PAGE_VAR_SHEET:
417       {
418         GtkSheet *var_sheet = 
419           GTK_SHEET(get_widget_assert(xml, "variable_sheet"));
420
421         PsppireVarStore *var_store = 
422           PSPPIRE_VAR_STORE(gtk_sheet_get_model(var_sheet));
423
424         psppire_dict_delete_variables(var_store->dict, 
425                                    var_sheet->range.row0,
426                                    1 + var_sheet->range.rowi 
427                                    - var_sheet->range.row0  );
428       }
429       break;
430     }
431 }
432
433
434 void
435 on_about1_activate(GtkMenuItem     *menuitem,
436                    gpointer         user_data)
437 {
438   GtkWidget *about =  get_widget_assert(xml, "aboutdialog1");
439   
440   
441   GdkPixbuf *pb  = gdk_pixbuf_new_from_file_at_size( "pspplogo.png", 64, 64, 0);
442
443   gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about), pb);
444
445   gtk_widget_show(about);
446
447   gtk_window_set_transient_for(GTK_WINDOW(about), 
448                                GTK_WINDOW(get_widget_assert(xml, "data_editor")));
449 }
450
451
452
453 void
454 on_toolbars1_activate
455                      (GtkMenuItem     *menuitem,
456                                         gpointer         user_data)
457 {
458
459
460 }
461
462 void
463 on_value_labels1_activate(GtkCheckMenuItem     *menuitem,
464                           gpointer         user_data)
465 {
466   GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
467   PsppireDataStore *ds = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
468                 
469   psppire_data_store_show_labels(ds, 
470                               gtk_check_menu_item_get_active(menuitem));
471 }
472
473 void
474 on_status_bar1_activate(GtkCheckMenuItem     *menuitem,
475  gpointer         user_data)
476 {
477
478   if ( gtk_check_menu_item_get_active(menuitem) ) 
479     gtk_widget_show(get_widget_assert(xml, "statusbar1"));
480   else
481     gtk_widget_hide(get_widget_assert(xml, "statusbar1"));
482 }
483
484 void
485 on_grid_lines1_activate(GtkCheckMenuItem     *menuitem,
486  gpointer         user_data)
487 {
488
489   const bool grid_visible = gtk_check_menu_item_get_active(menuitem); 
490
491   gtk_sheet_show_grid(GTK_SHEET(get_widget_assert(xml, "variable_sheet")),
492                       grid_visible);
493
494   gtk_sheet_show_grid(GTK_SHEET(get_widget_assert(xml, "data_sheet")),
495                       grid_visible);
496 }
497
498
499 void
500 on_fonts1_activate(GtkMenuItem     *menuitem,
501  gpointer         user_data)
502 {
503   static GtkWidget *dialog = 0 ; 
504   if ( !dialog ) 
505     dialog   = gtk_font_selection_dialog_new(_("Font Selection"));
506
507   gtk_window_set_transient_for(GTK_WINDOW(dialog), 
508                                GTK_WINDOW(get_widget_assert(xml, "data_editor")));
509
510
511   if ( GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(dialog)) ) 
512     {
513       GtkSheet *data_sheet = 
514         GTK_SHEET(get_widget_assert(xml, "data_sheet"));
515
516       GtkSheet *var_sheet = 
517         GTK_SHEET(get_widget_assert(xml, "variable_sheet"));
518
519       PsppireDataStore *ds = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
520       PsppireVarStore *vs = PSPPIRE_VAR_STORE(gtk_sheet_get_model(var_sheet));
521
522       const gchar *font = gtk_font_selection_dialog_get_font_name 
523         (GTK_FONT_SELECTION_DIALOG(dialog));
524
525       PangoFontDescription* font_desc = 
526         pango_font_description_from_string(font);
527
528       psppire_var_store_set_font(vs, font_desc);
529       psppire_data_store_set_font(ds, font_desc);
530     }
531   
532   gtk_widget_hide(dialog);
533
534 }
535
536
537 static GtkWidget *menuitems[2];
538 static GtkNotebook *notebook = 0;
539
540 static void
541 switch_menus(gint page)
542 {
543   switch (page) 
544     {
545     case PAGE_VAR_SHEET:
546       gtk_widget_hide(menuitems[PAGE_VAR_SHEET]);
547       gtk_widget_show(menuitems[PAGE_DATA_SHEET]);
548       break;
549     case PAGE_DATA_SHEET:
550       gtk_widget_show(menuitems[PAGE_VAR_SHEET]);
551       gtk_widget_hide(menuitems[PAGE_DATA_SHEET]);
552       break;
553     default:
554       g_assert_not_reached();
555       break;
556     }
557 }
558
559
560 void
561 select_sheet(gint page)
562 {
563   gtk_notebook_set_current_page(notebook, page);
564   switch_menus(page);
565 }
566
567
568
569 static void
570 data_var_select(GtkNotebook *notebook,
571                 GtkNotebookPage *page,
572                 guint page_num,
573                 gpointer user_data)
574 {
575   switch_menus(page_num);
576 }
577
578 static void
579 var_data_selection_init()
580 {
581   notebook = GTK_NOTEBOOK(get_widget_assert(xml, "notebook1"));
582   menuitems[PAGE_DATA_SHEET] = get_widget_assert(xml, "data1");
583   menuitems[PAGE_VAR_SHEET] = get_widget_assert(xml, "variables1");
584
585   gtk_notebook_set_current_page(notebook, PAGE_DATA_SHEET);
586   gtk_widget_hide(menuitems[PAGE_DATA_SHEET]);
587   gtk_widget_show(menuitems[PAGE_VAR_SHEET]);
588
589
590   g_signal_connect(G_OBJECT(notebook), "switch-page",
591                    G_CALLBACK(data_var_select), 0);
592
593 }
594
595
596 void
597 on_data1_activate(GtkMenuItem     *menuitem,
598                   gpointer         user_data)
599 {
600   select_sheet(PAGE_DATA_SHEET);
601 }
602
603
604 void
605 on_variables1_activate(GtkMenuItem     *menuitem,
606                   gpointer         user_data)
607 {
608   select_sheet(PAGE_VAR_SHEET);
609 }
610
611
612 /* Callback which occurs when gtk_main is entered */
613 gboolean
614 callbacks_on_init(gpointer data)
615 {
616   psppire_set_window_title(untitled);
617
618   var_data_selection_init();
619
620   return FALSE;
621 }