7e320bbdfd1603826225a7a972f576510b737384
[pspp-builds.git] / src / ui / gui / data-editor.c
1 /*
2     PSPPIRE --- A Graphical User Interface for PSPP
3     Copyright (C) 2006  Free Software Foundation
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18     02110-1301, USA. */
19
20 #include <config.h>
21 #include <stdlib.h>
22 #include <gettext.h>
23
24 #include <glade/glade.h>
25 #include <gtk/gtk.h>
26
27 #include "window-manager.h"
28 #include <gtksheet/gtksheet.h>
29
30 #include "helper.h"
31 #include "about.h"
32
33 #define _(msgid) gettext (msgid)
34 #define N_(msgid) msgid
35
36 #include "data-editor.h"
37 #include "syntax-editor.h"
38 #include "window-manager.h"
39
40 #include "psppire-data-store.h"
41 #include "psppire-var-store.h"
42
43
44 static void insert_variable (GtkCheckMenuItem *m, gpointer data);
45
46
47 /* Switch between the VAR SHEET and the DATA SHEET */
48 enum {PAGE_DATA_SHEET = 0, PAGE_VAR_SHEET};
49
50 static gboolean click2column (GtkWidget *w, gint col, gpointer data);
51
52 static gboolean click2row (GtkWidget *w, gint row, gpointer data);
53
54
55 static void select_sheet (struct data_editor *de, guint page_num);
56
57
58 static void data_var_select (GtkNotebook *notebook,
59                             GtkNotebookPage *page,
60                             guint page_num,
61                             gpointer user_data);
62
63 static void status_bar_activate (GtkCheckMenuItem *, gpointer);
64
65 static void grid_lines_activate (GtkCheckMenuItem *, gpointer);
66
67 static void data_sheet_activate (GtkCheckMenuItem *, gpointer);
68
69 static void variable_sheet_activate (GtkCheckMenuItem *, gpointer );
70
71 static void fonts_activate (GtkMenuItem *, gpointer);
72
73 static void value_labels_activate (GtkCheckMenuItem *, gpointer);
74 static void value_labels_toggled (GtkToggleToolButton *, gpointer);
75
76
77 static void file_quit (GtkCheckMenuItem *, gpointer );
78
79 static void on_clear_activate (GtkMenuItem *, gpointer);
80
81 static void
82 enable_edit_clear (GtkWidget *w, gint row, gpointer data)
83 {
84   struct data_editor *de = data;
85
86   GtkWidget *menuitem = get_widget_assert (de->xml, "edit_clear");
87
88   gtk_widget_set_sensitive (menuitem, TRUE);
89 }
90
91 static gboolean
92 disable_edit_clear (GtkWidget *w, gint x, gint y, gpointer data)
93 {
94   struct data_editor *de = data;
95
96   GtkWidget *menuitem = get_widget_assert (de->xml, "edit_clear");
97
98   gtk_widget_set_sensitive (menuitem, FALSE);
99
100   return FALSE;
101 }
102
103
104 /* Callback for when the dictionary changes its weights */
105 static void
106 on_weight_change (GObject *o, gint weight_index, gpointer data)
107 {
108   struct data_editor *de = data;
109   GtkWidget *weight_status_area =
110     get_widget_assert (de->xml, "weight-status-area");
111
112   if ( weight_index == -1 )
113     {
114       gtk_label_set_text (GTK_LABEL (weight_status_area), _("Weights off"));
115     }
116   else
117     {
118       GtkSheet *var_sheet =
119         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
120
121       PsppireVarStore *vs = PSPPIRE_VAR_STORE
122         (gtk_sheet_get_model (var_sheet) );
123
124       struct variable *var = psppire_dict_get_variable (vs->dict,
125                                                         weight_index);
126
127       gchar *text = g_strdup_printf (_("Weight by %s"), var_get_name (var));
128
129       gtk_label_set_text (GTK_LABEL (weight_status_area), text);
130
131       g_free (text);
132     }
133 }
134
135
136 /*
137   Create a new data editor.
138 */
139 struct data_editor *
140 new_data_editor (void)
141 {
142   struct data_editor *de ;
143   struct editor_window *e;
144   GtkSheet *var_sheet ;
145   PsppireVarStore *vs;
146
147   de = g_malloc (sizeof (*de));
148
149   e = (struct editor_window *) de;
150
151   de->xml = glade_xml_new (PKGDATADIR "/data-editor.glade", NULL, NULL);
152
153
154   var_sheet = GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
155
156   vs = PSPPIRE_VAR_STORE (gtk_sheet_get_model (var_sheet));
157
158   g_signal_connect (vs->dict, "weight-changed",
159                     G_CALLBACK (on_weight_change),
160                     de);
161
162   connect_help (de->xml);
163
164   e->window = GTK_WINDOW (get_widget_assert (de->xml, "data_editor"));
165
166   g_signal_connect (get_widget_assert (de->xml,"file_new_data"),
167                     "activate",
168                     G_CALLBACK (new_data_window),
169                     e->window);
170
171   g_signal_connect (get_widget_assert (de->xml,"file_open_data"),
172                     "activate",
173                     G_CALLBACK (open_data_window),
174                     e->window);
175
176   g_signal_connect (get_widget_assert (de->xml,"file_new_syntax"),
177                     "activate",
178                     G_CALLBACK (new_syntax_window),
179                     e->window);
180
181   g_signal_connect (get_widget_assert (de->xml,"file_open_syntax"),
182                     "activate",
183                     G_CALLBACK (open_syntax_window),
184                     e->window);
185
186
187   g_signal_connect (get_widget_assert (de->xml,"edit_clear"),
188                     "activate",
189                     G_CALLBACK (on_clear_activate),
190                     de);
191
192
193   g_signal_connect (get_widget_assert (de->xml,"data_insert-variable"),
194                     "activate",
195                     G_CALLBACK (insert_variable),
196                     de);
197
198
199   g_signal_connect (get_widget_assert (de->xml,"help_about"),
200                     "activate",
201                     G_CALLBACK (about_new),
202                     e->window);
203
204
205   g_signal_connect (get_widget_assert (de->xml,"help_reference"),
206                     "activate",
207                     G_CALLBACK (reference_manual),
208                     e->window);
209
210
211
212   g_signal_connect (get_widget_assert (de->xml,"data_sheet"),
213                     "double-click-column",
214                     G_CALLBACK (click2column),
215                     de);
216
217
218   g_signal_connect (get_widget_assert (de->xml, "variable_sheet"),
219                     "double-click-row",
220                     GTK_SIGNAL_FUNC (click2row),
221                     de);
222
223
224   g_signal_connect (get_widget_assert (de->xml, "variable_sheet"),
225                     "select-row",
226                     GTK_SIGNAL_FUNC (enable_edit_clear),
227                     de);
228
229   g_signal_connect (get_widget_assert (de->xml, "variable_sheet"),
230                     "activate",
231                     GTK_SIGNAL_FUNC (disable_edit_clear),
232                     de);
233
234
235   g_signal_connect (get_widget_assert (de->xml, "notebook"),
236                     "switch-page",
237                     G_CALLBACK (data_var_select), de);
238
239
240
241   g_signal_connect (get_widget_assert (de->xml, "view_statusbar"),
242                     "activate",
243                     G_CALLBACK (status_bar_activate), de);
244
245
246   g_signal_connect (get_widget_assert (de->xml, "view_gridlines"),
247                     "activate",
248                     G_CALLBACK (grid_lines_activate), de);
249
250
251
252   g_signal_connect (get_widget_assert (de->xml, "view_data"),
253                     "activate",
254                     G_CALLBACK (data_sheet_activate), de);
255
256   g_signal_connect (get_widget_assert (de->xml, "view_variables"),
257                     "activate",
258                     G_CALLBACK (variable_sheet_activate), de);
259
260
261
262   g_signal_connect (get_widget_assert (de->xml, "view_fonts"),
263                     "activate",
264                     G_CALLBACK (fonts_activate), de);
265
266
267
268   g_signal_connect (get_widget_assert (de->xml, "view_valuelabels"),
269                     "activate",
270                     G_CALLBACK (value_labels_activate), de);
271
272
273   g_signal_connect (get_widget_assert (de->xml, "togglebutton-value-labels"),
274                     "toggled",
275                     G_CALLBACK (value_labels_toggled), de);
276
277
278   g_signal_connect (get_widget_assert (de->xml, "file_quit"),
279                     "activate",
280                     G_CALLBACK (file_quit), de);
281
282
283   g_signal_connect (get_widget_assert (de->xml, "windows_minimise_all"),
284                     "activate",
285                     G_CALLBACK (minimise_all_windows), NULL);
286
287
288
289   select_sheet (de, PAGE_DATA_SHEET);
290
291   return de;
292 }
293
294
295 /* Callback which occurs when the var sheet's row title
296    button is double clicked */
297 static gboolean
298 click2row (GtkWidget *w, gint row, gpointer data)
299 {
300   struct data_editor *de = data;
301
302   gint current_row, current_column;
303
304   GtkWidget *data_sheet  = get_widget_assert (de->xml, "data_sheet");
305
306   data_editor_select_sheet (de, PAGE_DATA_SHEET);
307
308   gtk_sheet_get_active_cell (GTK_SHEET (data_sheet),
309                              &current_row, &current_column);
310
311   gtk_sheet_set_active_cell (GTK_SHEET (data_sheet), current_row, row);
312
313   return FALSE;
314 }
315
316
317 /* Callback which occurs when the data sheet's column title
318    is double clicked */
319 static gboolean
320 click2column (GtkWidget *w, gint col, gpointer data)
321 {
322   struct data_editor *de = data;
323
324   gint current_row, current_column;
325
326   GtkWidget *var_sheet  = get_widget_assert (de->xml, "variable_sheet");
327
328   data_editor_select_sheet (de, PAGE_VAR_SHEET);
329
330   gtk_sheet_get_active_cell (GTK_SHEET (var_sheet),
331                              &current_row, &current_column);
332
333   gtk_sheet_set_active_cell (GTK_SHEET (var_sheet), col, current_column);
334
335   return FALSE;
336 }
337
338
339
340
341 void
342 new_data_window (GtkMenuItem *menuitem, gpointer parent)
343 {
344   window_create (WINDOW_DATA, NULL);
345 }
346
347
348 static void
349 select_sheet (struct data_editor *de, guint page_num)
350 {
351   GtkWidget *insert_variable = get_widget_assert (de->xml, "data_insert-variable");
352   GtkWidget *insert_cases = get_widget_assert (de->xml, "insert-cases");
353
354   GtkWidget *view_data = get_widget_assert (de->xml, "view_data");
355   GtkWidget *view_variables = get_widget_assert (de->xml, "view_variables");
356
357   switch (page_num)
358     {
359     case PAGE_VAR_SHEET:
360       gtk_widget_hide (view_variables);
361       gtk_widget_show (view_data);
362       gtk_widget_set_sensitive (insert_variable, TRUE);
363       gtk_widget_set_sensitive (insert_cases, FALSE);
364       break;
365     case PAGE_DATA_SHEET:
366       gtk_widget_show (view_variables);
367       gtk_widget_hide (view_data);
368 #if 0
369       gtk_widget_set_sensitive (insert_cases, TRUE);
370 #endif
371       break;
372     default:
373       g_assert_not_reached ();
374       break;
375     }
376 }
377
378
379 static void
380 data_var_select (GtkNotebook *notebook,
381                 GtkNotebookPage *page,
382                 guint page_num,
383                 gpointer user_data)
384 {
385   struct data_editor *de = user_data;
386
387   select_sheet (de, page_num);
388 }
389
390
391
392
393 void
394 data_editor_select_sheet (struct data_editor *de, gint page)
395 {
396   gtk_notebook_set_current_page
397    (
398     GTK_NOTEBOOK (get_widget_assert (de->xml,"notebook")), page
399     );
400 }
401
402
403 void
404 open_data_window (GtkMenuItem *menuitem, gpointer parent)
405 {
406   bool finished = FALSE;
407
408   GtkWidget *dialog;
409
410   GtkFileFilter *filter ;
411
412   dialog = gtk_file_chooser_dialog_new (_("Open"),
413                                         GTK_WINDOW (parent),
414                                         GTK_FILE_CHOOSER_ACTION_OPEN,
415                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
416                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
417                                         NULL);
418
419   filter = gtk_file_filter_new ();
420   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
421   gtk_file_filter_add_pattern (filter, "*.sav");
422   gtk_file_filter_add_pattern (filter, "*.SAV");
423   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
424
425   filter = gtk_file_filter_new ();
426   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
427   gtk_file_filter_add_pattern (filter, "*.por");
428   gtk_file_filter_add_pattern (filter, "*.POR");
429   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
430
431   filter = gtk_file_filter_new ();
432   gtk_file_filter_set_name (filter, _("All Files"));
433   gtk_file_filter_add_pattern (filter, "*");
434   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
435
436   do {
437
438     if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
439       {
440         gchar *file_name =
441           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
442
443         g_free (file_name);
444       }
445     else
446       finished = TRUE;
447
448   } while ( ! finished ) ;
449
450   gtk_widget_destroy (dialog);
451 }
452
453
454
455
456 static void
457 status_bar_activate (GtkCheckMenuItem *menuitem, gpointer data)
458 {
459   struct data_editor *de = data;
460   GtkWidget *statusbar = get_widget_assert (de->xml, "status-bar");
461
462   if ( gtk_check_menu_item_get_active (menuitem) )
463     gtk_widget_show (statusbar);
464   else
465     gtk_widget_hide (statusbar);
466 }
467
468
469 static void
470 grid_lines_activate (GtkCheckMenuItem *menuitem, gpointer data)
471 {
472   struct data_editor *de = data;
473   const bool grid_visible = gtk_check_menu_item_get_active (menuitem);
474
475   gtk_sheet_show_grid (GTK_SHEET (get_widget_assert (de->xml,
476                                                      "variable_sheet")),
477                        grid_visible);
478
479   gtk_sheet_show_grid (GTK_SHEET (get_widget_assert (de->xml, "data_sheet")),
480                        grid_visible);
481 }
482
483
484
485 static void
486 data_sheet_activate (GtkCheckMenuItem *menuitem, gpointer data)
487 {
488   struct data_editor *de = data;
489
490   data_editor_select_sheet (de, PAGE_DATA_SHEET);
491 }
492
493
494 static void
495 variable_sheet_activate (GtkCheckMenuItem *menuitem, gpointer data)
496 {
497   struct data_editor *de = data;
498
499   data_editor_select_sheet (de, PAGE_VAR_SHEET);
500 }
501
502
503 static void
504 fonts_activate (GtkMenuItem *menuitem, gpointer data)
505 {
506   struct data_editor *de = data;
507   GtkWidget *dialog =
508     gtk_font_selection_dialog_new (_("Font Selection"));
509
510   gtk_window_set_transient_for (GTK_WINDOW (dialog),
511                                 GTK_WINDOW (get_widget_assert (de->xml,
512                                                                "data_editor")));
513   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
514     {
515       GtkSheet *data_sheet =
516         GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
517
518       GtkSheet *var_sheet =
519         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
520
521       PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
522       PsppireVarStore *vs = PSPPIRE_VAR_STORE (gtk_sheet_get_model (var_sheet));
523
524       const gchar *font = gtk_font_selection_dialog_get_font_name
525         (GTK_FONT_SELECTION_DIALOG (dialog));
526
527       PangoFontDescription* font_desc =
528         pango_font_description_from_string (font);
529
530       psppire_var_store_set_font (vs, font_desc);
531       psppire_data_store_set_font (ds, font_desc);
532     }
533
534   gtk_widget_hide (dialog);
535 }
536
537
538 /* The next two callbacks are mutually co-operative */
539
540 /* Callback for the value labels menu item */
541 static void
542 value_labels_activate (GtkCheckMenuItem *menuitem, gpointer data)
543 {
544   struct data_editor *de = data;
545
546   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
547
548   GtkToggleToolButton *tb =
549     GTK_TOGGLE_TOOL_BUTTON (get_widget_assert (de->xml,
550                                                "togglebutton-value-labels"));
551
552   PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
553
554   gboolean show_value_labels = gtk_check_menu_item_get_active (menuitem);
555
556   gtk_toggle_tool_button_set_active (tb, show_value_labels);
557
558   psppire_data_store_show_labels (ds, show_value_labels);
559 }
560
561
562 /* Callback for the value labels tooglebutton */
563 static void
564 value_labels_toggled (GtkToggleToolButton *toggle_tool_button,
565                       gpointer data)
566 {
567   struct data_editor *de = data;
568
569   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
570
571   GtkCheckMenuItem *item =
572     GTK_CHECK_MENU_ITEM (get_widget_assert (de->xml, "view_valuelabels"));
573
574   PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
575
576   gboolean show_value_labels =
577     gtk_toggle_tool_button_get_active (toggle_tool_button);
578
579   gtk_check_menu_item_set_active (item, show_value_labels);
580
581   psppire_data_store_show_labels (ds, show_value_labels);
582 }
583
584
585 static void
586 file_quit (GtkCheckMenuItem *menuitem, gpointer data)
587 {
588   /* FIXME: Need to be more intelligent here.
589      Give the user the opportunity to save any unsaved data.
590   */
591   gtk_main_quit ();
592 }
593
594
595
596 /* Callback for when the Clear item in the edit menu is activated */
597 static void
598 on_clear_activate (GtkMenuItem *menuitem, gpointer data)
599 {
600   struct data_editor *de = data;
601
602   GtkNotebook *notebook = GTK_NOTEBOOK (get_widget_assert (de->xml,
603                                                            "notebook"));
604
605   switch ( gtk_notebook_get_current_page (notebook) )
606     {
607     case PAGE_VAR_SHEET:
608       {
609         GtkSheet *var_sheet =
610           GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
611
612         PsppireVarStore *vs = PSPPIRE_VAR_STORE
613           (gtk_sheet_get_model (var_sheet) );
614
615         /* This shouldn't be able to happen, because the menuitem
616            should be disabled */
617         g_return_if_fail (var_sheet->state  ==  GTK_SHEET_ROW_SELECTED );
618
619         psppire_dict_delete_variables (vs->dict,
620                                        var_sheet->range.row0,
621                                        1 +
622                                        var_sheet->range.rowi -
623                                        var_sheet->range.row0 );
624       }
625       break;
626       case PAGE_DATA_SHEET:
627         break;
628       default:
629         g_assert_not_reached ();
630     }
631 }
632
633
634 /* Insert a new variable before the current row in the variable sheet,
635    or before the current column in the data sheet, whichever is selected */
636 static void
637 insert_variable (GtkCheckMenuItem *m, gpointer data)
638 {
639   struct data_editor *de = data;
640   gint posn;
641
642   GtkWidget *notebook = get_widget_assert (de->xml, "notebook");
643
644   GtkSheet *var_sheet =
645     GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
646
647
648
649   PsppireVarStore *vs = PSPPIRE_VAR_STORE
650     (gtk_sheet_get_model (var_sheet) );
651
652
653   switch ( gtk_notebook_get_current_page ( GTK_NOTEBOOK (notebook)) )
654     {
655     case PAGE_VAR_SHEET:
656       posn = var_sheet->active_cell.row;
657       break;
658     case PAGE_DATA_SHEET:
659       {
660         GtkSheet *data_sheet =
661           GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
662
663         if ( data_sheet->state == GTK_SHEET_COLUMN_SELECTED )
664           posn = data_sheet->range.col0;
665         else
666           posn = data_sheet->active_cell.col;
667       }
668       break;
669     default:
670       g_assert_not_reached ();
671     }
672
673   psppire_dict_insert_variable (vs->dict, posn, NULL);
674
675 }
676
677