d1a76802840b8970b84f57b9fc1e596771853e9f
[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
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 #include "psppire-variable.h"
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
200       case_destroy(&c);
201
202     }
203   
204   sfm_close_reader(reader);      
205
206   psppire_case_file_get_case_count(data_store->case_file);
207
208   return TRUE;
209 }
210
211
212 void
213 on_open1_activate                      (GtkMenuItem     *menuitem,
214                                         gpointer         user_data)
215 {
216   bool finished = FALSE;
217
218   GtkWidget *dialog;
219   GtkWidget *data_editor  = get_widget_assert(xml, "data_editor");
220   GtkFileFilter *filter ;
221  
222   dialog = gtk_file_chooser_dialog_new (_("Open"),
223                                         GTK_WINDOW(data_editor),
224                                         GTK_FILE_CHOOSER_ACTION_OPEN,
225                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
226                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
227                                         NULL);
228
229   filter = gtk_file_filter_new();
230   gtk_file_filter_set_name(filter, _("System Files (*.sav)"));
231   gtk_file_filter_add_pattern(filter, "*.sav");
232   gtk_file_filter_add_pattern(filter, "*.SAV");
233   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
234
235   filter = gtk_file_filter_new();
236   gtk_file_filter_set_name(filter, _("Portable Files (*.por) "));
237   gtk_file_filter_add_pattern(filter, "*.por");
238   gtk_file_filter_add_pattern(filter, "*.POR");
239   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
240
241   filter = gtk_file_filter_new();
242   gtk_file_filter_set_name(filter, _("All Files"));
243   gtk_file_filter_add_pattern(filter, "*");
244   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
245
246   do {
247
248     if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
249       {
250         gchar *file_name = 
251           gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (dialog));
252         
253         finished =  load_system_file(file_name) ;
254
255         g_free(file_name);
256       }
257     else
258       finished = TRUE;
259
260   } while ( ! finished ) ;
261
262   gtk_widget_destroy (dialog);
263 }
264
265
266 /* Re initialise HANDLE, by interrogating the user for a new file name */
267 static void
268 recreate_save_handle(struct file_handle **handle)
269 {
270   GtkWidget *dialog;
271
272   GtkWidget *data_editor  = get_widget_assert(xml, "data_editor");
273
274   dialog = gtk_file_chooser_dialog_new (_("Save Data As"),
275                                         GTK_WINDOW(data_editor),
276                                         GTK_FILE_CHOOSER_ACTION_SAVE,
277                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
278                                         GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
279                                         NULL);
280
281   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
282     {
283       char *file_name = gtk_file_chooser_get_filename
284         (GTK_FILE_CHOOSER (dialog));
285
286 #if 0
287       if ( *handle ) 
288         destroy_file_handle(*handle, 0);
289 #endif
290       *handle = fh_create_file (handle_name, file_name, fh_default_properties());
291
292       psppire_set_window_title(basename(file_name));
293
294       g_free (file_name);
295     }
296
297   gtk_widget_destroy (dialog);
298 }
299
300 void
301 on_save1_activate                      (GtkMenuItem     *menuitem,
302                                         gpointer         user_data)
303 {
304   GtkSheet *data_sheet ;
305   PsppireDataStore *data_store ;
306
307   if ( ! psppire_handle ) 
308     recreate_save_handle(&psppire_handle);
309   
310   data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
311   data_store = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
312   
313   if ( psppire_handle ) 
314     psppire_data_store_create_system_file(data_store,
315                                        psppire_handle);
316 }
317
318
319 void
320 on_save_as1_activate                   (GtkMenuItem     *menuitem,
321                                         gpointer         user_data)
322 {
323   GtkSheet *data_sheet ;
324   PsppireDataStore *data_store ;
325
326   recreate_save_handle(&psppire_handle);
327   if ( ! psppire_handle ) 
328     return ;
329
330   data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
331   data_store = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
332
333   if ( psppire_handle ) 
334     psppire_data_store_create_system_file(data_store,
335                                        psppire_handle);
336 }
337
338
339 void
340 on_quit1_activate                      (GtkMenuItem     *menuitem,
341                                         gpointer         user_data)
342 {
343   gtk_main_quit();
344 }
345
346
347 void
348 on_cut1_activate                       (GtkMenuItem     *menuitem,
349                                         gpointer         user_data)
350 {
351
352 }
353
354
355 void
356 on_copy1_activate                      (GtkMenuItem     *menuitem,
357                                         gpointer         user_data)
358 {
359
360 }
361
362
363 void
364 on_paste1_activate                     (GtkMenuItem     *menuitem,
365                                         gpointer         user_data)
366 {
367
368 }
369
370 /* Fill a case with SYSMIS for numeric and whitespace for string
371    variables respectively */ 
372 static gboolean 
373 blank_case(struct ccase *cc, gpointer _dict)
374 {
375   gint i;
376   PsppireDict *dict = _dict;
377
378   for(i = 0 ; i < psppire_dict_get_var_cnt(dict); ++i ) 
379     {
380       union value *val ;
381
382       const struct PsppireVariable *var = psppire_dict_get_variable(dict, i);
383       
384       gint idx = psppire_variable_get_fv(var);
385
386       val = case_data_rw(cc, idx) ;
387
388       if ( psppire_variable_get_type(var) == ALPHA ) 
389         memset(val->s, ' ', psppire_variable_get_width(var));
390       else
391         val->f = SYSMIS;
392
393       case_unshare(cc);
394     }
395
396   return TRUE;
397 }
398
399
400 void
401 on_insert1_activate                    (GtkMenuItem     *menuitem,
402                                         gpointer         user_data)
403 {
404   GtkNotebook *notebook = GTK_NOTEBOOK(get_widget_assert(xml, "notebook1"));
405   gint page = -1;
406
407   page = gtk_notebook_get_current_page(notebook);
408
409   switch (page) 
410     {
411     case PAGE_DATA_SHEET:
412 #if 0
413       {
414         GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
415         PsppireDataStore *data_store = 
416           PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
417
418         psppire_case_array_insert_case(data_store->cases, 
419                                        data_sheet->range.row0, 
420                                        blank_case, the_dictionary);
421       }
422       break;
423 #endif
424     case PAGE_VAR_SHEET:
425       {
426         GtkSheet *var_sheet = 
427           GTK_SHEET(get_widget_assert(xml, "variable_sheet"));
428
429         PsppireVarStore *var_store = 
430           PSPPIRE_VAR_STORE(gtk_sheet_get_model(var_sheet));
431
432         psppire_dict_insert_variable(var_store->dict, var_sheet->range.row0, 0);
433       }
434       break;
435     }
436 }
437
438 void
439 on_delete1_activate                    (GtkMenuItem     *menuitem,
440                                         gpointer         user_data)
441 {
442   gint page = -1;
443   GtkWidget *notebook = get_widget_assert(xml, "notebook1");
444
445   page = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
446   switch ( page) 
447     {
448 #if 0
449     case PAGE_DATA_SHEET:
450       {
451         GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
452         PsppireDataStore *data_store = 
453           PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
454
455         psppire_case_array_delete_cases(data_store->cases, 
456                                     data_sheet->range.row0, 
457                                     1 + data_sheet->range.rowi 
458                                     - data_sheet->range.row0  );
459       }
460       break;
461 #endif
462     case PAGE_VAR_SHEET:
463       {
464         GtkSheet *var_sheet = 
465           GTK_SHEET(get_widget_assert(xml, "variable_sheet"));
466
467         PsppireVarStore *var_store = 
468           PSPPIRE_VAR_STORE(gtk_sheet_get_model(var_sheet));
469
470         psppire_dict_delete_variables(var_store->dict, 
471                                    var_sheet->range.row0,
472                                    1 + var_sheet->range.rowi 
473                                    - var_sheet->range.row0  );
474       }
475       break;
476     }
477 }
478
479
480 void
481 on_about1_activate(GtkMenuItem     *menuitem,
482                    gpointer         user_data)
483 {
484   GtkWidget *about =  get_widget_assert(xml, "aboutdialog1");
485   
486   
487   GdkPixbuf *pb  = gdk_pixbuf_new_from_file_at_size( "pspplogo.png", 64, 64, 0);
488
489   gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about), pb);
490
491   gtk_widget_show(about);
492
493   gtk_window_set_transient_for(GTK_WINDOW(about), 
494                                GTK_WINDOW(get_widget_assert(xml, "data_editor")));
495 }
496
497
498 /* Set the value labels state from the toolbar's toggle button */
499 void
500 on_togglebutton_value_labels_toggled(GtkToggleToolButton *toggle_tool_button,
501                                      gpointer             user_data)
502 {
503   GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
504   GtkCheckMenuItem *item = 
505     GTK_CHECK_MENU_ITEM(get_widget_assert(xml, "menuitem-value-labels"));
506
507   PsppireDataStore *ds = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
508
509   gboolean show_value_labels = gtk_toggle_tool_button_get_active(toggle_tool_button);
510   
511   gtk_check_menu_item_set_active(item, show_value_labels);
512
513   psppire_data_store_show_labels(ds, show_value_labels);
514 }
515
516 /* Set the value labels state from the view menu */
517 void
518 on_value_labels_activate(GtkCheckMenuItem     *menuitem,
519                           gpointer         user_data)
520 {
521   GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
522   GtkToggleToolButton *tb = 
523    GTK_TOGGLE_TOOL_BUTTON(get_widget_assert(xml, "togglebutton-value-labels"));
524
525   PsppireDataStore *ds = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
526
527   gboolean show_value_labels = gtk_check_menu_item_get_active(menuitem);
528
529   gtk_toggle_tool_button_set_active(tb, show_value_labels);
530
531   psppire_data_store_show_labels(ds, show_value_labels);
532 }
533
534 void
535 on_status_bar1_activate(GtkCheckMenuItem     *menuitem,
536  gpointer         user_data)
537 {
538
539   if ( gtk_check_menu_item_get_active(menuitem) ) 
540     gtk_widget_show(get_widget_assert(xml, "statusbar1"));
541   else
542     gtk_widget_hide(get_widget_assert(xml, "statusbar1"));
543 }
544
545 void
546 on_grid_lines1_activate(GtkCheckMenuItem     *menuitem,
547  gpointer         user_data)
548 {
549
550   const bool grid_visible = gtk_check_menu_item_get_active(menuitem); 
551
552   gtk_sheet_show_grid(GTK_SHEET(get_widget_assert(xml, "variable_sheet")),
553                       grid_visible);
554
555   gtk_sheet_show_grid(GTK_SHEET(get_widget_assert(xml, "data_sheet")),
556                       grid_visible);
557 }
558
559
560 void
561 on_fonts1_activate(GtkMenuItem     *menuitem,
562  gpointer         user_data)
563 {
564   static GtkWidget *dialog = 0 ; 
565   if ( !dialog ) 
566     dialog   = gtk_font_selection_dialog_new(_("Font Selection"));
567
568   gtk_window_set_transient_for(GTK_WINDOW(dialog), 
569                                GTK_WINDOW(get_widget_assert(xml, "data_editor")));
570
571
572   if ( GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(dialog)) ) 
573     {
574       GtkSheet *data_sheet = 
575         GTK_SHEET(get_widget_assert(xml, "data_sheet"));
576
577       GtkSheet *var_sheet = 
578         GTK_SHEET(get_widget_assert(xml, "variable_sheet"));
579
580       PsppireDataStore *ds = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
581       PsppireVarStore *vs = PSPPIRE_VAR_STORE(gtk_sheet_get_model(var_sheet));
582
583       const gchar *font = gtk_font_selection_dialog_get_font_name 
584         (GTK_FONT_SELECTION_DIALOG(dialog));
585
586       PangoFontDescription* font_desc = 
587         pango_font_description_from_string(font);
588
589       psppire_var_store_set_font(vs, font_desc);
590       psppire_data_store_set_font(ds, font_desc);
591     }
592   
593   gtk_widget_hide(dialog);
594
595 }
596
597
598 static GtkWidget *menuitems[2];
599 static GtkNotebook *notebook = 0;
600
601 static void
602 switch_menus(gint page)
603 {
604   switch (page) 
605     {
606     case PAGE_VAR_SHEET:
607       gtk_widget_hide(menuitems[PAGE_VAR_SHEET]);
608       gtk_widget_show(menuitems[PAGE_DATA_SHEET]);
609       break;
610     case PAGE_DATA_SHEET:
611       gtk_widget_show(menuitems[PAGE_VAR_SHEET]);
612       gtk_widget_hide(menuitems[PAGE_DATA_SHEET]);
613       break;
614     default:
615       g_assert_not_reached();
616       break;
617     }
618 }
619
620
621 void
622 select_sheet(gint page)
623 {
624   gtk_notebook_set_current_page(notebook, page);
625   switch_menus(page);
626 }
627
628
629
630 static void
631 data_var_select(GtkNotebook *notebook,
632                 GtkNotebookPage *page,
633                 guint page_num,
634                 gpointer user_data)
635 {
636   switch_menus(page_num);
637 }
638
639
640 /* Initialised things on the variable sheet */
641 void
642 var_data_selection_init(void)
643 {
644   notebook = GTK_NOTEBOOK(get_widget_assert(xml, "notebook1"));
645   menuitems[PAGE_DATA_SHEET] = get_widget_assert(xml, "data1");
646   menuitems[PAGE_VAR_SHEET] = get_widget_assert(xml, "variables1");
647
648   gtk_notebook_set_current_page(notebook, PAGE_DATA_SHEET);
649   gtk_widget_hide(menuitems[PAGE_DATA_SHEET]);
650   gtk_widget_show(menuitems[PAGE_VAR_SHEET]);
651
652
653   g_signal_connect(G_OBJECT(notebook), "switch-page",
654                    G_CALLBACK(data_var_select), 0);
655
656 }
657
658
659 void
660 on_data1_activate(GtkMenuItem     *menuitem,
661                   gpointer         user_data)
662 {
663   select_sheet(PAGE_DATA_SHEET);
664 }
665
666
667 void
668 on_variables1_activate(GtkMenuItem     *menuitem,
669                   gpointer         user_data)
670 {
671   select_sheet(PAGE_VAR_SHEET);
672 }
673
674
675
676 void
677 on_go_to_case_activate(GtkMenuItem     *menuitem,
678                        gpointer         user_data)
679 {
680   GtkWidget *dialog = get_widget_assert(xml, "go_to_case_dialog");
681   GtkEntry *entry = GTK_ENTRY(get_widget_assert(xml, "entry_go_to_case"));
682   GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
683   
684   gint result = gtk_dialog_run(GTK_DIALOG(dialog));
685   
686
687   
688   switch (result)
689     {
690     case GTK_RESPONSE_OK:
691       {
692         gint row, column;
693         const gchar *text = gtk_entry_get_text(entry);
694         gint casenum = g_strtod(text, NULL);
695
696         gtk_sheet_get_active_cell(data_sheet, &row, &column);
697         if ( column < 0 ) column = 0;
698         if ( row < 0 ) row = 0;
699         
700         gtk_sheet_set_active_cell(data_sheet, casenum, column);
701       }
702       break;
703     default:
704       break;
705     }
706
707   gtk_widget_hide(dialog);
708   gtk_entry_set_text(entry, "");
709 }
710
711
712
713 void
714 on_sort_cases_activate (GtkMenuItem     *menuitem,
715                         gpointer         user_data)
716 {
717   gint response;
718   PsppireDataStore *data_store ;
719
720   struct sort_criteria criteria;
721   static struct sort_cases_dialog *dialog ;
722
723   GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
724
725   data_store = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
726
727   if ( NULL == dialog) 
728     dialog = sort_cases_dialog_create(xml);
729
730   response = sort_cases_dialog_run(dialog, the_dictionary, &criteria);
731
732   switch ( response) 
733     {
734     case GTK_RESPONSE_OK:
735       psppire_case_file_sort(data_store->case_file, &criteria);
736       break;
737     }
738 }