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