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