Re-added Data->Insert_Variable menu item
[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
105 /*
106   Create a new data editor.
107 */
108 struct data_editor *
109 new_data_editor (void)
110 {
111   struct data_editor *de ;
112   struct editor_window *e;
113
114   de = g_malloc (sizeof (*de));
115
116   e = (struct editor_window *) de;
117
118   de->xml = glade_xml_new (PKGDATADIR "/data-editor.glade", NULL, NULL);
119
120   connect_help (de->xml);
121
122   e->window = GTK_WINDOW (get_widget_assert (de->xml, "data_editor"));
123
124   g_signal_connect (get_widget_assert (de->xml,"file_new_data"),
125                     "activate",
126                     G_CALLBACK (new_data_window),
127                     e->window);
128
129   g_signal_connect (get_widget_assert (de->xml,"file_open_data"),
130                     "activate",
131                     G_CALLBACK (open_data_window),
132                     e->window);
133
134   g_signal_connect (get_widget_assert (de->xml,"file_new_syntax"),
135                     "activate",
136                     G_CALLBACK (new_syntax_window),
137                     e->window);
138
139   g_signal_connect (get_widget_assert (de->xml,"file_open_syntax"),
140                     "activate",
141                     G_CALLBACK (open_syntax_window),
142                     e->window);
143
144
145   g_signal_connect (get_widget_assert (de->xml,"edit_clear"),
146                     "activate",
147                     G_CALLBACK (on_clear_activate),
148                     de);
149
150
151   g_signal_connect (get_widget_assert (de->xml,"data_insert-variable"),
152                     "activate",
153                     G_CALLBACK (insert_variable),
154                     de);
155
156
157   g_signal_connect (get_widget_assert (de->xml,"help_about"),
158                     "activate",
159                     G_CALLBACK (about_new),
160                     e->window);
161
162
163   g_signal_connect (get_widget_assert (de->xml,"help_reference"),
164                     "activate",
165                     G_CALLBACK (reference_manual),
166                     e->window);
167
168
169
170   g_signal_connect (get_widget_assert (de->xml,"data_sheet"),
171                     "double-click-column",
172                     G_CALLBACK (click2column),
173                     de);
174
175
176   g_signal_connect (get_widget_assert (de->xml, "variable_sheet"),
177                     "double-click-row",
178                     GTK_SIGNAL_FUNC (click2row),
179                     de);
180
181
182   g_signal_connect (get_widget_assert (de->xml, "variable_sheet"),
183                     "select-row",
184                     GTK_SIGNAL_FUNC (enable_edit_clear),
185                     de);
186
187   g_signal_connect (get_widget_assert (de->xml, "variable_sheet"),
188                     "activate",
189                     GTK_SIGNAL_FUNC (disable_edit_clear),
190                     de);
191
192
193   g_signal_connect (get_widget_assert (de->xml, "notebook"),
194                     "switch-page",
195                     G_CALLBACK (data_var_select), de);
196
197
198
199   g_signal_connect (get_widget_assert (de->xml, "view_statusbar"),
200                     "activate",
201                     G_CALLBACK (status_bar_activate), de);
202
203
204   g_signal_connect (get_widget_assert (de->xml, "view_gridlines"),
205                     "activate",
206                     G_CALLBACK (grid_lines_activate), de);
207
208
209
210   g_signal_connect (get_widget_assert (de->xml, "view_data"),
211                     "activate",
212                     G_CALLBACK (data_sheet_activate), de);
213
214   g_signal_connect (get_widget_assert (de->xml, "view_variables"),
215                     "activate",
216                     G_CALLBACK (variable_sheet_activate), de);
217
218
219
220   g_signal_connect (get_widget_assert (de->xml, "view_fonts"),
221                     "activate",
222                     G_CALLBACK (fonts_activate), de);
223
224
225
226   g_signal_connect (get_widget_assert (de->xml, "view_valuelabels"),
227                     "activate",
228                     G_CALLBACK (value_labels_activate), de);
229
230
231   g_signal_connect (get_widget_assert (de->xml, "togglebutton-value-labels"),
232                     "toggled",
233                     G_CALLBACK (value_labels_toggled), de);
234
235
236   g_signal_connect (get_widget_assert (de->xml, "file_quit"),
237                     "activate",
238                     G_CALLBACK (file_quit), de);
239
240
241   g_signal_connect (get_widget_assert (de->xml, "windows_minimise_all"),
242                     "activate",
243                     G_CALLBACK (minimise_all_windows), NULL);
244
245
246   select_sheet (de, PAGE_DATA_SHEET);
247
248   return de;
249 }
250
251
252 /* Callback which occurs when the var sheet's row title
253    button is double clicked */
254 static gboolean
255 click2row (GtkWidget *w, gint row, gpointer data)
256 {
257   struct data_editor *de = data;
258
259   gint current_row, current_column;
260
261   GtkWidget *data_sheet  = get_widget_assert (de->xml, "data_sheet");
262
263   data_editor_select_sheet (de, PAGE_DATA_SHEET);
264
265   gtk_sheet_get_active_cell (GTK_SHEET (data_sheet),
266                              &current_row, &current_column);
267
268   gtk_sheet_set_active_cell (GTK_SHEET (data_sheet), current_row, row);
269
270   return FALSE;
271 }
272
273
274 /* Callback which occurs when the data sheet's column title
275    is double clicked */
276 static gboolean
277 click2column (GtkWidget *w, gint col, gpointer data)
278 {
279   struct data_editor *de = data;
280
281   gint current_row, current_column;
282
283   GtkWidget *var_sheet  = get_widget_assert (de->xml, "variable_sheet");
284
285   data_editor_select_sheet (de, PAGE_VAR_SHEET);
286
287   gtk_sheet_get_active_cell (GTK_SHEET (var_sheet),
288                              &current_row, &current_column);
289
290   gtk_sheet_set_active_cell (GTK_SHEET (var_sheet), col, current_column);
291
292   return FALSE;
293 }
294
295
296
297
298 void
299 new_data_window (GtkMenuItem *menuitem, gpointer parent)
300 {
301   window_create (WINDOW_DATA, NULL);
302 }
303
304
305 static void
306 select_sheet (struct data_editor *de, guint page_num)
307 {
308   GtkWidget *insert_variable = get_widget_assert (de->xml, "data_insert-variable");
309   GtkWidget *insert_cases = get_widget_assert (de->xml, "insert-cases");
310
311   GtkWidget *view_data = get_widget_assert (de->xml, "view_data");
312   GtkWidget *view_variables = get_widget_assert (de->xml, "view_variables");
313
314   switch (page_num)
315     {
316     case PAGE_VAR_SHEET:
317       gtk_widget_hide (view_variables);
318       gtk_widget_show (view_data);
319       gtk_widget_set_sensitive (insert_variable, TRUE);
320       gtk_widget_set_sensitive (insert_cases, FALSE);
321       break;
322     case PAGE_DATA_SHEET:
323       gtk_widget_show (view_variables);
324       gtk_widget_hide (view_data);
325 #if 0
326       gtk_widget_set_sensitive (insert_cases, TRUE);
327 #endif
328       break;
329     default:
330       g_assert_not_reached ();
331       break;
332     }
333 }
334
335
336 static void
337 data_var_select (GtkNotebook *notebook,
338                 GtkNotebookPage *page,
339                 guint page_num,
340                 gpointer user_data)
341 {
342   struct data_editor *de = user_data;
343
344   select_sheet (de, page_num);
345 }
346
347
348
349
350 void
351 data_editor_select_sheet (struct data_editor *de, gint page)
352 {
353   gtk_notebook_set_current_page
354    (
355     GTK_NOTEBOOK (get_widget_assert (de->xml,"notebook")), page
356     );
357 }
358
359
360 void
361 open_data_window (GtkMenuItem *menuitem, gpointer parent)
362 {
363   bool finished = FALSE;
364
365   GtkWidget *dialog;
366
367   GtkFileFilter *filter ;
368
369   dialog = gtk_file_chooser_dialog_new (_("Open"),
370                                         GTK_WINDOW (parent),
371                                         GTK_FILE_CHOOSER_ACTION_OPEN,
372                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
373                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
374                                         NULL);
375
376   filter = gtk_file_filter_new ();
377   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
378   gtk_file_filter_add_pattern (filter, "*.sav");
379   gtk_file_filter_add_pattern (filter, "*.SAV");
380   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
381
382   filter = gtk_file_filter_new ();
383   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
384   gtk_file_filter_add_pattern (filter, "*.por");
385   gtk_file_filter_add_pattern (filter, "*.POR");
386   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
387
388   filter = gtk_file_filter_new ();
389   gtk_file_filter_set_name (filter, _("All Files"));
390   gtk_file_filter_add_pattern (filter, "*");
391   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
392
393   do {
394
395     if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
396       {
397         gchar *file_name =
398           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
399
400         g_free (file_name);
401       }
402     else
403       finished = TRUE;
404
405   } while ( ! finished ) ;
406
407   gtk_widget_destroy (dialog);
408 }
409
410
411
412
413 static void
414 status_bar_activate (GtkCheckMenuItem *menuitem, gpointer data)
415 {
416   struct data_editor *de = data;
417   GtkWidget *statusbar = get_widget_assert (de->xml, "statusbar");
418
419   if ( gtk_check_menu_item_get_active (menuitem) )
420     gtk_widget_show (statusbar);
421   else
422     gtk_widget_hide (statusbar);
423 }
424
425
426
427
428 static void
429 grid_lines_activate (GtkCheckMenuItem *menuitem, gpointer data)
430 {
431   struct data_editor *de = data;
432   const bool grid_visible = gtk_check_menu_item_get_active (menuitem);
433
434   gtk_sheet_show_grid (GTK_SHEET (get_widget_assert (de->xml,
435                                                      "variable_sheet")),
436                        grid_visible);
437
438   gtk_sheet_show_grid (GTK_SHEET (get_widget_assert (de->xml, "data_sheet")),
439                        grid_visible);
440 }
441
442
443
444 static void
445 data_sheet_activate (GtkCheckMenuItem *menuitem, gpointer data)
446 {
447   struct data_editor *de = data;
448
449   data_editor_select_sheet (de, PAGE_DATA_SHEET);
450 }
451
452
453 static void
454 variable_sheet_activate (GtkCheckMenuItem *menuitem, gpointer data)
455 {
456   struct data_editor *de = data;
457
458   data_editor_select_sheet (de, PAGE_VAR_SHEET);
459 }
460
461
462 static void
463 fonts_activate (GtkMenuItem *menuitem, gpointer data)
464 {
465   struct data_editor *de = data;
466   GtkWidget *dialog =
467     gtk_font_selection_dialog_new (_("Font Selection"));
468
469   gtk_window_set_transient_for (GTK_WINDOW (dialog),
470                                 GTK_WINDOW (get_widget_assert (de->xml,
471                                                                "data_editor")));
472   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
473     {
474       GtkSheet *data_sheet =
475         GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
476
477       GtkSheet *var_sheet =
478         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
479
480       PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
481       PsppireVarStore *vs = PSPPIRE_VAR_STORE (gtk_sheet_get_model (var_sheet));
482
483       const gchar *font = gtk_font_selection_dialog_get_font_name
484         (GTK_FONT_SELECTION_DIALOG (dialog));
485
486       PangoFontDescription* font_desc =
487         pango_font_description_from_string (font);
488
489       psppire_var_store_set_font (vs, font_desc);
490       psppire_data_store_set_font (ds, font_desc);
491     }
492
493   gtk_widget_hide (dialog);
494 }
495
496
497 /* The next two callbacks are mutually co-operative */
498
499 /* Callback for the value labels menu item */
500 static void
501 value_labels_activate (GtkCheckMenuItem *menuitem, gpointer data)
502 {
503   struct data_editor *de = data;
504
505   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
506
507   GtkToggleToolButton *tb =
508     GTK_TOGGLE_TOOL_BUTTON (get_widget_assert (de->xml,
509                                                "togglebutton-value-labels"));
510
511   PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
512
513   gboolean show_value_labels = gtk_check_menu_item_get_active (menuitem);
514
515   gtk_toggle_tool_button_set_active (tb, show_value_labels);
516
517   psppire_data_store_show_labels (ds, show_value_labels);
518 }
519
520
521 /* Callback for the value labels tooglebutton */
522 static void
523 value_labels_toggled (GtkToggleToolButton *toggle_tool_button,
524                       gpointer data)
525 {
526   struct data_editor *de = data;
527
528   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
529
530   GtkCheckMenuItem *item =
531     GTK_CHECK_MENU_ITEM (get_widget_assert (de->xml, "view_valuelabels"));
532
533   PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
534
535   gboolean show_value_labels =
536     gtk_toggle_tool_button_get_active (toggle_tool_button);
537
538   gtk_check_menu_item_set_active (item, show_value_labels);
539
540   psppire_data_store_show_labels (ds, show_value_labels);
541 }
542
543
544 static void
545 file_quit (GtkCheckMenuItem *menuitem, gpointer data)
546 {
547   /* FIXME: Need to be more intelligent here.
548      Give the user the opportunity to save any unsaved data.
549   */
550   gtk_main_quit ();
551 }
552
553
554
555 /* Callback for when the Clear item in the edit menu is activated */
556 static void
557 on_clear_activate (GtkMenuItem *menuitem, gpointer data)
558 {
559   struct data_editor *de = data;
560
561   GtkNotebook *notebook = GTK_NOTEBOOK (get_widget_assert (de->xml,
562                                                            "notebook"));
563
564   switch ( gtk_notebook_get_current_page (notebook) )
565     {
566     case PAGE_VAR_SHEET:
567       {
568         GtkSheet *var_sheet =
569           GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
570
571         PsppireVarStore *vs = PSPPIRE_VAR_STORE
572           (gtk_sheet_get_model (var_sheet) );
573
574         /* This shouldn't be able to happen, because the menuitem
575            should be disabled */
576         g_return_if_fail (var_sheet->state  ==  GTK_SHEET_ROW_SELECTED );
577
578         psppire_dict_delete_variables (vs->dict,
579                                        var_sheet->range.row0,
580                                        1 +
581                                        var_sheet->range.rowi -
582                                        var_sheet->range.row0 );
583       }
584       break;
585       case PAGE_DATA_SHEET:
586         break;
587       default:
588         g_assert_not_reached ();
589     }
590 }
591
592
593 /* Insert a new variable before the current row in the variable sheet,
594    or before the current column in the data sheet, whichever is selected */
595 static void
596 insert_variable (GtkCheckMenuItem *m, gpointer data)
597 {
598   struct data_editor *de = data;
599   gint posn;
600
601   GtkWidget *notebook = get_widget_assert (de->xml, "notebook");
602
603   GtkSheet *var_sheet =
604     GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
605
606
607
608   PsppireVarStore *vs = PSPPIRE_VAR_STORE
609     (gtk_sheet_get_model (var_sheet) );
610
611
612   switch ( gtk_notebook_get_current_page ( GTK_NOTEBOOK (notebook)) )
613     {
614     case PAGE_VAR_SHEET:
615       posn = var_sheet->active_cell.row;
616       break;
617     case PAGE_DATA_SHEET:
618       {
619         GtkSheet *data_sheet =
620           GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
621
622         if ( data_sheet->state == GTK_SHEET_COLUMN_SELECTED )
623           posn = data_sheet->range.col0;
624         else
625           posn = data_sheet->active_cell.col;
626       }
627       break;
628     default:
629       g_assert_not_reached ();
630     }
631
632   psppire_dict_insert_variable (vs->dict, posn, NULL);
633
634 }
635
636