Make the missing value code do more work, so that its callers can do
[pspp-builds.git] / src / ui / gui / menu-actions.c
1 /*
2     PSPPIRE --- A Graphical User Interface for PSPP
3     Copyright (C) 2004, 2005, 2006  Free Software Foundation
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18     02110-1301, USA. */
19
20 #include <config.h>
21 #include <stdlib.h>
22 #include <gettext.h>
23 #define _(msgid) gettext (msgid)
24 #define N_(msgid) msgid
25
26 #include <math/sort.h>
27
28 #include <data/casefile.h>
29 #include <data/file-handle-def.h>
30 #include <data/sys-file-reader.h>
31 #include <data/case.h>
32 #include <data/variable.h>
33
34 #include <glade/glade.h>
35 #include <gtk/gtk.h>
36
37 #include <libpspp/str.h>
38
39 #include <gtksheet/gtksheet.h>
40 #include "helper.h"
41 #include "menu-actions.h"
42
43 #include "psppire-dict.h"
44
45 #include "var-sheet.h"
46 #include "data-sheet.h"
47
48 #include "psppire-var-store.h"
49 #include "psppire-data-store.h"
50
51 #include "sort-cases-dialog.h"
52
53
54 extern GladeXML *xml;
55
56
57 extern PsppireDict *the_dictionary ;
58
59 static struct file_handle *psppire_handle = 0;
60
61 static const gchar handle_name[] = "psppire_handle";
62
63 static const gchar untitled[] = N_("Untitled");
64
65 static const gchar window_title[] = N_("PSPP Data Editor");
66
67
68 /* Sets the title bar to TEXT */
69 static void
70 psppire_set_window_title(const gchar *text)
71 {
72   GtkWidget *data_editor = get_widget_assert(xml, "data_editor");
73
74   gchar *title = g_strdup_printf("%s --- %s", text, gettext(window_title));
75
76   gtk_window_set_title(GTK_WINDOW(data_editor), title);
77
78   g_free(title);
79 }
80
81 /* Clear the active file and set the data and var sheets to
82    reflect this.
83  */
84 gboolean
85 clear_file(void)
86 {
87   PsppireDataStore *data_store ;
88   GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
89   GtkSheet *var_sheet = GTK_SHEET(get_widget_assert(xml, "variable_sheet"));
90
91   gtk_sheet_set_active_cell(data_sheet, -1, -1);
92   gtk_sheet_set_active_cell(var_sheet, 0, 0);
93
94   if ( GTK_WIDGET_REALIZED(GTK_WIDGET(data_sheet)))
95     gtk_sheet_unselect_range(data_sheet);
96
97   if ( GTK_WIDGET_REALIZED(GTK_WIDGET(var_sheet)))
98     gtk_sheet_unselect_range(var_sheet);
99
100   gtk_sheet_moveto(data_sheet, 0, 0, 0.0, 0.0);
101   gtk_sheet_moveto(var_sheet,  0, 0, 0.0, 0.0);
102
103   data_store = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
104
105   psppire_data_store_clear(data_store);
106
107   psppire_set_window_title(gettext(untitled));
108
109   if (psppire_handle)
110     fh_free(psppire_handle);
111   psppire_handle = 0 ;
112
113   return TRUE;
114 }
115
116 void
117 on_new1_activate                       (GtkMenuItem     *menuitem,
118                                         gpointer         user_data)
119 {
120   clear_file();
121 }
122
123
124
125 /* Load a system file.
126    Return TRUE if successfull
127 */
128 gboolean
129 load_system_file(const gchar *file_name)
130 {
131   int var_cnt ;
132
133   PsppireVarStore *var_store ;
134   PsppireDataStore *data_store ;
135   struct dictionary *new_dict;
136   struct sfm_read_info ri;
137   struct sfm_reader *reader ;
138
139   GtkWidget *data_sheet = get_widget_assert(xml, "data_sheet");
140   GtkWidget *var_sheet = get_widget_assert(xml, "variable_sheet");
141
142   g_assert(data_sheet);
143   g_assert(var_sheet);
144
145   clear_file();
146
147   psppire_handle =
148     fh_create_file (handle_name, file_name, fh_default_properties());
149
150   if ( !psppire_handle )
151     {
152       g_warning("Cannot read handle for reading system file \"%s\"\n",
153                 file_name);
154       return FALSE;
155     }
156
157   reader = sfm_open_reader (psppire_handle, &new_dict, &ri);
158
159   if ( ! reader )
160     return FALSE;
161
162   /* FIXME: We need a better way of updating a dictionary than this */
163   the_dictionary = psppire_dict_new_from_dict(new_dict);
164
165   var_store =
166     PSPPIRE_VAR_STORE(gtk_sheet_get_model(GTK_SHEET(var_sheet)));
167
168   psppire_var_store_set_dictionary(var_store, the_dictionary);
169
170
171   data_store =
172     PSPPIRE_DATA_STORE(gtk_sheet_get_model(GTK_SHEET(data_sheet)));
173
174   psppire_data_store_set_dictionary(data_store,
175                                     the_dictionary);
176
177   psppire_set_window_title(basename(file_name));
178
179   var_cnt = dict_get_next_value_idx(the_dictionary->dict);
180   if ( var_cnt == 0 )
181     return FALSE;
182
183
184   for(;;)
185     {
186       struct ccase c;
187       case_create(&c, var_cnt);
188       if ( 0 == sfm_read_case (reader, &c) )
189         {
190           case_destroy(&c);
191           break;
192         }
193
194       if ( !psppire_case_file_append_case(data_store->case_file, &c) )
195         {
196           g_warning("Cannot write case to casefile\n");
197           break;
198         }
199       case_destroy(&c);
200     }
201
202   sfm_close_reader(reader);
203
204   psppire_case_file_get_case_count(data_store->case_file);
205
206   return TRUE;
207 }
208
209 void
210 open_data                      (GtkMenuItem     *menuitem,
211                                 gpointer         user_data)
212 {
213   bool finished = FALSE;
214
215   GtkWidget *dialog;
216   GtkWidget *data_editor  = get_widget_assert(xml, "data_editor");
217   GtkFileFilter *filter ;
218
219   dialog = gtk_file_chooser_dialog_new (_("Open"),
220                                         GTK_WINDOW(data_editor),
221                                         GTK_FILE_CHOOSER_ACTION_OPEN,
222                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
223                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
224                                         NULL);
225
226   filter = gtk_file_filter_new();
227   gtk_file_filter_set_name(filter, _("System Files (*.sav)"));
228   gtk_file_filter_add_pattern(filter, "*.sav");
229   gtk_file_filter_add_pattern(filter, "*.SAV");
230   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
231
232   filter = gtk_file_filter_new();
233   gtk_file_filter_set_name(filter, _("Portable Files (*.por) "));
234   gtk_file_filter_add_pattern(filter, "*.por");
235   gtk_file_filter_add_pattern(filter, "*.POR");
236   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
237
238   filter = gtk_file_filter_new();
239   gtk_file_filter_set_name(filter, _("All Files"));
240   gtk_file_filter_add_pattern(filter, "*");
241   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
242
243   do {
244
245     if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
246       {
247         gchar *file_name =
248           gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (dialog));
249
250         finished =  load_system_file(file_name) ;
251
252         g_free(file_name);
253       }
254     else
255       finished = TRUE;
256
257   } while ( ! finished ) ;
258
259   gtk_widget_destroy (dialog);
260 }
261
262
263 void
264 on_data3_activate                      (GtkMenuItem     *menuitem,
265                                         gpointer         user_data)
266 {
267   open_data(menuitem, user_data);
268 }
269
270 void
271 on_data5_activate                      (GtkMenuItem     *menuitem,
272                                         gpointer         user_data)
273 {
274   open_data(menuitem, user_data);
275 }
276
277
278 /* Re initialise HANDLE, by interrogating the user for a new file name */
279 static gboolean
280 recreate_save_handle(struct file_handle **handle)
281 {
282   gint response;
283   GtkWidget *dialog;
284
285   GtkWidget *data_editor  = get_widget_assert(xml, "data_editor");
286
287   dialog = gtk_file_chooser_dialog_new (_("Save Data As"),
288                                         GTK_WINDOW(data_editor),
289                                         GTK_FILE_CHOOSER_ACTION_SAVE,
290                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
291                                         GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
292                                         NULL);
293
294   response = gtk_dialog_run (GTK_DIALOG (dialog));
295
296   if (response == GTK_RESPONSE_ACCEPT)
297     {
298       char *file_name = gtk_file_chooser_get_filename
299         (GTK_FILE_CHOOSER (dialog));
300
301
302       if ( *handle )
303         fh_free(*handle);
304
305       *handle = fh_create_file (handle_name, file_name,
306                         fh_default_properties());
307
308       psppire_set_window_title(basename(file_name));
309
310       g_free (file_name);
311     }
312
313   gtk_widget_destroy (dialog);
314
315   return ( response == GTK_RESPONSE_ACCEPT ) ;
316 }
317
318 void
319 on_save1_activate                      (GtkMenuItem     *menuitem,
320                                         gpointer         user_data)
321 {
322   GtkSheet *data_sheet ;
323   PsppireDataStore *data_store ;
324
325   if ( ! psppire_handle )
326     {
327       if ( ! recreate_save_handle(&psppire_handle) )
328         return;
329     }
330
331   data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
332   data_store = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
333
334   if ( psppire_handle )
335     psppire_data_store_create_system_file(data_store,
336                                        psppire_handle);
337 }
338
339
340 void
341 on_save_as1_activate                   (GtkMenuItem     *menuitem,
342                                         gpointer         user_data)
343 {
344   GtkSheet *data_sheet ;
345   PsppireDataStore *data_store ;
346
347   if ( ! recreate_save_handle(&psppire_handle) )
348     return ;
349
350   if ( ! psppire_handle )
351     return ;
352
353   data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
354   data_store = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
355
356   if ( psppire_handle )
357     psppire_data_store_create_system_file(data_store,
358                                        psppire_handle);
359 }
360
361
362 void
363 on_quit1_activate                      (GtkMenuItem     *menuitem,
364                                         gpointer         user_data)
365 {
366   gtk_main_quit();
367 }
368
369
370 void
371 on_clear_activate                    (GtkMenuItem     *menuitem,
372                                       gpointer         user_data)
373 {
374   GtkNotebook *notebook = GTK_NOTEBOOK(get_widget_assert(xml, "notebook1"));
375   gint page = -1;
376
377   page = gtk_notebook_get_current_page(notebook);
378
379   switch (page)
380     {
381     case PAGE_VAR_SHEET:
382             break;
383     case PAGE_DATA_SHEET:
384       {
385         GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
386         PsppireDataStore *data_store =
387           PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
388
389
390         switch ( data_sheet->state )
391           {
392           case GTK_SHEET_ROW_SELECTED:
393             psppire_case_file_delete_cases(data_store->case_file,
394                                            data_sheet->range.rowi
395                                            - data_sheet->range.row0 + 1,
396                                            data_sheet->range.row0);
397             break;
398           case GTK_SHEET_COLUMN_SELECTED:
399             {
400               gint fv;
401               struct variable *pv =
402                 psppire_dict_get_variable (the_dictionary,
403                                            data_sheet->range.col0);
404
405               fv = var_get_case_index (pv);
406
407               psppire_dict_delete_variables (the_dictionary,
408                                              data_sheet->range.col0,
409                                              1);
410
411               psppire_case_file_insert_values (data_store->case_file,
412                                                -1, fv);
413             }
414             break;
415           default:
416             gtk_sheet_cell_clear (data_sheet,
417                                   data_sheet->active_cell.row,
418                                   data_sheet->active_cell.col);
419             break;
420           }
421
422       }
423       break;
424     }
425
426 }
427
428 void
429 on_about1_activate(GtkMenuItem     *menuitem,
430                    gpointer         user_data)
431 {
432   GtkWidget *about =  get_widget_assert(xml, "aboutdialog1");
433
434
435   GdkPixbuf *pb  = gdk_pixbuf_new_from_file_at_size( "pspplogo.png", 64, 64, 0);
436
437   gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about), pb);
438
439   gtk_widget_show(about);
440
441   gtk_window_set_transient_for(GTK_WINDOW(about),
442                                GTK_WINDOW(get_widget_assert(xml, "data_editor")));
443 }
444
445
446 /* Set the value labels state from the toolbar's toggle button */
447 void
448 on_togglebutton_value_labels_toggled(GtkToggleToolButton *toggle_tool_button,
449                                      gpointer             user_data)
450 {
451   GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
452   GtkCheckMenuItem *item =
453     GTK_CHECK_MENU_ITEM(get_widget_assert(xml, "menuitem-value-labels"));
454
455   PsppireDataStore *ds = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
456
457   gboolean show_value_labels = gtk_toggle_tool_button_get_active(toggle_tool_button);
458
459   gtk_check_menu_item_set_active(item, show_value_labels);
460
461   psppire_data_store_show_labels(ds, show_value_labels);
462 }
463
464 /* Set the value labels state from the view menu */
465 void
466 on_value_labels_activate(GtkCheckMenuItem     *menuitem,
467                           gpointer         user_data)
468 {
469   GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
470   GtkToggleToolButton *tb =
471    GTK_TOGGLE_TOOL_BUTTON(get_widget_assert(xml, "togglebutton-value-labels"));
472
473   PsppireDataStore *ds = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
474
475   gboolean show_value_labels = gtk_check_menu_item_get_active(menuitem);
476
477   gtk_toggle_tool_button_set_active(tb, show_value_labels);
478
479   psppire_data_store_show_labels(ds, show_value_labels);
480 }
481
482 void
483 on_status_bar1_activate(GtkCheckMenuItem     *menuitem,
484  gpointer         user_data)
485 {
486
487   if ( gtk_check_menu_item_get_active(menuitem) )
488     gtk_widget_show(get_widget_assert(xml, "statusbar1"));
489   else
490     gtk_widget_hide(get_widget_assert(xml, "statusbar1"));
491 }
492
493 void
494 on_grid_lines1_activate(GtkCheckMenuItem     *menuitem,
495  gpointer         user_data)
496 {
497
498   const bool grid_visible = gtk_check_menu_item_get_active(menuitem);
499
500   gtk_sheet_show_grid(GTK_SHEET(get_widget_assert(xml, "variable_sheet")),
501                       grid_visible);
502
503   gtk_sheet_show_grid(GTK_SHEET(get_widget_assert(xml, "data_sheet")),
504                       grid_visible);
505 }
506
507
508 void
509 on_fonts1_activate(GtkMenuItem     *menuitem,
510  gpointer         user_data)
511 {
512   static GtkWidget *dialog = 0 ;
513   if ( !dialog )
514     dialog   = gtk_font_selection_dialog_new(_("Font Selection"));
515
516   gtk_window_set_transient_for(GTK_WINDOW(dialog),
517                                GTK_WINDOW(get_widget_assert(xml, "data_editor")));
518
519
520   if ( GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(dialog)) )
521     {
522       GtkSheet *data_sheet =
523         GTK_SHEET(get_widget_assert(xml, "data_sheet"));
524
525       GtkSheet *var_sheet =
526         GTK_SHEET(get_widget_assert(xml, "variable_sheet"));
527
528       PsppireDataStore *ds = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
529       PsppireVarStore *vs = PSPPIRE_VAR_STORE(gtk_sheet_get_model(var_sheet));
530
531       const gchar *font = gtk_font_selection_dialog_get_font_name
532         (GTK_FONT_SELECTION_DIALOG(dialog));
533
534       PangoFontDescription* font_desc =
535         pango_font_description_from_string(font);
536
537       psppire_var_store_set_font(vs, font_desc);
538       psppire_data_store_set_font(ds, font_desc);
539     }
540
541   gtk_widget_hide(dialog);
542
543 }
544
545
546 static GtkWidget *menuitems[2];
547 static GtkNotebook *notebook = 0;
548
549 static void
550 switch_menus(gint page)
551 {
552   GtkWidget *insert_variable = get_widget_assert(xml, "insert-variable");
553   GtkWidget *insert_cases = get_widget_assert(xml, "insert-cases");
554
555   switch (page)
556     {
557     case PAGE_VAR_SHEET:
558       gtk_widget_hide(menuitems[PAGE_VAR_SHEET]);
559       gtk_widget_show(menuitems[PAGE_DATA_SHEET]);
560       gtk_widget_set_sensitive(insert_variable, TRUE);
561       gtk_widget_set_sensitive(insert_cases, FALSE);
562       break;
563     case PAGE_DATA_SHEET:
564       gtk_widget_show(menuitems[PAGE_VAR_SHEET]);
565       gtk_widget_hide(menuitems[PAGE_DATA_SHEET]);
566       gtk_widget_set_sensitive(insert_variable, FALSE);
567       gtk_widget_set_sensitive(insert_cases, TRUE);
568       break;
569     default:
570       g_assert_not_reached();
571       break;
572     }
573 }
574
575
576 void
577 select_sheet(gint page)
578 {
579   gtk_notebook_set_current_page(notebook, page);
580   switch_menus(page);
581 }
582
583
584
585 static void
586 data_var_select(GtkNotebook *notebook,
587                 GtkNotebookPage *page,
588                 guint page_num,
589                 gpointer user_data)
590 {
591   switch_menus(page_num);
592 }
593
594
595 /* Initialised things on the variable sheet */
596 void
597 var_data_selection_init(void)
598 {
599   notebook = GTK_NOTEBOOK(get_widget_assert(xml, "notebook1"));
600   menuitems[PAGE_DATA_SHEET] = get_widget_assert(xml, "data1");
601   menuitems[PAGE_VAR_SHEET] = get_widget_assert(xml, "variables1");
602
603   gtk_notebook_set_current_page(notebook, PAGE_DATA_SHEET);
604   gtk_widget_hide(menuitems[PAGE_DATA_SHEET]);
605   gtk_widget_show(menuitems[PAGE_VAR_SHEET]);
606
607
608   g_signal_connect(G_OBJECT(notebook), "switch-page",
609                    G_CALLBACK(data_var_select), 0);
610
611 }
612
613
614 void
615 on_data1_activate(GtkMenuItem     *menuitem,
616                   gpointer         user_data)
617 {
618   select_sheet(PAGE_DATA_SHEET);
619 }
620
621
622 void
623 on_variables1_activate(GtkMenuItem     *menuitem,
624                   gpointer         user_data)
625 {
626   select_sheet(PAGE_VAR_SHEET);
627 }
628
629
630
631 void
632 on_go_to_case_activate(GtkMenuItem     *menuitem,
633                        gpointer         user_data)
634 {
635   GtkWidget *dialog = get_widget_assert(xml, "go_to_case_dialog");
636   GtkEntry *entry = GTK_ENTRY(get_widget_assert(xml, "entry_go_to_case"));
637   GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
638
639   gint result = gtk_dialog_run(GTK_DIALOG(dialog));
640
641
642
643   switch (result)
644     {
645     case GTK_RESPONSE_OK:
646       {
647         gint row, column;
648         const gchar *text = gtk_entry_get_text(entry);
649         gint casenum = g_strtod(text, NULL);
650
651         gtk_sheet_get_active_cell(data_sheet, &row, &column);
652         if ( column < 0 ) column = 0;
653         if ( row < 0 ) row = 0;
654
655         gtk_sheet_set_active_cell(data_sheet, casenum, column);
656       }
657       break;
658     default:
659       break;
660     }
661
662   gtk_widget_hide(dialog);
663   gtk_entry_set_text(entry, "");
664 }
665
666
667
668 void
669 on_sort_cases_activate (GtkMenuItem     *menuitem,
670                         gpointer         user_data)
671 {
672   gint response;
673   PsppireDataStore *data_store ;
674
675   struct sort_criteria criteria;
676   static struct sort_cases_dialog *dialog ;
677
678   GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
679
680   data_store = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
681
682   if ( NULL == dialog)
683     dialog = sort_cases_dialog_create(xml);
684
685   response = sort_cases_dialog_run(dialog, the_dictionary, &criteria);
686
687   switch ( response)
688     {
689     case GTK_RESPONSE_OK:
690       psppire_case_file_sort(data_store->case_file, &criteria);
691       break;
692     }
693 }
694
695
696 static void
697 insert_case(void)
698 {
699   gint row, col;
700   PsppireDataStore *data_store ;
701   GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
702
703   data_store = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
704
705   gtk_sheet_get_active_cell(data_sheet, &row, &col);
706
707   psppire_data_store_insert_new_case(data_store, row);
708 }
709
710 void
711 on_insert_case_clicked (GtkButton *button, gpointer user_data)
712 {
713   insert_case();
714 }
715
716 void
717 on_insert_cases (GtkMenuItem *menuitem, gpointer user_data)
718 {
719   insert_case();
720 }
721
722
723 void
724 on_insert_variable (GtkMenuItem *menuitem, gpointer user_data)
725 {
726   gint row, col;
727   GtkSheet *var_sheet = GTK_SHEET(get_widget_assert(xml, "variable_sheet"));
728
729   gtk_sheet_get_active_cell(var_sheet, &row, &col);
730
731   psppire_dict_insert_variable(the_dictionary, row, NULL);
732 }
733
734