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