Replaced GtkStatusBar with a custom one more suited to our needs.
[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   select_sheet (de, PAGE_DATA_SHEET);
246
247   return de;
248 }
249
250
251 /* Callback which occurs when the var sheet's row title
252    button is double clicked */
253 static gboolean
254 click2row (GtkWidget *w, gint row, gpointer data)
255 {
256   struct data_editor *de = data;
257
258   gint current_row, current_column;
259
260   GtkWidget *data_sheet  = get_widget_assert (de->xml, "data_sheet");
261
262   data_editor_select_sheet (de, PAGE_DATA_SHEET);
263
264   gtk_sheet_get_active_cell (GTK_SHEET (data_sheet),
265                              &current_row, &current_column);
266
267   gtk_sheet_set_active_cell (GTK_SHEET (data_sheet), current_row, row);
268
269   return FALSE;
270 }
271
272
273 /* Callback which occurs when the data sheet's column title
274    is double clicked */
275 static gboolean
276 click2column (GtkWidget *w, gint col, gpointer data)
277 {
278   struct data_editor *de = data;
279
280   gint current_row, current_column;
281
282   GtkWidget *var_sheet  = get_widget_assert (de->xml, "variable_sheet");
283
284   data_editor_select_sheet (de, PAGE_VAR_SHEET);
285
286   gtk_sheet_get_active_cell (GTK_SHEET (var_sheet),
287                              &current_row, &current_column);
288
289   gtk_sheet_set_active_cell (GTK_SHEET (var_sheet), col, current_column);
290
291   return FALSE;
292 }
293
294
295
296
297 void
298 new_data_window (GtkMenuItem *menuitem, gpointer parent)
299 {
300   window_create (WINDOW_DATA, NULL);
301 }
302
303
304 static void
305 select_sheet (struct data_editor *de, guint page_num)
306 {
307   GtkWidget *insert_variable = get_widget_assert (de->xml, "data_insert-variable");
308   GtkWidget *insert_cases = get_widget_assert (de->xml, "insert-cases");
309
310   GtkWidget *view_data = get_widget_assert (de->xml, "view_data");
311   GtkWidget *view_variables = get_widget_assert (de->xml, "view_variables");
312
313   switch (page_num)
314     {
315     case PAGE_VAR_SHEET:
316       gtk_widget_hide (view_variables);
317       gtk_widget_show (view_data);
318       gtk_widget_set_sensitive (insert_variable, TRUE);
319       gtk_widget_set_sensitive (insert_cases, FALSE);
320       break;
321     case PAGE_DATA_SHEET:
322       gtk_widget_show (view_variables);
323       gtk_widget_hide (view_data);
324 #if 0
325       gtk_widget_set_sensitive (insert_cases, TRUE);
326 #endif
327       break;
328     default:
329       g_assert_not_reached ();
330       break;
331     }
332 }
333
334
335 static void
336 data_var_select (GtkNotebook *notebook,
337                 GtkNotebookPage *page,
338                 guint page_num,
339                 gpointer user_data)
340 {
341   struct data_editor *de = user_data;
342
343   select_sheet (de, page_num);
344 }
345
346
347
348
349 void
350 data_editor_select_sheet (struct data_editor *de, gint page)
351 {
352   gtk_notebook_set_current_page
353    (
354     GTK_NOTEBOOK (get_widget_assert (de->xml,"notebook")), page
355     );
356 }
357
358
359 void
360 open_data_window (GtkMenuItem *menuitem, gpointer parent)
361 {
362   bool finished = FALSE;
363
364   GtkWidget *dialog;
365
366   GtkFileFilter *filter ;
367
368   dialog = gtk_file_chooser_dialog_new (_("Open"),
369                                         GTK_WINDOW (parent),
370                                         GTK_FILE_CHOOSER_ACTION_OPEN,
371                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
372                                         GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
373                                         NULL);
374
375   filter = gtk_file_filter_new ();
376   gtk_file_filter_set_name (filter, _("System Files (*.sav)"));
377   gtk_file_filter_add_pattern (filter, "*.sav");
378   gtk_file_filter_add_pattern (filter, "*.SAV");
379   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
380
381   filter = gtk_file_filter_new ();
382   gtk_file_filter_set_name (filter, _("Portable Files (*.por) "));
383   gtk_file_filter_add_pattern (filter, "*.por");
384   gtk_file_filter_add_pattern (filter, "*.POR");
385   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
386
387   filter = gtk_file_filter_new ();
388   gtk_file_filter_set_name (filter, _("All Files"));
389   gtk_file_filter_add_pattern (filter, "*");
390   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
391
392   do {
393
394     if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
395       {
396         gchar *file_name =
397           gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
398
399         g_free (file_name);
400       }
401     else
402       finished = TRUE;
403
404   } while ( ! finished ) ;
405
406   gtk_widget_destroy (dialog);
407 }
408
409
410
411
412 static void
413 status_bar_activate (GtkCheckMenuItem *menuitem, gpointer data)
414 {
415   struct data_editor *de = data;
416   GtkWidget *statusbar = get_widget_assert (de->xml, "status-bar");
417
418   if ( gtk_check_menu_item_get_active (menuitem) )
419     gtk_widget_show (statusbar);
420   else
421     gtk_widget_hide (statusbar);
422 }
423
424
425
426
427 static void
428 grid_lines_activate (GtkCheckMenuItem *menuitem, gpointer data)
429 {
430   struct data_editor *de = data;
431   const bool grid_visible = gtk_check_menu_item_get_active (menuitem);
432
433   gtk_sheet_show_grid (GTK_SHEET (get_widget_assert (de->xml,
434                                                      "variable_sheet")),
435                        grid_visible);
436
437   gtk_sheet_show_grid (GTK_SHEET (get_widget_assert (de->xml, "data_sheet")),
438                        grid_visible);
439 }
440
441
442
443 static void
444 data_sheet_activate (GtkCheckMenuItem *menuitem, gpointer data)
445 {
446   struct data_editor *de = data;
447
448   data_editor_select_sheet (de, PAGE_DATA_SHEET);
449 }
450
451
452 static void
453 variable_sheet_activate (GtkCheckMenuItem *menuitem, gpointer data)
454 {
455   struct data_editor *de = data;
456
457   data_editor_select_sheet (de, PAGE_VAR_SHEET);
458 }
459
460
461 static void
462 fonts_activate (GtkMenuItem *menuitem, gpointer data)
463 {
464   struct data_editor *de = data;
465   GtkWidget *dialog =
466     gtk_font_selection_dialog_new (_("Font Selection"));
467
468   gtk_window_set_transient_for (GTK_WINDOW (dialog),
469                                 GTK_WINDOW (get_widget_assert (de->xml,
470                                                                "data_editor")));
471   if ( GTK_RESPONSE_OK == gtk_dialog_run (GTK_DIALOG (dialog)) )
472     {
473       GtkSheet *data_sheet =
474         GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
475
476       GtkSheet *var_sheet =
477         GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
478
479       PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
480       PsppireVarStore *vs = PSPPIRE_VAR_STORE (gtk_sheet_get_model (var_sheet));
481
482       const gchar *font = gtk_font_selection_dialog_get_font_name
483         (GTK_FONT_SELECTION_DIALOG (dialog));
484
485       PangoFontDescription* font_desc =
486         pango_font_description_from_string (font);
487
488       psppire_var_store_set_font (vs, font_desc);
489       psppire_data_store_set_font (ds, font_desc);
490     }
491
492   gtk_widget_hide (dialog);
493 }
494
495
496 /* The next two callbacks are mutually co-operative */
497
498 /* Callback for the value labels menu item */
499 static void
500 value_labels_activate (GtkCheckMenuItem *menuitem, gpointer data)
501 {
502   struct data_editor *de = data;
503
504   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
505
506   GtkToggleToolButton *tb =
507     GTK_TOGGLE_TOOL_BUTTON (get_widget_assert (de->xml,
508                                                "togglebutton-value-labels"));
509
510   PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
511
512   gboolean show_value_labels = gtk_check_menu_item_get_active (menuitem);
513
514   gtk_toggle_tool_button_set_active (tb, show_value_labels);
515
516   psppire_data_store_show_labels (ds, show_value_labels);
517 }
518
519
520 /* Callback for the value labels tooglebutton */
521 static void
522 value_labels_toggled (GtkToggleToolButton *toggle_tool_button,
523                       gpointer data)
524 {
525   struct data_editor *de = data;
526
527   GtkSheet *data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
528
529   GtkCheckMenuItem *item =
530     GTK_CHECK_MENU_ITEM (get_widget_assert (de->xml, "view_valuelabels"));
531
532   PsppireDataStore *ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
533
534   gboolean show_value_labels =
535     gtk_toggle_tool_button_get_active (toggle_tool_button);
536
537   gtk_check_menu_item_set_active (item, show_value_labels);
538
539   psppire_data_store_show_labels (ds, show_value_labels);
540 }
541
542
543 static void
544 file_quit (GtkCheckMenuItem *menuitem, gpointer data)
545 {
546   /* FIXME: Need to be more intelligent here.
547      Give the user the opportunity to save any unsaved data.
548   */
549   gtk_main_quit ();
550 }
551
552
553
554 /* Callback for when the Clear item in the edit menu is activated */
555 static void
556 on_clear_activate (GtkMenuItem *menuitem, gpointer data)
557 {
558   struct data_editor *de = data;
559
560   GtkNotebook *notebook = GTK_NOTEBOOK (get_widget_assert (de->xml,
561                                                            "notebook"));
562
563   switch ( gtk_notebook_get_current_page (notebook) )
564     {
565     case PAGE_VAR_SHEET:
566       {
567         GtkSheet *var_sheet =
568           GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
569
570         PsppireVarStore *vs = PSPPIRE_VAR_STORE
571           (gtk_sheet_get_model (var_sheet) );
572
573         /* This shouldn't be able to happen, because the menuitem
574            should be disabled */
575         g_return_if_fail (var_sheet->state  ==  GTK_SHEET_ROW_SELECTED );
576
577         psppire_dict_delete_variables (vs->dict,
578                                        var_sheet->range.row0,
579                                        1 +
580                                        var_sheet->range.rowi -
581                                        var_sheet->range.row0 );
582       }
583       break;
584       case PAGE_DATA_SHEET:
585         break;
586       default:
587         g_assert_not_reached ();
588     }
589 }
590
591
592 /* Insert a new variable before the current row in the variable sheet,
593    or before the current column in the data sheet, whichever is selected */
594 static void
595 insert_variable (GtkCheckMenuItem *m, gpointer data)
596 {
597   struct data_editor *de = data;
598   gint posn;
599
600   GtkWidget *notebook = get_widget_assert (de->xml, "notebook");
601
602   GtkSheet *var_sheet =
603     GTK_SHEET (get_widget_assert (de->xml, "variable_sheet"));
604
605
606
607   PsppireVarStore *vs = PSPPIRE_VAR_STORE
608     (gtk_sheet_get_model (var_sheet) );
609
610
611   switch ( gtk_notebook_get_current_page ( GTK_NOTEBOOK (notebook)) )
612     {
613     case PAGE_VAR_SHEET:
614       posn = var_sheet->active_cell.row;
615       break;
616     case PAGE_DATA_SHEET:
617       {
618         GtkSheet *data_sheet =
619           GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
620
621         if ( data_sheet->state == GTK_SHEET_COLUMN_SELECTED )
622           posn = data_sheet->range.col0;
623         else
624           posn = data_sheet->active_cell.col;
625       }
626       break;
627     default:
628       g_assert_not_reached ();
629     }
630
631   psppire_dict_insert_variable (vs->dict, posn, NULL);
632
633 }
634
635