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