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