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